技术头条 - 一个快速在微博传播文章的方式     搜索本站
您现在的位置首页 --> Android开发 --> 启动Activity的流程(Launcher中点击图标启动)

启动Activity的流程(Launcher中点击图标启动)

浏览:3608次  出处信息

启动Activity一般有多种方式,常见的有三种:

  1. 在Launcher桌面点击app图标

  2. 调用startActivity启动一个Activity

  3. 命令am start启动

这三种方式在服务端的处理方式基本相同,客户端的请求方式也差别不大,理解其中之一就可以类推到其他方式。本文结合案例分析在Launcher桌面点击app图标启动应用的方式,再简要给出其他两种方式的区别。

案例

应用名称为TestLaunchApp,包含A和B两个Activity,A为入口类,点击按钮跳转到B





当点击A中的按钮时,跳转到B,先暂停A,A从前台转入到后台,开始执行B的onCreate、onResume方法,B被调入到栈顶,B现在可见,日志为:

启动一个Activity的标志是开始执行生命周期onCreate方法,转入到后台的标志是onPause方法,正在运行、可见的标志是onResume方法,本文将从源码着手,分析启动activity的过程。

1. 在Launcher桌面点击app图标启动入口Activity

本文基于android5.1.1源码,在Launcher主页面当点击图表时,调用过程为:

onClick—->……—->startActivitySafely—->startActivity(v, intent, tag)—->startActivity(intent, optsBundle);

源码:packages/apps/Launcher3/src/com/android/launcher3/Launcher.java

intent设置了FLAG_ACTIVITY_NEW_TASK标志,表示开启一个新任务,在新任务中启动activity,本案例没有特殊的动画设置,optsBundle为null。

framework层客户端

过程1    frameworks\base\core\java\android\app\Activity.java

startActivity所属的对象是this,表示当前启动类Launcher对象,下一步执行到Activity的startActivity方法,过程为:

startActivity(Intent intent, @Nullable Bundle options)

—-> startActivityForResult(intent, -1)

—-> startActivityForResult(intent, requestCode, null)

注:由于源码较长,本文不贴上全部源码,只给出方法名称、部分代码以及源码路径

action为字符串“com.feeyan.www.b_activity”,requestCode等于-1,如果>=0, B被启动后会返回到A中,且A中的onActivityResult()方法会被调用。即便是调用startActivity,还是会调到 startActivityForResult,只不过此时requestCode是-1了。

mParent:如果不为空,表示当前Activity有子类,本案例没有子类,为空,进程执行到:

要搞懂源码,最关键的就是弄清楚参数的具体含义,源码中参数有时候多达十几个,如果不清楚参数的来龙去脉,无从分析。

this:当前进程还是在Launcher所在的进程,this就是Launcher类的一个对象。

mMainThread.getApplicationThread():返回一个ApplicationThread对象类型,也是一个IBinder对象类型,,mMainThread是ActivityThread的一个对象,代表当前Launcher主线程对象

mToken:也是一个IBinder对象类型

requestCode仍为-1,options为null

过程2    frameworks\base\core\java\android\app\Instrumentation.java

this对象传给execStartActivity,该函数的第一个形参who是Context类型,第4个形参target是Activity类型,其实际类型都是Launcher对象,只是名字起的不一样,这就是一种共识,代表着某种含义,读者看到名字就能猜得着其用意。

contextThread既是IBinder对象,也是IApplicationThread对象,此处向上转型为IApplicationThread对象,

ActivityManagerNative实现了IActivityManager接口,调用getDefault方法最终返回ActivityManagerService的代理类ActivityManagerProxy的一个对象,于是,startActivity便转入到ActivityManagerProxy对象中开始执行。

过程3    frameworks\base\core\java\android\app\ActivityManagerNative.java

分析参数时,结合实际参数来看,否则单独看形参不能确定具体含义。

caller:前面传过来的值,代表ApplicationThread对象

