6.3.3 startActivityLocked分析

startActivityLocked是startActivityMayWait第二阶段的工作重点,该函数有点长,请读者耐心看代码。

[—>ActivityStack.java:startActivityLocked]


final int startActivityLocked(IApplicationThread caller,

Intent intent, String resolvedType,

Uri[]grantedUriPermissions,

int grantedMode, ActivityInfo aInfo, IBinder resultTo,

String resultWho, int requestCode,

int callingPid, int callingUid, boolean onlyIfNeeded,

boolean componentSpecified, ActivityRecord[]outActivity){

int err=START_SUCCESS;

ProcessRecord callerApp=null;

//如果caller不为空,则需要从AMS中找到它的ProcessRecord。本例的caller为null

if(caller!=null){

callerApp=mService.getRecordForAppLocked(caller);

//其实就是想得到调用进程的pid和uid

if(callerApp!=null){

callingPid=callerApp.pid;//一定要保证调用进程的pid和uid正确

callingUid=callerApp.info.uid;

}else{//如调用进程没有在AMS中注册,则认为其是非法的

err=START_PERMISSION_DENIED;

}

}//if(caller!=null)判断结束

//下面两个变量意义很重要。sourceRecord用于描述启动目标Activity的那个Activity,

//resultRecord用于描述接收启动结果的Activity,即该Activity的onActivityResult

//将被调用以通知启动结果,读者可先阅读SDK中startActivityForResult函数的说明

ActivityRecord sourceRecord=null;

ActivityRecord resultRecord=null;

if(resultTo!=null){

//本例resultTo为null,

}

//获取Intent设置的启动标志,它们是和Launch Mode类似的“小把戏”,

//所以,读者务必理解本书介绍的关于Launch Mode的知识点

int launchFlags=intent.getFlags();

if((launchFlags&Intent.FLAG_ACTIVITY_FORWARD_RESULT)!=0

&&sourceRecord!=null){

……

/*

前面介绍的Launch Mode和Activity的启动有关,实际上还有一部分标志用于控制

Activity启动结果的通知。有关FLAG_ACTIVITY_FORWARD_RESULT的作用,读者可

参考SDK中的说明。使用这个标签有个前提,即Activity必须先存在,正如上边代码的if中sourceRecord

不为null的判断所示。另外,读者自己可尝试编写例子,以测试FLAG_ACTIVITY_FORWARD_RESULT

标志的作用

*/}

//检查err值及Intent的情况

if(err==START_SUCCESS&&intent.getComponent()==null)

err=START_INTENT_NOT_RESOLVED;

……

//如果err不为0,则调用sendActivityResultLocked返回错误

if(err!=START_SUCCESS){

if(resultRecord!=null){//resultRecord接收启动结果

sendActivityResultLocked(-1,resultRecord, resultWho, requestCode,

Activity.RESULT_CANCELED, null);

}

……

return err;

}

//检查权限

final int perm=mService.checkComponentPermission(aInfo.permission,

callingPid, callingUid, aInfo.applicationInfo.uid, aInfo.exported);

……//权限检查失败的处理,不必理会

if(mMainStack){

//可为AMS设置一个IActivityController类型的监听者,AMS有任何动静都会回调该

//监听者。不过谁又有如此本领去监听AMS呢?在进行Monkey测试的时候,Monkey会

//设置该回调对象。这样,Monkey就能根据AMS放映的情况进行相应处理了

if(mService.mController!=null){

boolean abort=false;

try{

Intent watchIntent=intent.cloneFilter();

//交给回调对象处理,由它判断是否能继续后面的行程

abort=!mService.mController.activityStarting(watchIntent,

aInfo.applicationInfo.packageName);

}……

//回调对象决定不启动该Activity。在进行Monkey测试时,可设置黑名单,位于

//黑名单中的Activity将不能启动

if(abort){

……//通知resultRecord

return START_SUCCESS;

}

}

}//if(mMainStack)判断结束

//创建一个ActivityRecord对象

ActivityRecord r=new ActivityRecord(mService, this, callerApp, callingUid,

intent, resolvedType, aInfo, mService.mConfiguration,

resultRecord, resultWho, requestCode, componentSpecified);

if(outActivity!=null)

outActivity[0]=r;//保存到输入参数outActivity数组中

if(mMainStack){

//mResumedActivity代表当前界面显示的Activity

if(mResumedActivity==null

||mResumedActivity.info.applicationInfo.uid!=callingUid){

//检查调用进程是否有权限切换Application,相关知识见下文的解释

if(!mService.checkAppSwitchAllowedLocked(callingPid, callingUid,

"Activity start")){

PendingActivityLaunch pal=new PendingActivityLaunch();

//如果调用进程没有权限切换Activity,则只能把这次Activity启动请求保存起来,

//后续有机会时再启动它

pal.r=r;

pal.sourceRecord=sourceRecord;

……

//所有Pending的请求均保存到AMS mPendingActivityLaunches变量中

mService.mPendingActivityLaunches.add(pal);

mDismissKeyguardOnNextActivity=false;

return START_SWITCHES_CANCELED;

}

}//if(mResumedActivity==null……)判断结束

if(mService.mDidAppSwitch){//用于控制app switch,见下文解释

mService.mAppSwitchesAllowedTime=0;

}else{

mService.mDidAppSwitch=true;

}

//启动处于Pending状态的Activity

mService.doPendingActivityLaunchesLocked(false);

}//if(mMainStack)判断结束

//调用startActivityUncheckedLocked函数

err=startActivityUncheckedLocked(r, sourceRecord,

grantedUriPermissions, grantedMode, onlyIfNeeded, true);

……

return err;

}


startActivityLocked函数的主要工作包括:

处理sourceRecord及resultRecord。其中,sourceRecord表示发起本次请求的Activity,resultRecord表示接收处理结果的Activity(启动一个Activity肯定需要它完成某项事情,当目标Activity将事情成后,就需要告知请求者该事情的处理结果)。在一般情况下,sourceRecord和resultRecord应指向同一个Activity。

处理app switch。如果AMS当前禁止app switch,则只能把本次启动请求保存起来,以待允许app switch时再处理。从代码中可知,AMS在处理本次请求前,会先调用doPendingActivityLaunchesLocked函数,在该函数内部将启动之前因系统禁止app switch而保存的Pending请求。

调用startActivityUncheckedLocked处理本次Activity启动请求。

先来看app switch,它虽然是一个小变量,但是意义重大。

1.关于resume/stopAppSwitches的介绍

AMS提供了两个函数,用于暂时(注意,是暂时)禁止App切换。为什么会有这种需求呢?因为当某些重要(例如设置账号等)Activity处于前台(即用户当前所见的Activity)时,不希望系统因用户操作之外的原因而切换Activity(例如恰好此时收到来电信号而弹出来电界面)。

先来看stopAppSwitches,其代码如下:

[—>ActivityManagerService.java:stopAppSwitches]


public void stopAppSwitches(){

……//检查调用进程是否有STOP_APP_SWITCHES权限

synchronized(this){

//设置一个超时时间,过了该时间,AMS可以重新切换App(switch app)了

mAppSwitchesAllowedTime=SystemClock.uptimeMillis()

+APP_SWITCH_DELAY_TIME;

mDidAppSwitch=false;//设置mDidAppSwitch为false

mHandler.removeMessages(DO_PENDING_ACTIVITY_LAUNCHES_MSG);

Message msg=//防止应用进程调用了stop却没调用resume,5秒后处理该消息

mHandler.obtainMessage(DO_PENDING_ACTIVITY_LAUNCHES_MSG);

mHandler.sendMessageDelayed(msg, APP_SWITCH_DELAY_TIME);

}

}


在以上代码中有两点需要注意:

此处控制机制名为app switch,而不是activity switch。为什么呢?因为如果从受保护的Activity中启动另一个Activity,那么这个新Activity的目的应该是针对同一任务,这次启动就不应该受app switch的制约,反而应该对其大开绿灯。目前,在执行Settings中设置设备策略(DevicePolicy)时就会stopAppSwitch。

执行stopAppSwitch后,应用程序应该调用resumeAppSwitches以允许app switch,但是为了防止应用程序有意或无意忘记resume app switch,系统设置了一个超时时间

(5秒),过了此超时时间,系统将处理相应的消息,其内部会resume app switch。再来看resumeAppSwitches函数,其实现代码如下:

[—>ActivityManagerService:resumeAppSwitches]


public void resumeAppSwitches(){

……//检查调用进程是否有STOP_APP_SWITCHES权限

synchronized(this){

mAppSwitchesAllowedTime=0;

}

//注意,系统并不在此函数内启动那些被阻止的Activity

}


在resumeAppSwitches中只设置mAppSwitchesAllowedTime的值为0,它并不处理在stop和resume这段时间内积攒起的Pending请求,那么这些请求是在何时被处理的呢?

从前面代码可知,如果在执行resume app switch后,又有新的请求需要处理,则先处理那些pending的请求(调用doPendingActivityLaunchesLocked)。

在resumeAppSwitches中并未撤销stopAppSwitches函数中设置的超时消息,所以在处理那条超时消息的过程中,也会处理pending的请求。

