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-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-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-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”。