callingPackage:由who.getBasePackageName()的值传递而来,who是Context对象,getBasePackageName()的实现在ContextImple中,返回当前启动类的包名,就是Launcher的包名

resolvedType:解析当前发送的Intent的MIME数据类型,本案例没有为intent设置type、data属性,因此,intent.resolveTypeIfNeeded(who.getContentResolver())返回null

resultTo:Ibinder对象,具体含义后面继续看

resultWho:由target != null ? target.mEmbeddedID : null得来,target是activity对象即启动类Launcher对象,不为空,该语句返回mEmbeddedID,一个id号,这个值必须要从Launcher这个apk启动中获得,在Launcher启动后,代表Launcher启动类的对象是一个ActivityClientRecord对象,该对象所属的类路径为:

frameworks\base\core\java\android\app\ActivityThread.java

该对象的scheduleLaunchActivity方法中,有一句:

ActivityClientRecord r = new ActivityClientRecord();

在ActivityClientRecord的构造方法中会把embeddedID初始化为null,因此mEmbeddedID为空

startFlags:整型值,已经初始化为0,具体作用后面分析

profilerInfo:为null,具体作用后面分析

这些参数都会被打包到持久化类Parcel的对象data中,把data作为transact的参数进行跨进程传递:

mRemote.transact(START_ACTIVITY_TRANSACTION, data, reply, 0);

该方法通过binder通信机制会传递到ActivityManagerNative的onTransact方法,在onTransact方法中,根据发送命令START_ACTIVITY_TRANSACTION找到case处理语句,把data中的数据取出来赋给相应的变量,继续调用:

startActivity最终会调用到服务端ActivityManagerService中。此时,进程也从启动类Launcher所在的进程切换到了服务端进程。从ActivityManagerNative.getDefault().startActivity一直到ActivityManagerService的startActivity方法,主要由binder通信实现,该过程相当复杂,但binder通信不属于本文重点,而且binder机制贯穿于整个Android系统、内核、驱动部分,本文如再遇到binder通信机制,直接给出最终被调用的类及方法。

在进入到服务端之前,看看客户端到底做了哪些工作?

主要是获得了一些必要的参数:IApplicationThread对象、启动类包名、Intent的MIME数据类型、IApplicationToken.Stub类型对象resultTo等,除了这些,没有其他特殊的操作了,其实最关键的操作还是在服务端进行的,这就是为何本文一开始提到无论哪种启动方式,客户端都是大同小异。

framework层服务端

过程4    frameworks\base\services\core\java\com\android\server\am\ActivityManagerService.java

startActivity—->startActivityAsUser—->mStackSupervisor.startActivityMayWait

这几步没有太多的操作,获得了一个用户id,用来作一些检测

过程5    frameworks\base\services\core\java\com\android\server\am\ActivityStackSupervisor.java

先看多了哪些参数:
voiceSession:IVoiceInteractionSession对象类型,被初始化null。IVoiceInteractionSession本是一个aidl远程接口,定义了任务栈启动taskStarted、任务栈结束taskFinished等方法

voiceInteractor:IVoiceInteractor对象类型,被初始化为null。IVoiceInteractor也是一个aidl远程接口

outResult:WaitResult对象类型,被初始化为null。WaitResult是IActivityManager的内部类,实现了Parcelable接口,主要用来保存启动Activity后返回的结果信息

config:Configuration对象类型,被初始化为null。Configuration描述了所有设备相关的配置信息,比如,本地语言、屏幕大小、屏幕方向、输入法模式,可以通过Resources的getConfiguration获得改对象

iContainer:IActivityContainer对象类型,被初始化为null。IActivityContainer也是一个aidl远程接口

inTask:TaskRecord对象类型,被初始化为null。TaskRecord很重要,会经常用到此类,描述一个任务栈,每个任务栈可以包含多个Activity对象,每个TaskRecord对象都有一个当前栈ActivityStack的引用,每个栈可以对应多个TaskRecord对象