在本例中,由于不考虑app switch的情况,那么接下来的工作就是调用startActivity-UncheckedLocked函数来处理本次Activity的启动请求。此时,我们已经创建了一个ActivityRecord用于保存目标Activity的相关信息。

2.startActivityUncheckedLocked函数分析

startActivityUncheckedLocked函数很长,但是目的比较简单,即为新创建的Activity-Record找到一个合适的Task。本例最终的结果是创建一个新的Task,其中startActivity-UncheckedLocked函数的处理逻辑却比较复杂,对它的分析可分为三个阶段,下面先看第一阶段。

(1)startActivityUncheckedLocked分析之一

这部分的代码如下:

[—>ActivityStack.java:startActivityUncheckedLocked]


final int startActivityUncheckedLocked(ActivityRecord r,

ActivityRecord sourceRecord, Uri[]grantedUriPermissions,

int grantedMode, boolean onlyIfNeeded, boolean doResume){

//在本例中,sourceRecord为null, onlyIfNeeded为false, doResume为true

final Intent intent=r.intent;

final int callingUid=r.launchedFromUid;

int launchFlags=intent.getFlags();

//判断是否需要调用因本次Activity启动而被系统移到后台的当前Activity的

//onUserLeaveHint函数。可阅读SDK文档中关于Activity onUserLeaveHint函数的说明

mUserLeaving=(launchFlags&Intent.FLAG_ACTIVITY_NO_USER_ACTION)==0;

//设置ActivityRecord的delayedResume为true,本例中的doResume为true

if(!doResume)r.delayedResume=true;

//在本例中,notTop为null

ActivityRecord notTop=(launchFlags&Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP)

!=0?r:null;

if(onlyIfNeeded){……//在本例中,该变量为false,故略去相关代码

}

//根据sourceRecord的情况进行对应处理。你能理解下面这段if/else的判断语句吗

if(sourceRecord==null){

//如果请求的发起者为空,则需要新建一个Task

if((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK)==0)

launchFlags|=Intent.FLAG_ACTIVITY_NEW_TASK;

}else if(sourceRecord.launchMode==ActivityInfo.LAUNCH_SINGLE_INSTANCE){

//如果sourceRecord单独占一个Instance,则新的Activity必然处于另一个Task中

launchFlags|=Intent.FLAG_ACTIVITY_NEW_TASK;

}else if(r.launchMode==ActivityInfo.LAUNCH_SINGLE_INSTANCE

||r.launchMode==ActivityInfo.LAUNCH_SINGLE_TASK){

//如果启动模式设置了singleTask或singleInstance,则也要创建新Task

launchFlags|=Intent.FLAG_ACTIVITY_NEW_TASK;

}//if(sourceRecord==null)判断结束

//如果新Activity和接收结果的Activity不在一个Task中,则不能启动新的Activity

if(r.resultTo!=null&&(launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK)!=0){

sendActivityResultLocked(-1,r.resultTo, r.resultWho, r.requestCode,

Activity.RESULT_CANCELED, null);

r.resultTo=null;

}


startActivityUncheckedLocked第一阶段的工作还算简单,主要确定是否需要为新的Activity创建一个Task,即是否设置FLAG_ACTIVITY_NEW_TASK标志。

接下来看下一阶段的工作。

(2)startActivityUncheckedLocked分析之二

这部分的代码如下:

[—>ActivityStack.java:startActivityUncheckedLocked]


boolean addingToTask=false;

TaskRecord reuseTask=null;

if(((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK)!=0&&

(launchFlags&Intent.FLAG_ACTIVITY_MULTIPLE_TASK)==0)

||r.launchMode==ActivityInfo.LAUNCH_SINGLE_TASK

||r.launchMode==ActivityInfo.LAUNCH_SINGLE_INSTANCE){

if(r.resultTo==null){

//搜索mHistory,得到一个ActivityRecord

ActivityRecord taskTop=r.launchMode!=

ActivityInfo.LAUNCH_SINGLE_INSTANCE

?findTaskLocked(intent, r.info)

:findActivityLocked(intent, r.info);

if(taskTop!=null){

……//这么多复杂的逻辑处理,无非就是要找到一个合适的Task,然后对应做一些

//处理。此处不讨论这段代码,读者可根据工作中的具体情况进行研究

}

}//if(r.resultTo==null)判断结束

}


在本例中,目标Activity首次登场,所以前面的逻辑处理都没有起作用,建议读者根据具体情况分析该段代码。

下面来看startActivityUncheckLocked第三阶段的工作。

(3)startActivityUncheckLocked分析之三

这部分的代码如下:

[—>ActivityStack.java:startActivityUncheckLocked]


if(r.packageName!=null){

//判断目标Activity是否已经在栈顶,如果是,需要判断是创建一个新的Activity

//还是调用onNewIntent(singleTop模式的处理)

ActivityRecord top=topRunningNonDelayedActivityLocked(notTop);

if(top!=null&&r.resultTo==null){

……//不讨论此段代码

}//if(top!=null……)结束

}else{

……//通知错误

return START_CLASS_NOT_FOUND;

}

//在本例中,需要创建一个Task

boolean newTask=false;

boolean keepCurTransition=false;

if(r.resultTo==null&&!addingToTask

&&(launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK)!=0){

if(reuseTask==null){

mService.mCurTask++;//AMS中保存了当前Task的数量

if(mService.mCurTask<=0)mService.mCurTask=1;

//为该ActivityRecord设置一个新的TaskRecord

r.setTask(new TaskRecord(mService.mCurTask, r.info, intent),

null, true);

}else r.setTask(reuseTask, reuseTask, true);

newTask=true;

//下面这个函数为Android 4.0新增的,用于处理FLAG_ACTIVITY_TASK_ON_HOME的情况,

//请阅读SDK文档对Intent的相关说明

moveHomeToFrontFromLaunchLocked(launchFlags);

}else if……//其他处理情况

//授权控制。在SDK中启动Activity的函数没有授权设置方面的参数。在实际工作中,笔者曾碰

//到过一个有趣的情况:在开发的一款定制系统中,用浏览器下载了受DRM保护的图片,

//此时要启动Gallery3D来查看该图片,但是由于为DRM目录设置了读写权限,而Gallery3D

//并未声明相关权限,结果抛出异常,导致不能浏览该图片。由于startActivity等函数不能设置

//授权,最终只能修改Gallery3D并为其添加use-permissions项

if(grantedUriPermissions!=null&&callingUid>0){

for(int i=0;i<grantedUriPermissions.length;i++){

mService.grantUriPermissionLocked(callingUid, r.packageName,

grantedUriPermissions[i],grantedMode,

r.getUriPermissionsLocked());

}

mService.grantUriPermissionFromIntentLocked(callingUid, r.packageName,

intent, r.getUriPermissionsLocked());

//调用startActivityLocked,此时ActivityRecord和TaskRecord均创建完毕

startActivityLocked(r, newTask, doResume, keepCurTransition);

return START_SUCCESS;

}//startActivityUncheckLocked函数结束


startActivityUncheckLocked的第三阶段工作也比较复杂,不过针对本例,它将创建一个新的TaskRecord,并调用startActivityLocked函数进行处理。

下面我们分析startActivityLocked函数。

(4)startActivityLocked函数分析

该函数的实现代码如下:

[—>ActivityStack.java:startActivityLocked]


private final void startActivityLocked(ActivityRecord r, boolean newTask,

boolean doResume, boolean keepCurTransition){

final int NH=mHistory.size();

int addPos=-1;

if(!newTask){//如果不是新Task,则从mHistory中找到对应的ActivityRecord的位置

……

}

if(addPos<0)addPos=NH;

//否则加到mHistory数组的最后

mHistory.add(addPos, r);

//设置ActivityRecord的inHistory变量为true,表示已经加到mHistory数组中了

r.putInHistory();

r.frontOfTask=newTask;

if(NH>0){

//判断是否显示Activity切换动画之类的事情,需要与WindowManagerService交互

}

//最终调用resumeTopActivityLocked

if(doResume)resumeTopActivityLocked(null);//重点分析这个函数

}


在以上列出的startActivityLocked函数中,略去了一部分逻辑处理,这部分内容和Activity之间的切换动画有关(通过这些动画,使切换过程看起来更加平滑和美观,需和WMS交互)。

提示 笔者认为,此处将Activity切换和动画处理这两个逻辑揉到一起并不合适,但是似乎也没有更合适的地方来进行该工作了。读者不妨自行研读一下该段代码以加深体会。

(5)startActivityUncheckedLocked总结

说实话,startActivityUncheckedLocked函数的复杂度超乎笔者的想象,只这些函数名就够让人头疼的。但是针对本例而言,相关逻辑的难度还算适中,毕竟这是Activity启动流程中最简单的情况。可用一句话总结本例中startActivityUncheckedLocked函数的功能:创建ActivityRecord和TaskRecord,并将ActivityRecord添加到mHistory末尾,然后调用resumeTopActivityLocked启动它。

下面用一节来分析resumeTopActivityLocked函数。

3.resumeTopActivityLocked函数分析

resumeTopActivityLocked函数的实现代码如下:

[—>ActivityStack.java:resumeTopActivityLocked]


final boolean resumeTopActivityLocked(ActivityRecord prev){

//从mHistory中找到第一个需要启动的ActivityRecord

ActivityRecord next=topRunningActivityLocked(null);

final boolean userLeaving=mUserLeaving;

mUserLeaving=false;

if(next==null){

//如果mHistory中没有要启动的Activity,则启动Home

if(mMainStack)return mService.startHomeActivityLocked();

}

//在本例中,next将是目标Activity

next.delayedResume=false;

……//和WMS交互,略去

//将该ActivityRecord从下面几个队列中移除

mStoppingActivities.remove(next);

mGoingToSleepActivities.remove(next);

next.sleeping=false;

mWaitingVisibleActivities.remove(next);

//如果当前正在中断一个Activity,需先等待那个Activity pause完毕,然后系统会重新

//调用resumeTopActivityLocked函数以找到下一个要启动的Activity

if(mPausingActivity!=null)return false;

/**请读者注意*/

//①mResumedActivity指向上一次启动的Activity,也就是当前界面显示的这个Activity

//在本例中,当前Activity就是Home界面

if(mResumedActivity!=null){

//先中断Home。这种情况放到最后进行分析

startPausingLocked(userLeaving, false);

return true;

}

//②如果mResumedActivity为空,则一定是系统第一个启动的Activity,读者应能猜测到它就

//是Home

……//如果prev不为空,则需要通知WMS进行与Activity切换相关的工作

try{

//通知PKMS修改该Package stop状态,详细信息参考4.3.1节的说明

AppGlobals.getPackageManager().setPackageStoppedState(

next.packageName, false);

}……

if(prev!=null){

……//还是和WMS有关,通知它停止绘画

}

if(next.app!=null&&next.app.thread!=null){

//如果该ActivityRecord已有对应的进程存在,则只需要重启Activity。就本例而言,

//此进程还不存在,所以要先创建一个应用进程

}else{

//第一次启动

if(!next.hasBeenLaunched){

next.hasBeenLaunched=true;

}else{

……//通知WMS显示启动界面

}

//调用另外一个startSpecificActivityLocked函数

startSpecificActivityLocked(next, true, true);

}

return true;

}


resumeTopActivityLocked函数中有两个非常重要的关键点:

如果mResumedActivity不为空,则需要先暂停(pause)这个Activity。由代码中的注释可知,mResumedActivity代表上一次启动的(即当前正显示的)Activity。现在要启动一个新的Activity,须先停止当前Activity,这部分工作由startPausingLocked函数完成。

mResumedActivity什么时候为空呢?当然是在启动全系统第一个Activity时,即启动Home界面的时候。除此之外,该值都不会为空。

上面代码中先分析了第二个关键点,即mResumedActivity为空的情况。选择先分析此种情况的原因是:如果先分析startPausingLocked,则后续分析会涉及3个进程,即当前Activity所在进程、AMS所在进程及目标进程,分析的难度相当大。

好了,继续我们的分析。resumeTopActivityLocked最后将调用另外一个startSpecific-ActivityLocked,该函数将真正创建一个应用进程。

(1)startSpecificActivityLocked分析

这部分的代码如下:

[—>ActivityStack.java:startSpecificActivityLocked]


private final void startSpecificActivityLocked(ActivityRecord r,

boolean andResume, boolean checkConfig){

//从AMS中查询是否已经存在满足要求的进程(根据processName和uid来查找)

//在本例中,查询结果应该为null

ProcessRecord app=mService.getProcessRecordLocked(r.processName,

r.info.applicationInfo.uid);

//设置启动时间等信息

if(r.launchTime==0){

r.launchTime=SystemClock.uptimeMillis();

if(mInitialStartTime==0)mInitialStartTime=r.launchTime;

}else if(mInitialStartTime==0){

mInitialStartTime=SystemClock.uptimeMillis();

}

//如果该进程存在并已经向AMS注册(例如之前在该进程中启动了其他Activity)

if(app!=null&&app.thread!=null){

try{

app.addPackage(r.info.packageName);

//通知该进程中的启动目标Activity

realStartActivityLocked(r, app, andResume, checkConfig);

return;

}……

}

//如果该进程不存在,则需要调用AMS的startProcessLocked创建一个应用进程

mService.startProcessLocked(r.processName, r.info.applicationInfo,

true,0,"activity",r.intent.getComponent(),false);

}


AMS的startProcessLocked函数将创建一个新的应用进程,下面将分析这个函数。

(2)startProcessLocked分析

这部分内容的代码如下:

[—>ActivityManagerService.java:startProcessLocked]


final ProcessRecord startProcessLocked(String processName,

ApplicationInfo info, boolean knownToBeDead, int intentFlags,

String hostingType, ComponentName hostingName,

boolean allowWhileBooting){

//根据processName和uid寻找是否已经存在ProcessRecord

ProcessRecord app=getProcessRecordLocked(processName, info.uid);

if(app!=null&&app.pid>0){

……//处理相关情况

}

String hostingNameStr=hostingName!=null

?hostingName.flattenToShortString():null;

//①处理FLAG_FROM_BACKGROUND标志,见下文解释

if((intentFlags&Intent.FLAG_FROM_BACKGROUND)!=0){

if(mBadProcesses.get(info.processName, info.uid)!=null)

return null;

}else{

mProcessCrashTimes.remove(info.processName, info.uid);

if(mBadProcesses.get(info.processName, info.uid)!=null){

mBadProcesses.remove(info.processName, info.uid);

if(app!=null)app.bad=false;

}

}

if(app==null){

//创建一个ProcessRecord,并保存到mProcessNames中。注意,此时还没有创建实际进程

app=newProcessRecordLocked(null, info, processName);

mProcessNames.put(processName, info.uid, app);

}else app.addPackage(info.packageName);

……

//②调用另外一个startProcessLocked函数

startProcessLocked(app, hostingType, hostingNameStr);

return(app.pid!=0)?app:null;

}


在以上代码中列出两个关键点,其中第一个和FLAG_FROM_BACKGROUND有关,相关知识点如下:

FLAG_FROM_BACKGROUND标志发起这次启动的Task属于后台任务。很显然,手机中没有界面供用户操作位于后台Task中的Activity。如果没有设置该标志,那么这次启动请求就是由前台Task因某种原因而触发的(例如,用户单击某个按钮)。

如果一个应用进程在1分钟内连续崩溃超过2次,则AMS会将其ProcessRecord加入所谓的mBadProcesses中。一个应用崩溃后,系统会弹出一个警告框以提醒用户。但是,如果一个后台Task启动了一个“Bad Process”,然后该Process崩溃,结果弹出一个警告框,那么用户就会觉得很奇怪:“为什么突然弹出一个框?”因此,此处将禁止后台Task启动“Bad Process”。如果用户主动选择启动(例如,单击一个按钮),则不能禁止该操作,并且要把应用进程从mBadProcesses中移除,以给它们“重新做人”的机会。当然,若作为测试工作者,要是该应用每次启动后都会崩溃,就需要其不停地去启动该应用以达到测试的目的。

提示 这其实是一种安全机制,防止不健全的程序不断启动可能会崩溃的组件,但是这种机制并不限制用户的行为。

下面来看第二个关键点,即另一个startProcessLocked函数,其代码如下:

[—>ActivityManagerService.java:startProcessLocked]


private final void startProcessLocked(ProcessRecord app,

String hostingType, String hostingNameStr){

if(app.pid>0&&app.pid!=MY_PID){

synchronized(mPidsSelfLocked){

mPidsSelfLocked.remove(app.pid);

mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);

}

app.pid=0;

}

//mProcessesOnHold用于保存那些在系统还没有准备好就提前请求启动的ProcessRecord

mProcessesOnHold.remove(app);

updateCpuStats();

System.arraycopy(mProcDeaths,0,mProcDeaths,1,mProcDeaths.length-1);

mProcDeaths[0]=0;

try{

int uid=app.info.uid;

int[]gids=null;

try{//从PKMS中查询该进程所属的gid

gids=mContext.getPackageManager().getPackageGids(

app.info.packageName);

}……

……//工厂测试

int debugFlags=0;

if((app.info.flags&ApplicationInfo.FLAG_DEBUGGABLE)!=0){

debugFlags|=Zygote.DEBUG_ENABLE_DEBUGGER;

debugFlags|=Zygote.DEBUG_ENABLE_CHECKJNI;

}……//设置其他一些debugFlags

//发送消息给Zygote,它将派生一个子进程,该子进程执行ActivityThread的main函数

//注意,我们传递给Zygote的参数并没有包含任何与Activity相关的信息。现在仅仅启动

//一个应用进程

Process.ProcessStartResult startResult=

Process.start("android.app.ActivityThread",

app.processName, uid, uid, gids, debugFlags,

app.info.targetSdkVersion, null);

//电量统计项

BatteryStatsImpl bs=app.batteryStats.getBatteryStats();

synchronized(bs){

if(bs.isOnBattery())app.batteryStats.incStartsLocked();

}

//如果该进程为persisitent,则需要通知Watchdog,实际上processStarted内部只

//关心刚才创建的进程是不是com.android.phone

if(app.persistent){

Watchdog.getInstance().processStarted(app.processName,

startResult.pid);

}

app.pid=startResult.pid;

app.usingWrapper=startResult.usingWrapper;

app.removed=false;

synchronized(mPidsSelfLocked){

//以pid为key,将代表该进程的ProcessRecord对象加入到mPidsSelfLocked中保管

this.mPidsSelfLocked.put(startResult.pid, app);

//发送一个超时消息,如果这个新创建的应用进程10秒内没有和AMS交互,则可断定

//该应用进程启动失败

Message msg=mHandler.obtainMessage(PROC_START_TIMEOUT_MSG);

msg.obj=app;

//正常的超时时间为10秒。不过如果该应用进程通过valgrind加载,则延长到300秒

//valgrind是Linux平台上一款检查内存泄露的程序,被加载的应用将在它的环境中工作,

//这项工作需耗费较长时间。读者可查询valgrind的用法

mHandler.sendMessageDelayed(msg, startResult.usingWrapper

?PROC_START_TIMEOUT_WITH_WRAPPER:PROC_START_TIMEOUT);

}……

}


startProcessLocked通过发送消息给Zygote以派生一个应用进程[1],读者仔细研究所发消息的内容,会发现此处并未设置和Activity相关的信息,也就是说,该进程启动后,将完全不知道自己要干什么,怎么办?下面就此进行分析。

4.startActivity分析之半程总结

至此,我们已经走完了startActivity分析之旅的一半路程,一路走来,我们越过了很多“险滩恶途”。此处用图6-14来记录前半程中的各个关键点。

图6-14列出了针对本例的调用顺序,其中对每个函数的大体功能也做了简单描述。

注意 图6-14中的调用顺序及功能说明只是针对本例而言的。读者以后可结合具体情况再深入研究其中的内容。

6.3.3 startActivityLocked分析 - 图1

图 6-14 startActivity半程总结

5.应用进程的创建及初始化

如前所述,应用进程的入口是ActivityThread的main函数,它是在主线程中执行的,其代码如下:

[—>ActivityThread.java:main]


public static void main(String[]args){

SamplingProfilerIntegration.start();

//和调试及strictMode有关

CloseGuard.setEnabled(false);

//设置进程名为"<pre-initialized>"

Process.setArgV0("<pre-initialized>");

//准备主线程消息循环

Looper.prepareMainLooper();

if(sMainThreadHandler==null)

sMainThreadHandler=new Handler();

//创建一个ActivityThread对象

ActivityThread thread=new ActivityThread();

//①调用attach函数,注意其参数值为false

thread.attach(false);

Looper.loop();//进入主线程消息循环

throw new RuntimeException("Main thread loop unexpectedly exited");

}


在main函数内部将创建一个消息循环Loop,接着调用ActivityThread的attach函数,最终将主线程加入消息循环。

我们在分析AMS的setSystemProcess时曾分析过ActivityThread的attach函数,那时传入的参数值为true。现在来看设置其为false的情况。

[—>ActivityThread.java:attach]


private void attach(boolean system){

sThreadLocal.set(this);

mSystemThread=system;

if(!system){

ViewRootImpl.addFirstDrawHandler(new Runnable(){

public void run(){

ensureJitEnabled();

}

});

//设置在DDMS中看到的本进程的名字为"<pre-initialized>"

android.ddm.DdmHandleAppName.setAppName("<pre-initialized>");

//设置RuntimeInit的mApplicationObject参数,后续会介绍RuntimeInit类

RuntimeInit.setApplicationObject(mAppThread.asBinder());

//获取和AMS交互的Binder客户端

IActivityManager mgr=ActivityManagerNative.getDefault();

try{

//①调用AMS的attachApplication, mAppThread为ApplicationThread类型,

//它是应用进程和AMS交互的接口

mgr.attachApplication(mAppThread);

}……

}else……//system process的处理

ViewRootImpl.addConfigCallback(new ComponentCallbacks2()

{……//添加回调函数});

}


我们知道,AMS创建一个应用进程后,会设置一个超时时间(一般是10秒)。如果超过这个时间,应用进程还没有和AMS交互,则断定该进程创建失败。所以,应用进程启动后,需要尽快和AMS交互,即调用AMS的attachApplication函数。在该函数内部将调用attachApplicationLocked,所以此处直接分析attachApplicationLocked,先看其第一阶段的工作。

(1)attachApplicationLocked分析之一

这部分的代码如下:

[—>ActivityManagerService.java:attachApplicationLocked]


private final boolean attachApplicationLocked(IApplicationThread thread,

int pid){//此pid代表调用进程的pid

ProcessRecord app;

if(pid!=MY_PID&&pid>=0){

synchronized(mPidsSelfLocked){

app=mPidsSelfLocked.get(pid);//根据pid查找对应的ProcessRecord对象

}

}else

app=null;

/*

如果该应用进程由AMS启动,则它一定在AMS中有对应的ProcessRecord,读者可回顾前面创建

应用进程的代码:AMS先创建了一个ProcessRecord对象,然后才发命令给Zygote。

如果此处app为null,则表示AMS没有该进程的记录,故需要“杀死”它

*/

if(app==null){

if(pid>0&&pid!=MY_PID)//如果pid大于零,且不是system_server进程,则

//Quietly(即不打印任何输出)“杀死”process

Process.killProcessQuiet(pid);

else{

//调用ApplicationThread的scheduleExit函数。应用进程完成处理工作后

//将退出运行

//为何不像上面一样直接“杀死”它呢?读者可查阅Linux pid相关的知识自行解答

thread.scheduleExit();

}

return false;

}

/*

判断app的thread是否为空,如果不为空,则表示该ProcessRecord对象还未和一个

应用进程绑定。注意,app是根据pid查找到的,如果旧进程没有被“杀死”,则系统不会重用

该pid。为什么此处会出现ProcessRecord thread不为空的情况呢?见下面代码的注释说明

*/

if(app.thread!=null)handleAppDiedLocked(app, true, true);

String processName=app.processName;

try{

/*

创建一个应用进程“讣告”接收对象。当应用进程退出时,该对象的binderDied将被调

用。这样,AMS就能做相应处理。binderDied函数将在另外一个线程中执行,其内部也会

调用handleAppDiedLocked。假如用户在binderDied被调用之前又启动一个进程,

那么就会出现以上代码中app.thread不为null的情况。这是多线程环境中常出现的

情况,不熟悉多线程编程的读者要仔细体会。

*/

AppDeathRecipient adr=new AppDeathRecipient(pp, pid, thread);

thread.asBinder().linkToDeath(adr,0);

app.deathRecipient=adr;

}……

//设置该进程的调度优先级和oom_adj相关的成员

app.thread=thread;

app.curAdj=app.setAdj=-100;

app.curSchedGroup=Process.THREAD_GROUP_DEFAULT;

app.setSchedGroup=Process.THREAD_GROUP_BG_NONINTERACTIVE;

app.forcingToForeground=null;

app.foregroundServices=false;

app.hasShownUi=false;

app.debugging=false;

//启动成功,从消息队列中撤销PROC_START_TIMEOUT_MSG消息

mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);


attachApplicationLocked第一阶段的工作比较简单:

设置代表该应用进程的ProcessRecord对象的一些成员变量,如用于和应用进程交互的thread对象、进程调度优先级及oom_adj的值等。

从消息队列中撤销PROC_START_TIMEOUT_MSG。至此,该进程启动成功,但是这一阶段的工作仅针对进程本身(如设置调度优先级,oom_adj等),还没有涉及和Activity启动相关的内容,这部分工作将在第二阶段完成。

(2)attachApplicationLocked分析之二

这部分的代码如下:

[—>ActivityManagerService.java:attachApplicationLocked]


……

//system_server早就启动完毕,所以normalMode为true

boolean normalMode=mProcessesReady||isAllowedWhileBooting(app.info);

/*

我们在6.2.3节中分析过generateApplicationProvidersLocked函数,

在该函数内部将查询(根据进程名,uid确定)PKMS以获取需运行在该进程中的ContentProvider

*/

List providers=normalMode?generateApplicationProvidersLocked(app):null;

try{

int testMode=IApplicationThread.DEBUG_OFF;

if(mDebugApp!=null&&mDebugApp.equals(processName)){

……//处理debug选项

}

……//处理Profile

boolean isRestrictedBackupMode=false;

……//

//dex化对应的APK包

ensurePackageDexOpt(app.instrumentationInfo!=null?

app.instrumentationInfo.packageName:app.info.packageName);

//如果设置了Instrumentation类,该类所在的Package也需要dex化

if(app.instrumentationClass!=null)

ensurePackageDexOpt(app.instrumentationClass.getPackageName());

ApplicationInfo appInfo=app.instrumentationInfo!=null

?app.instrumentationInfo:app.info;

//查询该Application使用的CompatibiliyInfo

app.compat=compatibilityInfoForPackageLocked(appInfo);

if(profileFd!=null)//用于记录性能文件

profileFd=profileFd.dup();

//①通过ApplicationThread和应用进程交互,调用其bindApplication函数

thread.bindApplication(processName, appInfo, providers,

app.instrumentationClass, profileFile, profileFd,

profileAutoStop, app.instrumentationArguments,

app.instrumentationWatcher, testMode,

isRestrictedBackupMode||!normalMode, app.persistent,

mConfiguration, app.compat, getCommonServicesLocked(),

mCoreSettingsObserver.getCoreSettingsLocked());

//updateLruProcessLocked函数以后再作分析

updateLruProcessLocked(app, false, true);

//记录两个时间

app.lastRequestedGc=app.lastLowMemory=SystemClock.uptimeMillis();

}……//try结束

..//从mProcessesOnHold和mPersistentStartingProcesses中删除相关信息

mPersistentStartingProcesses.remove(app);

mProcessesOnHold.remove(app);


由以上代码可知,第二阶段的工作主要是为调用ApplicationThread的bindApplication做准备,我们将在后面的章节中分析该函数的具体内容。此处先来看它的原型。


/*

正如我们在前面分析时提到的,刚创建的这个进程并不知道自己的历史使命是什么,甚至连自己的

进程名都不知道,只能设为<pre-initialized>。其实,Android应用进程的历史使命是

AMS在其启动后才赋予它的,这一点和我们理解的一般意义上的进程不太一样。根据之前的介绍,

Android的组件应该运行在Android运行环境中。从OS角度来说,该运行环境需要和一个进程绑定。

所以,创建应用进程这一步只是创建了一个能运行Android运行环境的容器,而我们的工作实际上

还远未结束。

bindApplication的功能就是创建并初始化位于该进程中的Android运行环境

*/

public final void bindApplication(

String processName,//进程名,一般是package名

ApplicationInfo appInfo,//该进程对应的ApplicationInfo

List<ProviderInfo>providers,//在该Package中声明的Provider信息

ComponentName instrumentationName,//和instrumentation有关

//下面3个参数和性能统计有关

String profileFile,

ParcelFileDescriptor profileFd, boolean autoStopProfiler,

//这两个和Instrumentation有关,在本例中,这几个参数暂时都没有用

Bundle instrumentationArgs,

IInstrumentationWatcher instrumentationWatcher,

int debugMode,//调试模式

boolean isRestrictedBackupMode,

boolean persistent,//该进程是否是为persistent进程

Configuration config,//当前的配置信息,如屏幕大小和语言等

CompatibilityInfo compatInfo,//兼容信息

//AMS将常用的Service信息传递给应用进程,目前传递的Service信息只有PKMS、

//WMS及AlarmManagerService。读者可参看AMS getCommonServicesLocked函数

Map<String, IBinder>services,

Bundle coreSettings)//核心配置参数,目前仅有“long_press”值


对bindApplication的原型分析就到此为止,再来看attachApplicationLocked最后一阶段的工作。

(3)attachApplicationLocked分析之三

这部分的代码如下:

[—>ActivityManagerService.java:attachApplicationLocked]


boolean badApp=false;

boolean didSomething=false;

/*

至此,应用进程已经准备好了Android运行环境,下面这条调用代码将返回ActivityStack中

第一个需要运行的ActivityRecord。由于多线程的原因,能保证得到的hr就是我们的目标

Activity吗?

*/

ActivityRecord hr=mMainStack.topRunningActivityLocked(null);

if(hr!=null&&normalMode){

//需要根据processName和uid等确定该Activity是否与目标进程有关

if(hr.app==null&&app.info.uid==hr.info.applicationInfo.uid

&&processName.equals(hr.processName)){

try{

//调用AS的realStartActivityLocked启动该Activity,最后两个参数为true

if(mMainStack.realStartActivityLocked(hr, app, true, true)){

didSomething=true;

}

}catch(Exception e){

badApp=true;//设置badApp为true

}

}else{

//如果hr和目标进程无关,则调用ensureActivitiesVisibleLocked函数处理它

mMainStack.ensureActivitiesVisibleLocked(hr, null, processName,0);

}

}//if(hr!=null&&normalMode)判断结束

//mPendingServices存储那些因目标进程还未启动而处于等待状态的ServiceRecord

if(!badApp&&mPendingServices.size()>0){

ServiceRecord sr=null;

try{

for(int i=0;i<mPendingServices.size();i++){

sr=mPendingServices.get(i);

//和Activity不一样的是,如果Service不属于目标进程,则暂不处理

if(app.info.uid!=sr.appInfo.uid

||!processName.equals(sr.processName))continue;//继续循环

//该Service将运行在目标进程中,所以从mPendingService中移除它

mPendingServices.remove(i);

i—;

//处理此Service的启动,以后再作分析

realStartServiceLocked(sr, app);

didSomething=true;//设置该值为true

}

}

}……

……//启动等待的BroadcastReceiver

……//启动等待的BackupAgent,相关代码类似Service的启动

if(badApp){

//如果以上几个组件启动有错误,则设置badApp为true。此处将调用handleAppDiedLocked

//进行处理。该函数我们以后再作分析

handleAppDiedLocked(app, false, true);

return false;

}

/*

调整进程的oom_adj值。didSomething表示在以上流程中是否启动了Acivity或其他组件。

如果启动了任一组件,则didSomething为true。读者以后会知道,这里的启动只是向

应用进程发出对应的指令,客户端进程是否成功处理还是未知数。基于这种考虑,此处不宜

马上调节进程的oom_adj。

读者可简单地把oom_adj看做一种优先级。如果一个应用进程没有运行任何组件,那么当内存

出现不足时,该进程是最先被系统“杀死”的。反之,如果一个进程运行的组件越多,那么它就

越不易被系统“杀死”以回收内存。updateOomAdjLocked就是根据该进程中组件的情况对应

调节进程的oom_adj值的。

*/

if(!didSomething)updateOomAdjLocked();

return true;

}


attachApplicationLocked第三阶段的工作就是通知应用进程启动Activity和Service等组件,其中用于启动Activity的函数是ActivityStack realStartActivityLocked。

此处先分析应用进程的bindApplication,该函数为应用进程绑定一个Application。

提示 还记得AMS中System Context执行的两次init吗?第二次init的功能就是将Context和对应的Application绑定在一起。

(4)ApplicationThread的bindApplication分析

bindApplication在ApplicationThread中的实现,其代码如下:

[—>ActivityThread.java:bindApplication]


public final void bindApplication(……){

if(services!=null)//保存AMS传递过来的系统Service信息

ServiceManager.initServiceCache(services);

//向主线程消息队列添加SET_CORE_SETTINGS消息

setCoreSettings(coreSettings);

//创建一个AppBindData对象,其实就是用来存储一些参数

AppBindData data=new AppBindData();

data.processName=processName;

data.appInfo=appInfo;

data.providers=providers;

data.instrumentationName=instrumentationName;

……//将AMS传过来的参数保存到AppBindData中

//向主线程发送H.BIND_APPLICATION消息

queueOrSendMessage(H.BIND_APPLICATION, data);

}


由以上代码可知,ApplicationThread接收到来自AMS的指令后,会将指令中的参数封装到一个数据结构中,然后通过发送消息的方式转交给主线程去处理。BIND_APPLICATION最终将由handleBindApplication函数处理。该函数并不复杂,但是其中有些点是值得关注的,这些点主要是初始化应用进程的一些参数。handleBindApplication函数的代码如下:

[—>ActivityThread.java:handleBindApplication]


private void handleBindApplication(AppBindData data){

mBoundApplication=data;

mConfiguration=new Configuration(data.config);

mCompatConfiguration=new Configuration(data.config);

//初始化性能统计对象

mProfiler=new Profiler();

mProfiler.profileFile=data.initProfileFile;

mProfiler.profileFd=data.initProfileFd;

mProfiler.autoStopProfiler=data.initAutoStopProfiler;

//设置进程名。从此,之前那个名为“<pre_initialized>”的进程终于有了正式的名字

Process.setArgV0(data.processName);

android.ddm.DdmHandleAppName.setAppName(data.processName);

if(data.persistent){

//对于persistent的进程,在低内存设备上,不允许其使用硬件加速显示

Display display=

WindowManagerImpl.getDefault().getDefaultDisplay();

//当内存大于512MB,或者屏幕尺寸大于1024像素*600像素时,可以使用硬件加速

if(!ActivityManager.isHighEndGfx(display))

HardwareRenderer.disable(false);

}

//启动性能统计

if(mProfiler.profileFd!=null)

mProfiler.startProfiling();

//如果目标SDK版本小于12,则设置AsyncTask使用pool executor,否则使用

//serialized executor。这些executor涉及Java Concurrent类,对此不熟悉的读者

//请自行学习和研究

if(data.appInfo.targetSdkVersion<=12)

AsyncTask.setDefaultExecutor(AsyncTask.THREAD_POOL_EXECUTOR);

//设置timezone

TimeZone.setDefault(null);

//设置语言

Locale.setDefault(data.config.locale);

//设置资源及兼容模式

applyConfigurationToResourcesLocked(data.config, data.compatInfo);

applyCompatConfiguration();

//根据传递过来的ApplicationInfo创建一个对应的LoadApk对象

data.info=getPackageInfoNoCheck(data.appInfo, data.compatInfo);

//对于系统APK,如果当前系统为userdebug/eng版,则需要使能dropbox的日志记录

if((data.appInfo.flags&

(ApplicationInfo.FLAG_SYSTEM|

ApplicationInfo.FLAG_UPDATED_SYSTEM_APP))!=0){

StrictMode.conditionallyEnableDebugLogging();

}

/*

如目标SDK版本大于9,则不允许在主线程使用网络操作(如Socket connect等),否则抛出

NetworkOnMainThreadException,这么做的目的是防止应用程序在主线程中因网络操作执行

时间过长而造成用户体验下降。说实话,没有必要进行这种限制,在主线程中是否进行网络操作

是应用的事情。再说,Socket也可作为进程间通信的手段,在这种情况下,网络操作耗时很短。

作为系统,不应该设置这种限制。另外,Goolge可以提供一些开发指南或规范来指导开发者,

而不应如此限制。

*/

if(data.appInfo.targetSdkVersion>9)

StrictMode.enableDeathOnNetwork();

//如果没有设置屏幕密度,则为Bitmap设置默认的屏幕密度

if((data.appInfo.flags

&ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES)==0)

Bitmap.setDefaultDensity(DisplayMetrics.DENSITY_DEFAULT);

if(data.debugMode!=IApplicationThread.DEBUG_OFF){

……//调试模式相关处理

}

IBinder b=ServiceManager.getService(Context.CONNECTIVITY_SERVICE);

IConnectivityManager service=

IConnectivityManager.Stub.asInterface(b);

try{

//设置HTTP代理信息

ProxyProperties proxyProperties=service.getProxy();

Proxy.setHttpProxySystemProperty(proxyProperties);

}catch(RemoteException e){}

if(data.instrumentationName!=null){

//在正常情况下,此条件不满足

}else{

//创建Instrumentation对象,在正常情况都在这个条件下执行

mInstrumentation=new Instrumentation();

}

//如果Package中声明了FLAG_LARGE_HEAP,则可跳过虚拟机的内存限制,放心使用内存

if((data.appInfo.flags&ApplicationInfo.FLAG_LARGE_HEAP)!=0)

dalvik.system.VMRuntime.getRuntime().clearGrowthLimit();

//创建一个Application, data.info为LoadedApk类型,在其内部会通过Java反射机制

//创建一个在该APK AndroidManifest.xml中声明的Application对象

Application app=data.info.makeApplication(

data.restrictedBackupMode, null);

//mInitialApplication保存该进程中第一个创建的Application

mInitialApplication=app;

//安装本Package中携带的ContentProvider

if(!data.restrictedBackupMode){

List<ProviderInfo>providers=data.providers;

if(providers!=null){

//installContentProviders我们已经分析过了

installContentProviders(app, providers);

mH.sendEmptyMessageDelayed(H.ENABLE_JIT,10*1000);

}

}

//调用Application的onCreate函数,做一些初始工作

mInstrumentation.callApplicationOnCreate(app);

}


由以上代码可知,bindApplication函数将设置一些初始化变量,其中最重要的有:

创建一个Application对象,该对象是本进程中运行的第一个Application。

如果该Application有ContentProvider,则应安装它们。

提示 从以上代码中可知,ContentProvider的创建于bindApplication函数中,其时机早于其他组件的创建。

(5)应用进程的创建及初始化总结

本节从应用进程的入口函数main开始,分析了应用进程和AMS之间的两次重要交互,它们分别是:

在应用进程启动后,需要尽快调用AMS的attachApplication函数,该函数是这个刚创建的应用进程第一次和AMS交互。此时的它还默默“无名”,连一个确定的进程名都没有。不过没关系,attachApplication函数将根据创建该应用进程之前所保存的ProcessRecord为其准备一切“手续”。

attachApplication准备好一切后,将调用应用进程的bindApplication函数,在该函数内部将发消息给主线程,最终该消息由handleBindApplication处理。handle-BindApplication将为该进程设置进程名,初始化一些策略和参数信息等。另外,它还创建一个Application对象。同时,如果该Application声明了ContentProvider,还需要为该进程安装ContentProvider。

提示 这个流程有点类似生孩子,一般生之前需要到医院去登记,生完后又需去注册户口,如此这般,这个孩子才会在社会中有合法的身份。

6.ActivityStack realStartActivityLocked分析

如前所述,AMS调用完bindApplication后,将通过realStartActivityLocked启动Activity。在此之前,要创建完应用进程并初始化Android运行环境(除此之外,连Content-Provider都安装好了)。

[—>ActivityStack.java:realStartActivityLocked]


//注意,在本例中该函数的最后两个参数的值都为true

final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,

boolean andResume, boolean checkConfig)throws RemoteException{

r.startFreezingScreenLocked(app,0);

mService.mWindowManager.setAppVisibility(r, true);

if(checkConfig){

……//处理Config发生变化的情况

mService.updateConfigurationLocked(config, r,false);

}

r.app=app;

app.waitingToKill=null;

//将ActivityRecord加到ProcessRecord的activities中保存

int idx=app.activities.indexOf(r);

if(idx<0)app.activities.add(r);

//更新进程的调度优先级等,以后再分析该函数

mService.updateLruProcessLocked(app, true, true);

try{

List<ResultInfo>results=null;

List<Intent>newIntents=null;

if(andResume){

results=r.results;

newIntents=r.newIntents;

}

if(r.isHomeActivity)mService.mHomeProcess=app;

//看看是否有dex对应Package的需要

mService.ensurePackageDexOpt(

r.intent.getComponent().getPackageName());

r.sleeping=false;

r.forceNewConfig=false;……

//①通知应用进程启动Activity

app.thread.scheduleLaunchActivity(new Intent(r.intent),r,

System.identityHashCode(r),r.info, mService.mConfiguration,

r.compat, r.icicle, results, newIntents,!andResume,

mService.isNextTransitionForward(),profileFile, profileFd,

profileAutoStop);

if((app.info.flags&ApplicationInfo.FLAG_CANT_SAVE_STATE)!=0){

……//处理heavy-weight的情况

}

}

}……//try结束

r.launchFailed=false;

……

if(andResume){

r.state=ActivityState.RESUMED;

r.stopped=false;

mResumedActivity=r;//设置mResumedActivity为目标Activity

r.task.touchActiveTime();

//添加该任务到近期任务列表中

if(mMainStack)mService.addRecentTaskLocked(r.task);

//②关键函数,见下文分析

completeResumeLocked(r);

//如果在这些过程中,用户按了Power键,怎么办?

checkReadyForSleepLocked();

r.icicle=null;

r.haveState=false;

}……

//启动系统设置向导Activity,当系统更新或初次使用时需要进行配置

if(mMainStack)mService.startSetupActivityLocked();

return true;

}