除了这些多余的参数,其他参数都是从客户端传递而来。

getComponent方法返回一个ComponentName对象,该对象表示通过intent要启动的组件类,本案例就对应A这个Activity,ComponentName对象一般用包名和类名标识一个组件,因此,componentSpecified为true

根据客户端传递过来的Intent对象重新构建一个Intent对象,这样做是不要破坏客户端传递来的Intent对象

resolveActivity方法开始解析Intent对象,返回intent对应的目标Activity类的ActivityInfo对象,ActivityInfo类专门用来描述AndroidManifest.xml中Activity、Receiver组件信息的,本案例返回的就是A这个类对应的信息,ActivityInfo的成员变量name就是类名称,packageName就是包名称,对应本案例分别为com.example.startapptest.A和com.example.startapptest

callingUid传过来时为-1,call又不为空,进程执行else字句callingPid = callingUid = -1;

iContainer为空,那么container也为空,调用getFocusedStack获得当前正在前台的栈,也就是Launcher所在的栈。

aInfo虽然不为空,但aInfo.applicationInfo.flags&ApplicationInfo.FLAG_CANT_SAVE_STATE却为0,因为没有设置这种属性,因此跳过该if语句,开始执行:

callingPid:int型变量,看字面意思与pid相关,具体含义后面再看

callingUid:int型变量,看字面意思与uid相关,具体含义后面再看

realCallingPid:启动类所在进程的pid,本案例是Launcher

realCallingUid:启动类所在进程的uid,本案例是Launcher

componentSpecified:为true,表明intent对应的目标Activity类存在

outActivity:ActivityRecord数组名称,初始化为null,ActivityRecord是一个动态生成的对象,代表Activity在历史栈中的记录,ActivityRecord包含了Activity所有信息。

mService是ActivityManagerService对象,通过getRecordForAppLocked方法获得启动类所在进程的进程记录对象ProcessRecord。参数caller是IApplicationThread对象,前文提到过,实际是ApplicationThread对象,代表Launcher类的主线程,caller在ActivityManagerService和ActivityThread两个进程之间完成通信,现在终于明白了,为何在startActivity时会带着这样一个参数:服务端通过该参数获得客户端进程信息,该参数起到桥梁作用。

callerApp不为空,分别获得启动类进程的pid和uid保存到callingPid、callingUid中,这个callingPid和之前的realCallingPid获得的值一样,都是Launcher进程pid

定义了两个ActivityRecord变量sourceRecord、resultRecord,用来对应启动类和目标类。上文提到,resultTo属于IBinder对象,属于启动方的对象。isInAnyStackLocked方法根据启动类的标记resultTo对象在列表栈中找出对应的栈,再在栈顶找到Activity记录保存到sourceRecord中。

上文提到,intent一开始在客户端就被设置了FLAG_ACTIVITY_NEW_TASK标志,getFlags方法便取出该标志,保存到launchFlags变量中。

创建一个ActivityRecord对象,这个ActivityRecord对象具体有什么作用?看看参数具体含义

前4个参数代表了启动类Launcher,第5~7参数(Intent,resolvedType, aInfo)代表了目标类

mService.mConfiguration表示系统配置,resultRecord代表目标类,resultWho代表启动类的一个id号,为空

componentSpecified为true

this:代表当前ActivityStackSupervisor对象

从参数来看,该类既包含启动类的属性,又包含目标类属性,推测该类应该用来表达目标类,后面可以证明。

startActivityLocked方法的作用:获得启动类进程信息、pid、uid,创建ActivityRecord类对象sourceRecord保存启动类信息,创建ActivityRecord对象r,暂时推测代表目标类,具体含义后面分析。进程继续调用:

开始调用下一步操作,第一个参数就是刚才创建的ActivityRecord对象;第二个参数是启动类对象,不为空;


r.intent就是传递而来的intent对象,r.launchedFromUid就是启动类Launcher的uid

这三个变量代表目标类的启动模式,本案例就是A的启动模式,没有任何设置,默认为Standard模式,因而这三个变量都是false

FLAG_ACTIVITY_NO_USER_ACTION:当启动目标Activity时Intent设置了此标志,前台正在行的Activity在暂停之前(执行onPaused方法)不会回调onUserLeaveHint方法。NO_USER_ACTION表示非用户操作,如果设置了此标志,表示非用户行为时不会回调onUserLeaveHint。比如,闹钟响了、来电话了,这属于非用户操作,如果设置了此标志,就不会回调onUserLeaveHint,相反,如果是用户操作行为比如按下HOME按键,返回键等,就会回调onUserLeaveHint。本案例中发送给A的Intent没有设置该标志,mUserLeaving为true,表明不是非用户操作行为。

Intent没有设置FLAG_ACTIVITY_PREVIOUS_IS_TOP,notTop为空

sourceRecord不为空,变量finishing为空,因为此时启动类Launcher还在前台,没有进入到销毁列表中,进程执行else语句,得到启动类所在的栈对象并保存到sourceStack中。

Intent没有设置FLAG_ACTIVITY_MULTIPLE_TASK,resultTo和startActivityLocked参数中resultTo不是一个意思,前者是在startActivityLocked函数中创建的ActivityRecord对象resultRecord,代表目标类一方,被初始化为空,而后者代表启动类一方,不能混淆。

启动A时没有设置启动模式,采用是默认的标准模式,因此launchSingleInstance为false,调用findTaskLocked(r)在当前栈顶中查询是否有目标类,如果有,就返回该类,否则,返回空。因为首次启动A,因此栈中肯定没有A,返回空保存到intentActivity变量中,这样的话,if语句不成立。如果栈中有实例,再次启动时就会执行这段代码。

目标类包名肯定不为空,执行if条件,getFocusedStack返回当前栈,topRunningNonDelayedActivityLocked返回当前ActivityRecord对象保存到top中,肯定不为空;top.realActivity表示启动类,r.realActivity表示目标类,本案例前者是Launcher,后者是A,两者肯定不相等,因此if语句不成立,跳过此段。如果成立的话,就会在当前栈中找到已存在的实例继续使用。

既然当前栈中没有已存在实例,那么只能新创建一个任务栈,继续看:

此if语句成立,launchTaskBehind为空,那么taskToAffiliate也为空

newTask代表新建一个任务的标志,设为true;adjustStackFocus获得一个ActivityStack保存到targetStack变量作为目标类的栈;

createTaskRecord创建TaskRecord对象并放到栈顶,然后再放到目标类ActivityRecord的task变量中

调用startActivityLocked进行下一步操作

startActivityUncheckedLocked函数非常复杂,最关键的就是查询是否有已存在的TaskRcord作为目标类的任务栈,如果栈中有就复用,否则就创建一个新的TaskRcord对象作为目标类的任务栈。该函数涉及到了FLAG标志,启动模式的判断等,其目的就是找到一个合适的任务栈,为何要找到这个栈,就是因为Activity在执行时以栈这个数据结构来管理。

过程6    frameworks\base\services\core\java\com\android\server\am\ActivityStack.java

第一个参数对应目标类对象记录,newTask为true,表示新建了一个任务栈,doResume为true,keepCurTransition为false。

r.task就是在startActivityUncheckedLocked中创建的目标类的RaskRecord对象,取出来保存到rRask变量中

mLaunchTaskBehind在上文得知为空,taskForIdLocked在历史栈中查询是否含有id号为目标类所在的栈id,如果有,表明目标类之前已经被创建过,现在开始复用该对象,属于非首次启动,否则为首次启动对象,本案例首次启动A,因此,此函数返回null;newTask传递过来为true,if语句成立,调用insertTaskAtTop函数把新创建的TaskRecord对象插入到列表mTaskHistory的尾部,也就是插入到历史栈顶;

过程7    frameworks\base\services\core\java\com\android\server\am\ActivityStackSupervisor.java