在以上代码中有两个关键函数,分别是:scheduleLaunchActivity和completeResume-Locked。其中,scheduleLaunchActivity用于和应用进程交互,通知它启动目标Activity。而completeResumeLocked将继续AMS的处理流程。先来看第一个关键函数。

(1)scheduleLaunchActivity函数分析

[—>ActivityThread.java:scheduleLaunchActivity]


public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,

ActivityInfo info, Configuration curConfig, CompatibilityInfo compatInfo,

Bundle state, List<ResultInfo>pendingResults,

List<Intent>pendingNewIntents, boolean notResumed, boolean isForward,

String profileName, ParcelFileDescriptor profileFd,

boolean autoStopProfiler){

ActivityClientRecord r=new ActivityClientRecord();

……//保存AMS发送过来的参数信息

//向主线程发送消息,该消息的处理在handleLaunchActivity中进行

queueOrSendMessage(H.LAUNCH_ACTIVITY, r);

}


[—>ActivityThread.java:handleMessage]


public void handleMessage(Message msg){

switch(msg.what){

case LAUNCH_ACTIVITY:{

ActivityClientRecord r=(ActivityClientRecord)msg.obj;

//根据ApplicationInfo得到对应的PackageInfo

r.packageInfo=getPackageInfoNoCheck(

r.activityInfo.applicationInfo, r.compatInfo);

//调用handleLaunchActivity处理

handleLaunchActivity(r, null);

}break;

……}


[—>ActivityThread.java:handleLaunchActivity]


private void handleLaunchActivity(ActivityClientRecord r,

Intent customIntent){

unscheduleGcIdler();

if(r.profileFd!=null){……//略去}

handleConfigurationChanged(null, null);/*

①创建Activity,通过Java反射机制创建目标Activity,将在内部完成Activity生命周期

的前两步,即调用其onCreate和onStart函数。至此,我们的目标com.dfp.test.TestActivity

创建完毕

*/

Activity a=performLaunchActivity(r, customIntent);

if(a!=null){

r.createdConfig=new Configuration(mConfiguration);

Bundle oldState=r.state;

//②调用handleResumeActivity,其内部有个关键点,见下文分析

handleResumeActivity(r.token, false, r.isForward);

if(!r.activity.mFinished&&r.startsNotResumed){

……//

.

r.paused=true;

}else{

//如果启动错误,通知AMS

ActivityManagerNative.getDefault()

.finishActivity(r.token, Activity.RESULT_CANCELED, null);

}

}


handleLaunchActivity的工作包括:

首先调用performLaunchActivity,该在函数内部通过Java反射机制创建目标Activity,然后调用它的onCreate及onStart函数。

调用handleResumeActivity,会在其内部调用目标Activity的onResume函数。除此

之外,handleResumeActivity还完成了一件很重要的事情,具体见下面的代码:

[—>ActivityThread.java:handleResumeActivity]


final void handleResumeActivity(IBinder token, boolean clearHide,

boolean isForward){

unscheduleGcIdler();

//内部调用目标Activity的onResume函数

ActivityClientRecord r=performResumeActivity(token, clearHide);

if(r!=null){

final Activity a=r.activity;

final int forwardBit=isForward?

WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION:0;

……

if(!r.onlyLocalRequest){

//将上面完成onResume的Activity保存到mNewActivities中

r.nextIdle=mNewActivities;

mNewActivities=r;

//①向消息队列中添加一个Idler对象

Looper.myQueue().addIdleHandler(new Idler());

}

r.onlyLocalRequest=false;

……

}


根据第2章对MessageQueue的分析,当消息队列中没有其他要处理的消息时,将处理以上代码中通过addIdleHandler添加的Idler对象,也就是说,Idler对象的优先级最低,这是不是说它的工作不重要呢?非也。至少在handleResumeActivity函数中添加的这个Idler并不简单,其代码如下:

[—>ActivityThread.java:Idler]


private class Idler implements MessageQueue.IdleHandler{

public final boolean queueIdle(){

ActivityClientRecord a=mNewActivities;

boolean stopProfiling=false;

……

if(a!=null){

mNewActivities=null;

IActivityManager am=ActivityManagerNative.getDefault();

ActivityClientRecord prev;

do{

if(a.activity!=null&&!a.activity.mFinished){

//调用AMS的activityIdle

am.activityIdle(a.token, a.createdConfig, stopProfiling);

a.createdConfig=null;

}

prev=a;

a=a.nextIdle;

prev.nextIdle=null;

}while(a!=null);//do循环结束

}//if(a!=null)判断结束

……

ensureJitEnabled();

return false;

}//queueIdle函数结束

}


由以上代码可知,Idler将为那些已经完成onResume的Activity调用AMS的activityIdle函数。该函数是Activity成功创建并启动的流程中与AMS交互的最后一步。虽然对应用进程来说,Idler处理的优先级最低,但AMS似乎不这么认为,因为它还设置了超时等待,以处理应用进程没有及时调用activityIdle的情况。这个超时等待即由realStartActivityLocked中最后一个关键点completeResumeLocked函数设置。

(2)completeResumeLocked函数分析

这部分的代码具体如下:

[—>ActivityStack.java:completeResumeLocked]


private final void completeResumeLocked(ActivityRecord next){

next.idle=false;

next.results=null;

next.newIntents=null;

//发送一个超时处理消息,默认为10秒。IDLE_TIMEOUT_MSG就是针对acitivityIdle函数的

Message msg=mHandler.obtainMessage(IDLE_TIMEOUT_MSG);

msg.obj=next;

mHandler.sendMessageDelayed(msg, IDLE_TIMEOUT);

//通知AMS

if(mMainStack)mService.reportResumedActivityLocked(next);

……//略去其他逻辑的代码

}


由以上代码可知,AMS给了应用进程10秒的时间,希望它在10秒内调用activityIdle函数。这个时间不算长,和前面AMS等待应用进程启动的超时时间一样。所以,笔者有些困惑,为什么要把这么重要的操作放到Idler中去做。

下面来看activityIdle函数,在其内部将调用ActivityStack activityIdleInternal。

(3)activityIdleInternal函数分析

这部分的代码具体如下:

[—>ActivityStack.java:activityIdleInternal]


final ActivityRecord activityIdleInternal(IBinder token, boolean fromTimeout,

Configuration config){

/*

如果应用进程在超时时间内调用了activityIdleInternal函数,则fromTimeout为false,

否则,一旦超时,在IDLE_TIMEOUT_MSG的消息处理中也会调用该函数,并设置fromTimeout

为true

*/

ActivityRecord res=null;

ArrayList<ActivityRecord>stops=null;

ArrayList<ActivityRecord>finishes=null;

ArrayList<ActivityRecord>thumbnails=null;

int NS=0;

int NF=0;

int NT=0;

IApplicationThread sendThumbnail=null;

boolean booting=false;

boolean enableScreen=false;

synchronized(mService){

//从消息队列中撤销IDLE_TIMEOUT_MSG

if(token!=null)mHandler.removeMessages(IDLE_TIMEOUT_MSG, token);

int index=indexOfTokenLocked(token);

if(index>=0){

ActivityRecord r=mHistory.get(index);

res=r;

//注意,只有fromTimeout为true,才会执行下面的条件语句

if(fromTimeout)reportActivityLaunchedLocked(fromTimeout, r,-1,-1);

if(config!=null)r.configuration=config;

/*

mLaunchingActivity是一个WakeLock,它能防止在操作Activity过程中掉电,同时

这个WakeLock不能长时间使用,否则有可能耗费过多电量。所以,系统设置了一个超时

处理消息LAUNCH_TIMEOUT_MSG,超时时间为10秒。一旦目标Activity启动成功,

就需要释放WakeLock

*/

if(mResumedActivity==r&&mLaunchingActivity.isHeld()){

mHandler.removeMessages(LAUNCH_TIMEOUT_MSG);

mLaunchingActivity.release();

}

r.idle=true;

mService.scheduleAppGcsLocked();

……

ensureActivitiesVisibleLocked(null,0);

if(mMainStack){

if(!mService.mBooted){

mService.mBooted=true;

enableScreen=true;

}

}//if(mMainStack)判断结束

}else if(fromTimeout){//注意,只有fromTimeout为true,才会执行下面的case

reportActivityLaunchedLocked(fromTimeout, null,-1,-1);

}

/*

①processStoppingActivitiesLocked函数返回那些因本次Activity启动而

被暂停(paused)的Activity

*/

stops=processStoppingActivitiesLocked(true);

……

for(i=0;i<NS;i++){

ActivityRecord r=(ActivityRecord)stops.get(i);

synchronized(mService){

//如果这些Acitivity处于finishing状态,则通知它们执行Destroy操作,最终它们

//的onDestroy函数会被调用

if(r.finishing)finishCurrentActivityLocked(r, FINISH_IMMEDIATELY);

else//否则将通知它们执行stop操作,最终Activity的onStop被调用

stopActivityLocked(r);

}//synchronized结束

}//for循环结束

……//处理等待结束的Activities

//发送ACTION_BOOT_COMPLETED广播

if(booting)mService.finishBooting();

……

return res;

}


在activityIdleInternal中有一个关键点,即处理那些因为本次Activity启动而被暂停的Activity。有两种情况需考虑:

如果被暂停的Activity处于finishing状态(如Activity在其onPause中调用了finish函数),则调用finishCurrentActivityLocked。

否则,要调用stopActivityLocked处理暂停的Activity。

此处涉及除AMS和目标进程外的第三个进程,即被切换到后台的那个进程。不过至此,我们的目标Activity终于正式登上了历史舞台。

提示 本例的分析结束了吗?没有。因为am设置了-W选项,所以其实我们还在startActivity-AndWait函数中等待结果。ActivityStack中有两个函数能够触发AMS notifyAll,一个是reportActivityLaunchedLocked,另一个是reportActivityVisibleLocked。前面介绍的activityInternal函数只在fromTimeout为true时才会调用reportActivityLaunchedLocked,但本例中fromTimeout为false,这该如何是好?该问题的解答非常复杂,简单点说就是:当Activity显示出来时,其在AMS中对应ActivityRecord对象的windowVisible函数将被调用,其内部会触发reportActivityLaunched-Locked函数,这样我们的startActivityAndWait才能被唤醒。

7.startActivity分析之后半程总结

总结startActivity后半部分的流程,主要涉及目标进程和AMS的交互,如图6-15所示。

6.3.3 startActivityLocked分析 - 图2

图 6-15 startActivity后半程总结

图6-15中涉及16个重要函数调用,而且这仅是startActivity后半部分的调用流程,可见整个流程有多么复杂!

8.startPausingLocked函数分析

现在我们分析图6-14中的startPausingLocked分支。根据前面的介绍,当启动一个新Activity时,系统将先行处理当前的Activity,即调用startPausingLocked函数来暂停当前Activity。

(1)startPausingLocked分析

该部分的代码如下:

[—>ActivityStack.java:startPausingLocked]


private final void startPausingLocked(boolean userLeaving, boolean uiSleeping){

//mResumedActivity保存当前正显示的Activity

ActivityRecord prev=mResumedActivity;

mResumedActivity=null;

//设置mPausingActivity为当前Activity

mPausingActivity=prev;

mLastPausedActivity=prev;

prev.state=ActivityState.PAUSING;//设置状态为PAUSING

prev.task.touchActiveTime();

……

if(prev.app!=null&&prev.app.thread!=null){

try{

//①调用当前Activity所在进程的schedulePauseActivity函数

prev.app.thread.schedulePauseActivity(prev, prev.finishing,

userLeaving, prev.configChangeFlags);

if(mMainStack)mService.updateUsageStats(prev, false);

}……//catch分支

}……//else分支

if(!mService.mSleeping&&!mService.mShuttingDown){

//获取WakeLock,以防止在Activity切换过程中掉电

mLaunchingActivity.acquire();

if(!mHandler.hasMessages(LAUNCH_TIMEOUT_MSG)){

Message msg=mHandler.obtainMessage(LAUNCH_TIMEOUT_MSG);

mHandler.sendMessageDelayed(msg, LAUNCH_TIMEOUT);

}

}

if(mPausingActivity!=null){

//暂停输入事件派发

if(!uiSleeping)prev.pauseKeyDispatchingLocked();

//设置PAUSE超时,时间为500毫秒,这个时间相对较短

Message msg=mHandler.obtainMessage(PAUSE_TIMEOUT_MSG);

msg.obj=prev;

mHandler.sendMessageDelayed(msg, PAUSE_TIMEOUT);

}……//else分支

}


startPausingLocked将调用应用进程的schedulePauseActivity函数,并设置500毫秒的超时时间,所以应用进程需尽快完成相关处理。和scheduleLaunchActivity一样,schedulePauseActivity将向ActivityThread主线程发送PAUSE_ACTIVITY消息,最终该消息由handlePauseActivity来处理。

(2)handlePauseActivity分析

这部分的代码如下:

[—>ActivityThread.java:handlePauseActivity]


private void handlePauseActivity(IBinder token, boolean finished,

boolean userLeaving, int configChanges){

//当Activity处于finishing状态时,finished参数为true,不过在本例中该值为false

ActivityClientRecord r=mActivities.get(token);

if(r!=null){

//调用Activity的onUserLeaving函数,

if(userLeaving)performUserLeavingActivity(r);

r.activity.mConfigChangeFlags|=configChanges;

//调用Activity的onPause函数

performPauseActivity(token, finished, r.isPreHoneycomb());

……

try{

//调用AMS的activityPaused函数

ActivityManagerNative.getDefault().activityPaused(token);

}……

}

}


[—>ActivityManagerService.java:activityPaused]


public final void activityPaused(IBinder token){

……

mMainStack.activityPaused(token, false);

}


[—>ActivityStack.java:activityPaused]


final void activityPaused(IBinder token, boolean timeout){

ActivityRecord r=null;

synchronized(mService){

int index=indexOfTokenLocked(token);

if(index>=0){

r=mHistory.get(index);

//从消息队列中撤销PAUSE_TIMEOUT_MSG消息

mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r);

if(mPausingActivity==r){

r.state=ActivityState.PAUSED;//设置ActivityRecord的状态

completePauseLocked();//完成本次Pause操作

}……

}

}


(3)completePauseLocked分析

这部分的代码具体如下:

[—>ActivityStack.java:completePauseLocked]


private final void completePauseLocked(){

ActivityRecord prev=mPausingActivity;

if(prev!=null){

if(prev.finishing){

prev=finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE);

}else if(prev.app!=null){

if(prev.configDestroy){

destroyActivityLocked(prev, true, false);

}else{

//①将刚才被暂停的Activity保存到mStoppingActivities中

mStoppingActivities.add(prev);

if(mStoppingActivities.size()>3){

//如果被暂停的Activity超过3个,则发送IDLE_NOW_MSG消息,该消息最终

//由我们前面介绍的activeIdleInternal处理

scheduleIdleLocked();

}

}

//设置mPausingActivity为null,这是图6-14中②、③分支的分割点

mPausingActivity=null;

}

//②resumeTopActivityLocked将启动目标Activity

if(!mService.isSleeping())resumeTopActivityLocked(prev);

……

}


就本例而言,以上代码还算简单,最后还是通过resumeTopActivityLocked来启动目标Activity。当然,由于之前已经设置了mPausingActivity为null,所以最终会走到图6-14中③的分支。

(4)stopActivityLocked分析

根据前面的介绍,此次目标Activity将走完onCreate、onStart和onResume流程,但是被暂停的Activity才刚走完onPause流程,那么它的onStop什么时候调用呢?答案就在activityIdelInternal中,它将为mStoppingActivities中的成员调用stopActivityLocked函数。

[—>ActivityStack.java:stopActivityLocked]


private final void stopActivityLocked(ActivityRecord r){

if((r.intent.getFlags()&Intent.FLAG_ACTIVITY_NO_HISTORY)!=0

||(r.info.flags&ActivityInfo.FLAG_NO_HISTORY)!=0){

if(!r.finishing){

requestFinishActivityLocked(r, Activity.RESULT_CANCELED, null,

"no-history");

}

}else if(r.app!=null&&r.app.thread!=null){

try{

r.stopped=false;

//设置STOPPING状态,并调用对应的scheduleStopActivity函数

r.state=ActivityState.STOPPING;

r.app.thread.scheduleStopActivity(r, r.visible,

r.configChangeFlags);

}……

}


对应进程的scheduleStopActivity函数将根据visible的情况,向主线程消息循环发送H.STOP_ACTIVITY_HIDE或H.STOP_ACTIVITY_SHOW消息。不论哪种情况,最终都由handleStopActivity来处理。

[—>ActivityThread.java:handleStopActivity]


private void handleStopActivity(IBinder token, boolean show, int configChanges){

ActivityClientRecord r=mActivities.get(token);

r.activity.mConfigChangeFlags|=configChanges;

StopInfo info=new StopInfo();

//调用Activity的onStop函数

performStopActivityInner(r, info, show, true);

……

try{//调用AMS的activityStopped函数

ActivityManagerNative.getDefault().activityStopped(

r.token, r.state, info.thumbnail, info.description);

}

}


虽然AMS没有为stop设置超时消息处理,但是严格来说,还是有超时限制的,只是这个超时处理与activityIdleInternal结合起来了。

(5)startPausingLocked总结

startPausingLocked的流程如图6-16所示。

6.3.3 startActivityLocked分析 - 图3

图 6-16 startPausingActivity流程总结

图6-16比较简单,读者最好结合代码再把流程走一遍,以加深理解。

9.startActivity总结

Activity的启动就介绍到这里。这一路分析下来,相信读者也和笔者一样觉得此行绝不轻松。先回顾一下此次旅程:

行程的起点是am。am是Android中很重要的程序,读者务必要掌握它的用法。我们利用am start命令,发起本次目标Activity的启动请求。

接下来进入ActivityManagerService和ActivityStack这两个核心类。对于启动Activity来说,这段行程又可分细分为两个阶段:第一阶段的主要工作就是根据启动模式和启动标志找到或创建ActivityRecord及对应的TaskRecord;第二阶段工作就是处理Activity启动或切换相关的工作。

然后讨论了AMS直接创建目标进程并运行Activity的流程,其中涉及目标进程的创建,目标进程中Android运行环境的初始化,目标Activity的创建以及onCreate、onStart及onResume等其生命周期中重要函数的调用等相关知识点。

接着又讨论了AMS先暂停当前Activity,然后再创建目标进程并运行Activity的流程。其中牵扯到两个应用进程和AMS的交互,其难度之大可见一斑。

读者在阅读本节时,务必要区分此旅程中两个阶段工作的重点:其一是找到合适的ActivityRecord和TaskRecord;其二是调度相关进程进行Activity切换。在SDK文档中,介绍最为详细的是第一阶段中系统的处理策略,例如启动模式、启动标志的作用等。第二阶段工作其实是与Android组件调度相关的工作。SDK文档只是针对单个Activity进行生命周期方面的介绍。

坦诚地说,这次旅程略过不少逻辑情况。原因有二,一方面受限于精力和篇幅;另一方面是作为调度核心类,和AMS相关的代码及处理逻辑非常复杂,而且其间还夹杂了与WMS的交互逻辑,使复杂度更甚。再者,笔者个人感觉这部分代码肯定谈不上高效、严谨和美观,甚至有些丑陋(在分析它们的过程中,远没有研究Audio、Surface时那种畅快淋漓的感觉)。

此处列出几个供读者深入研究的点:

各种启动模式、启动标志的处理流程。

Configuration发生变化时Activity的处理,以及在Activity中对状态保存及恢复的处理流程。

Activity生命周期各个阶段的转换及相关处理。Android 2.3以后新增的与Fragment的生命周期相关的转换及处理。

建议 在研究代码前,先仔细阅读SDK文档相关内容,以获取必要的感性认识,否则直接看代码很容易迷失方向。

[1]关于Zygote的工作原理,请读者阅读卷I第4章“深入理解Zygote”。