this对象表示当前对象ActivityStack,此ActivityStack是新建的对象,不是Launcher所在的ActivityStack,是在startActivityUncheckedLocked中的adjustStackFocus方法获得的,目的就是把新创建的任务插入到该ActivityStack对象中,这个对象就代表了目标类所属的栈

第二个参数r就表示目标类对象记录,第三个参数依然为null

isFrontStack判断新获得的ActivityStack对象位于栈顶,判断为真,执行if语句,调用resumeTopActivityLocked(target, targetOptions)

过程8    frameworks\base\services\core\java\com\android\server\am\ActivityStack.java

第一个参数prev表示目标类ActivityRecord对象,第二个传递过来为空

继续调用resumeTopActivityInnerLocked方法,再调用resumeTopActivityInnerLocked


topRunningActivityLocked方法找到栈顶的ActivityRecord对象,此处对应着A

mStackSupervisor.mUserLeaving的值在过程5中被设置为true,此处取出来赋值给userLeaving,表明是用户操作行为(按下返回键,HOME按键等);无论是true还是false,此处还是再复位一下,重新设置为false

目标类A没有设置FLAG_RESUME_WHILE_PAUSING标志,dontWaitForPause为false

pauseBackStacks函数返回false赋给pausing变量,mResumedActivity表示当前正在前台运行的Activity,就是Launcher,不为空,进程调用startPausingLocked继续执行

startPausingLocked开始暂停当前Activity,如果成功,返回true,否则false

4个参数分别为false,false,true,false

mResumedActivity代表Launcher,先赋值给prev再置空;prev赋值给mPausingActivity,表明即将要暂停的Activity是Launcher,mLastPausedActivity也赋值为prev,表示刚刚暂停的Activity是哪个

prev.app表示Launcher进程信息,不为空;prev.app.thread是一个IApplicationThread对象,对应Launcher也不为空,进程继续调用schedulePauseActivity方法,此处是一个Binder进程间通信,下一步调用到ApplicationThread对象的schedulePauseActivity方法中,ApplicationThread是ActivityThread内部类


过程9    frameworks\base\core\java\android\app\ActivityThread.java

finished传递过来为false,因为Launcher此时还没有执行生命周期方法onPause()、onDestory(),因此没有进入finishing状态,那么,sendMessage的第一个参数值为H.PAUSE_ACTIVITY

sendMessage把消息发送到队列中等待执行,执行方法是Handler的handleMessage方法,通过命令PAUSE_ACTIVITY可以得到执行程序:

继续调用handlePauseActivity方法

finished为false,userLeaving传递过来为true,configChanges为0,dontReport为false

token对应启动类Launcher,此处获得Launcher的ActivityRecord对象

调用performUserLeavingActivity方法,performUserLeavingActivity的最终调用过程为:

performUserLeavingActivity—->

mInstrumentation.callActivityOnUserLeaving(r.activity) —->

activity.performUserLeaving() —->

onUserInteraction()

onUserLeaveHint()

意味着,如果是用户操作的主动行为,比如返回按键,遥控器上下左右按键,HOME按键灯,会调用Activity的

onUserInteraction和onUserLeaveHint方法,如果是按键,触摸、轨迹球被分发到Activity时,onUserInteraction会被回调;onUserLeaveHint的作用是当Activity即将进入到后台前被回调,起到提示作用

performUserLeavingActivity执行完后,进程继续调用

performPauseActivity方法中继续调用callActivityOnPause方法,参数r.activity代表启动类Launcher


过程9.1    frameworks\base\core\java\android\app\Instrumentation.java


过程9.1.1    frameworks\base\core\java\android\app\Activity.java

最终调用到Activity的performPause方法,再调用生命周期方法onPause()意味着启动类处于暂停状态了,这一步执行完后返回到performPauseActivity中,执行r.paused = true把启动类的ActivityClientRecord的paused置为true,表示启动类此时已经处于暂停状态了。再返回到handlePauseActivity中,继续执行performPauseActivity后面的语句

这一步通过Binder进程间通信机制进入到ActivityManagerService的activityPaused方法中


过程9.2    frameworks\base\services\core\java\com\android\server\am\ActivityManagerService.java


过程10    frameworks\base\services\core\java\com\android\server\am\ActivityStack.java

mPausingActivity表示启动类Launcher,r是Launcher的ActivityRecord对象,if条件为真,进程继续调用completePauseLocked(true)方法

既然启动类都已经暂停了,那下一步工作是不是就是把目标类启动起来呢?如果是的话,应该会执行生命周期onResume方法,这只是猜测,具体详细看方法的执行过程

参数resumeNext传递过来为true

prev.finishing属性为false,这个属性一直没有设置

mPausingActivity = null;

如果启动类已经stop,就把mPausingActivity设为null

进程继续执行到:

当前系统处于非睡眠和关机状态,if条件为真,进程开始调用resumeTopActivitiesLocked方法


过程11    frameworks\base\services\core\java\com\android\server\am\ActivityStackSupervisor.java

要清楚方法具体做了什么,一定要先弄清楚参数的含义

形参targetStack的实参是topStack,通过mStackSupervisor.getFocusedStack获得,即当前获得焦点的栈,此处,启动类已经暂停,那么当前栈就是目标类所在的栈,prev是启动类

又调用了resumeTopActivityLocked方法


过程12    frameworks\base\services\core\java\com\android\server\am\ActivityStack.java

继续调用resumeTopActivityInnerLocked方法

再次进入到此方法时,mResumedActivity为空,因为这是在过程8中startPausingLocked方法内设置的,表明启动类Launcher已经不在是当前运行的Activity,因此

这个语句就不再成立,进程跳过此句继续执行

next就是目标类A,此时A的一些栈等信息已经构建,但是A得进程还没有创建,正常情况下,启动一个新的应用程序一般会创建一个新的进程,应用程序在此进程中执行,特殊情况下可以通过AndroidManifest中process属性执行指定应用程序在某个进程中执行,本文没有设置process属性,默认为启动一个新的进程,本文后面会分析到。由此可知,A还没有进程信息,if语句不成立,进程转到else语句执行


过程13    frameworks\base\services\core\java\com\android\server\am\ActivityStackSupervisor.java


此时A进程还没有创建,所以app为空,跳过if语句,开始调用startProcessLocked方法

假如A的应用程序已经启动,然后在A中启动B,B是A应用程序的一个Activity,那么此时进程已经创建,app就不会为空,进程会调用realStartActivityLocked方法


过程14    frameworks\base\services\core\java\com\android\server\am\ActivityManagerService.java


该方法中会为A创建ProcessRecord信息,然后继续调用


此方法中会调用进程的start方法创建一个新的进程,具体是通过zygote进程的来fork一个新的进程,成为子进程,子进程共享父进程资源,几乎和父进程一样。当子进程创建好后,系统会分配一个进程号PID给新进程并返回,否则抛出异常

第一个参数processClass为新创建的进程的入口类即android.app.ActivityThread.java

niceName:新创建的进程的进程名字,用ps命令可以查看到该名字,一般情况下,应用程序的进程名就是包名

如果进程创建成功,待方法start执行完后,系统就会转到ActivityThread.java的main方法入口开始执行,注意这个流程,和我们通常看到的方法调用方法是不一样的。


过程15    frameworks\base\core\java\android\app\ActivityThread.java

prepareMainLooper方法创建了looper对象和MessageQueue消息队列;创建了并初始化ActivityThread对象,同时也创建并初始化了ApplicationThread对象mAppThread

获得ActivityManagerProxy对象,调用该对象的attachApplication方法,通过binder通信,最终调用到ActivityManagerService的attachApplication方法


过程16    frameworks\base\services\core\java\com\android\server\am\ActivityManagerService.java


先通过pid获得ProcessRecord对象,该对象上一步创建过,不为空

初始化该ProcessRecord对象

这段话就是真正开始启动目标Activity了,本案例就是A

注:在这段话后面分别有:

用来启动Service、发送广播,此处作为一个备注,如果要分析启动Service、广播,研究这两段语句,本文只研究启动Activity,因此,详细看attachApplicationLocked方法


过程17    frameworks\base\services\core\java\com\android\server\am\ActivityStackSupervisor.java



hr就是目标类ActivityRecord对象,app是进程名字

调整进程LRU算法;参与管理进程

app.thrad是IApplicationThread对象,此处就是ApplicationThreadProxy对象接口,这里也是Binder通信过程,调用ApplicationThreadProxy的scheduleLaunchActivity方法,通过binder通信转到ApplicationThread对象的scheduleLaunchActivity方法,该方法在ActivityThread对象中


过程18    frameworks\base\core\java\android\app\ActivityThread.java

该方法创建了目标类对应的ActivityClientRecord对象,而后初始化该对象,作为sendMessage参数发送到消息队列待处理


该方法分为两个部分,先调用performLaunchActivity,再调用handleResumeActivity,最后还有finishActivity

先看performLaunchActivity


从目标类ActivityClientRecord对象中取出ActivityInfo对象,ActivityInfo对象包含了Activity、receiver对象信息

再根据Intent获得组件对象component,组件包含了启动类名称和包名称

通过Java反射机制找到目标类文件,再创建目标类的一个对象赋值给activity。从此处可知,原来Android中Activity对象是在启动时创建的,系统已经帮助程序员写好了new Activity对象的动作,无需程序员自行new对象,这解决了一开始学习android时总是搞不清楚Activity对象是从什么时候创建的的困惑,因此,Android并不关注组件的创建过程,而把关注点落在了组件的生命周期上。

makeApplication方法也是利用反射机制找到应用程序Application类并创建一个对象,并调用Application对象的onCreate方法,这就是为什么应用一启动后,Application的onCreate比Activity的onCreate先执行的原因!

createBaseContextForActivity方法中会调用createActivityContext创建ContextImpl对象,ContextImpl实现了Context,也就同时创建了上下文管理者Context对象,这也解决了为什么在写程序时总是能够获得Context对象的原因

attach方法把Context对象、Instrumentation对象等和Activity关联起来

通过Instrumentation对象的callActivityOnCreate方法进入到Instrumentation对象中,Instrumentation对象就是监控所有用户和系统之间的交互操作,比如onCreate、onResume等

过程19    frameworks\base\core\java\android\app\Instrumentation.java

继续调用performCreate方法

过程20    frameworks\base\core\java\android\app\Activity.java

最终调用Activity的onCreate方法开始生命周期

再返回到handleLaunchActivity中,进程继续执行到handleResumeActivity方法

这四步写在一个代码段,可以看到最终调用了Activity的onResume方法,目标类A启动起来了并成为可见状态

到此处为止,在Launcher中启动Activity的过程就分析完了。

整个过程相当复杂,涉及到很多动态对象,进程间通信,栈的管理等,可以不必要理解每句代码,但是清楚整个流程做了哪些核心的动作是有必要的:

1.  过程5

resolveActivity方法中调用了包管理器PackageManager的resolveIntent方法解析启动目标类的Intent对象,获得解析后的对象ActivityInfo,为何要获得这个对象,这个对象有有什么作用?

在AndroidManifest.xml中Activity和receiver标签包含了很多属性,比如主题、启动模式、屏幕方向,输入法设置,进程名称等,ActivityInfo就是对应目标类Activity的一个动态对象,该对象包含了这些属性信息。该对象的作用用来构建目标类对应的ActivityRecord对象,该对象也是一个动态对象,是历史栈中的一条记录,在内存中对应目标类。

这样就明白一个问题:启动activity时有显示和隐式,对于隐式方式,只需要在目标类中intetn-filter中增加一个action,然后启动类通过这个action即可启动目标类,这是如何做到的?实际上是通过包管理器PackageManager解析intent,查找到匹配的action对应的目标类的。

2. 过程8

startPausingLocked方法是进入到暂停启动类过程的标志,逐步调用prev.app.thread.schedulePauseActivity,然后又binder通信进入到ApplicationThread对象的schedulePauseActivity方法,在此方法中,发送消息给启动类Launcher主线程ActivityThread,ActivityThread利用handler循环处理消息,调用handlePauseActivity方法处理,最终调用到Activity的生命周期方法onPause暂停启动类

3.  过程14

Process对象的start方法开启了一个新的进程作为目标类的主线程,由此,目标类开始从ActivityThread的main方法开支执行,然后由

mgr.attachApplication(mAppThread);

语句通过binder通信进入到ActivityManagerService中,并传递了ApplicationThread对象,该对象传入到ActivityManagerService中后构建目标类进程信息,然后ActivityManagerService负责启动目标类,最终通过该对象又通过Binder通信返回到ApplicationThread对象中,然后ApplicationThread对象又发送消息给目标类主线程ActivityThread对象,该对象循环处理来自ActivityManagerService的消息,进而调用Activity的生命周期方法onCreate、onResume。其中,IApplicationThread远程接口对象起着非常关键作用,他在主线程对象ActivityThread与Activity管理器ActivityManagerService对象之间起着通信桥梁作用。

ActivityManagerService是Activity Manager(注意,分开大写,代表Framework层的核心模块,该模块包含了ActivityManagerService、ActivityStack、Binder接口等)的核心部分,负责启动Activity、Service、发送Broadcast Receiver、启动ContentProvider;调整进程调度算法,管理任务栈、检查权限等一些列核心功能。


2. 调用startActivity启动一个Activity

在应用程序内启动Activity,和应用程序外启动最根本的不同在于不会新创建进程,也就是说,过程13中,不会执行startProcessLocked方法,而执行realStartActivityLocked方法,过程13~过程16可以省略不看,此时在同一个进程中;除此之外,就是栈的获取不一样,用startActivity方法启动可能不会新建栈,直接使用已有的栈,而Launcher启动时一般会新建栈。

3. 命令am start启动

这种方法适合调试时使用,在串口中直接采用

am start -n xxx/.yyy

即可启动应用程序,包名是xxx,入口类名是yyy

am 这个可执行程序对应的源码目录位置:

当执行这条命令时,先从main方法开始执行

Am继承了BaseCommand类,run方法在BaseCommand中定义,在run方法中又调用了onRun()方法,系统运行时具体类型是Am,这样就调到了Am的onRun方法。onRun中先执行:

获得ActivityManagerService的代理类ActivityManagerProxy的一个对象

判断参数是否有start这个字符串,如果有,就调用runStart方法,本文启动Activity当然含有start参数

在runStart方法中,会执行这一句

这一步会继续调用ActivityStackSupervisor的startActivityMayWait方法,与上文的“过程4”一样,后面的步骤基本上差不多。

不管哪种方式启动,在服务端的操作基本上相同,区别就在于是否复用当前栈还是新创建一个栈,是否新建一个进程作为目标类的进程等;在客户端,区别在于启动Activity的方式不同,第三种命令启动方式缺少了startActivity部分。

建议继续学习:

  1. osx平台上lol英雄联盟launcher启动器的分析实现    (阅读:4659)
  2. 启动activity的4种模式(standard、singleTop、singleTask、singleINstance)    (阅读:1473)
  3. launchAnyWhere: Activity组件权限绕过漏洞解析(Google Bug 7699048 )    (阅读:931)
QQ技术交流群:445447336,欢迎加入!
扫一扫订阅我的微信号:IT技术博客大学习
© 2009 - 2024 by blogread.cn 微博:@IT技术博客大学习

京ICP备15002552号-1