多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
startActivityLocked是startActivityMayWait第二阶段的工作重点,该函数有点长,请读者耐心看代码。 **ActivityStack.java::startActivityLocked** ~~~ final int startActivityLocked(IApplicationThreadcaller, 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函数的说明 ActivityRecordsourceRecord = null; ActivityRecord resultRecord = null; if(resultTo != null) { //本例resultTo为null, } //获取Intent设置的启动标志,它们是和Launch Mode相类似的“小把戏”, //所以,读者务必理解“关于Launch Mode的介绍”一节的知识点 intlaunchFlags = intent.getFlags(); if((launchFlags&Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0 && sourceRecord != null) { ...... /* 前面介绍的Launch Mode和Activity的启动有关,实际上还有一部分标志用于控制 Activity启动结果的通知。有关FLAG_ACTIVITY_FORWARD_RESULT的作用,读者可 参考SDK中的说明。使用这个标签有个前提,即A必须先存在,正如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,esultRecord, resultWho, requestCode, Activity.RESULT_CANCELED, null); } ....... returnerr; } //检查权限 finalint 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对象 ActivityRecordr = 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 voidresumeAppSwitches() { ......//检查调用进程是否有STOP_APP_SWITCHES权限 synchronized(this) { mAppSwitchesAllowedTime = 0; } //注意,系统并不在此函数内启动那些被阻止的Activity } ~~~ 在resumeAppSwitches中只设置mAppSwitchesAllowedTime的值为0,它并不处理在stop和resume这段时间内积攒起的Pending请求,那么这些请求是在何时被处理的呢? - 从前面代码可知,如果在执行resume app switch后,又有新的请求需要处理,则先处理那些pending的请求(调用doPendingActivityLaunchesLocked)。 - 在resumeAppSwitches中并未撤销stopAppSwitches函数中设置的超时消息,所以在处理那条超时消息的过程中,也会处理pending的请求。 在本例中,由于不考虑app switch的情况,那么接下来的工作就是调用startActivityUncheckedLocked函数来处理本次activity的启动请求。此时,我们已经创建了一个ActivityRecord用于保存目标Activity的相关信息。 2. startActivityUncheckedLocked函数分析 startActivityUncheckedLocked函数很长,但是目的比较简单,即为新创建的ActivityRecord找到一个合适的Task。虽然本例最终的结果是创建一个新的Task,但是该函数的处理逻辑却比较复杂。先看第一段分析。 (1) startActivityUncheckedLocked分析之一 **ActivityStack.java::startActivityUncheckedLocked** ~~~ final intstartActivityUncheckedLocked(ActivityRecord r, ActivityRecord sourceRecord, Uri[] grantedUriPermissions, intgrantedMode, boolean onlyIfNeeded, boolean doResume) { //在本例中,sourceRecord为null,onlyIfNeeded为false,doResume为true finalIntent intent = r.intent; final intcallingUid = r.launchedFromUid; intlaunchFlags = 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** ~~~ booleanaddingToTask = 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 { ......//通知错误 returnSTART_CLASS_NOT_FOUND; } //在本例中,肯定需要创建一个Task booleannewTask = false; booleankeepCurTransition = 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; //为该AactivityRecord设置一个新的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); }elseif......//其他处理情况 //授权控制。在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 voidstartActivityLocked(ActivityRecord r, boolean newTask, boolean doResume, boolean keepCurTransition) { final intNH = mHistory.size(); intaddPos = -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函数分析 **ActivityStack.java::resumeTopActivityLocked** ~~~ finalboolean resumeTopActivityLocked(ActivityRecord prev) { //从mHistory中找到第一个需要启动的ActivityRecord ActivityRecord next = topRunningActivityLocked(null); finalboolean userLeaving = mUserLeaving; mUserLeaving = false; if (next== null) { //如果mHistory中没有要启动的Activity,则启动Home if(mMainStack) returnmService.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章“readLPw的‘佐料’” //一节的说明 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); } returntrue; } ~~~ resumeTopActivityLocked函数中有两个非常重要的关键点: - 如果mResumedActivity不为空,则需要先暂停(pause)这个Activity。由代码中的注释可知,mResumedActivity代表上一次启动的(即当前正显示的)Activity。现在要启动一个新的Activity,须先停止当前Activity,这部分工作由startPausingLocked函数完成。 - 那么,mResumedActivity什么时候为空呢?当然是在启动全系统第一个Activity时,即启动Home界面的时候。除此之外,该值都不会为空。 先分析第二个关键点,即mResumedActivity为null的情况选择分析此种情况的原因是:如果先分析startPausingLocked,则后续分析会牵扯三个进程,即当前Activity所在进程、AMS所在进程及目标进程,分析的难度相当大。 好了,继续我们的分析。resumeTopActivityLocked最后将调用另外一个startSpecificActivityLocked,该函数将真正创建一个应用进程。 (1) startSpecificActivityLocked分析 **ActivityStack.java::startSpecificActivityLocked** ~~~ private final voidstartSpecificActivityLocked(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(StringprocessName, ApplicationInfo info, boolean knownToBeDead, int intentFlags, String hostingType, ComponentName hostingName, boolean allowWhileBooting) { //根据processName和uid寻找是否已经存在ProcessRecord ProcessRecordapp = getProcessRecordLocked(processName, info.uid); if (app!= null && app.pid > 0) { ......//处理相关情况 } StringhostingNameStr = 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启动了一个“BadProcess”,然后该Process崩溃,结果弹出一个警告框,那么用户就会觉得很奇怪:“为什么突然弹出一个框?”因此,此处将禁止后台Task启动“Bad Process”。如果用户主动选择启动(例如单击一个按钮),则不能禁止该操作,并且要把应用进程从mBadProcesses中移除,以给它们“重新做人”的机会。当然,要是该应用每次启动时都会崩溃,而且用户不停地去启动,那该用户可能是位测试工作者。 * * * * * **提示**:这其实是一种安全机制,防止不健全的程序不断启动可能会崩溃的组件,但是这种机制并不限制用户的行为。 * * * * * 下面来看第二个关键点,即另一个startProcessLocked函数,其代码如下: **ActivityManagerService.java::startProcessLocked** ~~~ private final voidstartProcessLocked(ProcessRecord app, String hostingType, StringhostingNameStr) { 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 { intuid = app.info.uid; int[] gids = null; try {//从PKMS中查询该进程所属的gid gids = mContext.getPackageManager().getPackageGids( app.info.packageName); }...... ......//工厂测试 intdebugFlags = 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以派生一个应用进程[^①],读者仔细研究所发消息的内容,大概会发现此处并未设置和Activity相关的信息,也就是说,该进程启动后,将完全不知道自己要干什么,怎么办?下面就此进行分析。 4. startActivity分析之半程总结 很抱歉,我们现在还处于startActivity分析之旅的中间点,即使越过了很多险滩恶途,一路走来还是发觉有点艰难。此处用图6-14来记录半程中的各个关键点。 :-: ![](http://img.blog.csdn.net/20150803123218950?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 图6-14 startActivity半程总结 图6-14列出了针对本例的调用顺序,其中对每个函数的大体功能也做了简单描述。 * * * * * **注意**:图6-14中的调用顺序及功能说明只是针对本例而言的。读者以后可结合具体情况再深入研究其中的内容。 * * * * * 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 newRuntimeException("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(newComponentCallbacks2() {.......//添加回调函数}); } ~~~ 我们知道,AMS创建一个应用进程后,会设置一个超时时间(一般是10秒)。如果超过这个时间,应用进程还没有和AMS交互,则断定该进程创建失败。所以,应用进程启动后,需要尽快和AMS交互,即调用AMS的attachApplication函数。在该函数内部将调用attachApplicationLocked,所以此处直接分析attachApplicationLocked,先看其第一阶段的工作。 (1) attachApplicationLocked分析之一 **ActivityManagerService.java::attachApplicationLocked** ~~~ private final booleanattachApplicationLocked(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大于零,且不是SystemServer进程,则 //Quietly(即不打印任何输出)”杀死”process Process.killProcessQuiet(pid); else{ //调用ApplicationThread的scheduleExit函数。应用进程完成处理工作后 //将退出运行 //为何不像上面一样直接杀死它呢?可查阅linux pid相关的知识并自行解答 thread.scheduleExit(); } returnfalse; } /* 判断app的thread是否为空,如果不为空,则表示该ProcessRecord对象还未和一个 应用进程绑定。注意,app是根据pid查找到的,如果旧进程没有被杀死,系统则不会重用 该pid。为什么此处会出现ProcessRecord thread不为空的情况呢?见下面代码的注释说明 */ if(app.thread != null) handleAppDiedLocked(app, true, true); StringprocessName = 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第一阶段的工作比较简单: - 设置代表该应用进程的ProcessRecrod对象的一些成员变量,例如用于和应用进程交互的thread对象、进程调度优先级及oom_adj的值等。 - 从消息队列中撤销PROC_START_TIMEOUT_MSG。 至此,该进程启动成功,但是这一阶段的工作仅针对进程本身(如设置调度优先级,oom_adj等),还没有涉及和Activity启动相关的内容,这部分工作将在第二阶段完成。 (2) attachApplicationLocked分析之二 **ActivityManagerService.java::attachApplicationLocked** ~~~ ...... //SystemServer早就启动完毕,所以normalMode为true booleannormalMode = mProcessesReady || isAllowedWhileBooting(app.info); /* 我们在6.2.3的标题1中分析过generateApplicationProvidersLocked函数, 在该函数内部将查询(根据进程名,uid确定)PKMS以获取需运行在该进程中的ContentProvider */ Listproviders = 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( StringprocessName,//进程名,一般是package名 ApplicationInfo appInfo,//该进程对应的ApplicationInfo List<ProviderInfo> providers,//在该APackage中声明的Provider信息 ComponentName instrumentationName,//和instrumentation有关 //下面3个参数和性能统计有关 StringprofileFile, ParcelFileDescriptor profileFd, boolean autoStopProfiler, //这两个和Instrumentation有关,在本例中,这几个参数暂时都没有作用 Bundle instrumentationArgs, IInstrumentationWatcherinstrumentationWatcher, intdebugMode,//调试模式 boolean isRestrictedBackupMode, boolean persistent,//该进程是否是persist Configuration config,//当前的配置信息,如屏幕大小和语言等 CompatibilityInfocompatInfo,//兼容信息 //AMS将常用的Service信息传递给应用进程,目前传递的Service信息只有PKMS、 //WMS及AlarmManagerService。读者可参看AMS getCommonServicesLocked函数 Map<String,IBinder> services, BundlecoreSettings)//核心配置参数,目前仅有“long_press”值 ~~~ 对bindApplication的原型分析就到此为止,再来看attachApplicationLocked最后一阶段的工作。 (3) attachApplicationLocked分析之三 **ActivityManagerService.java::attachApplicationLocked** ~~~ booleanbadApp = false; booleandidSomething = 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); returnfalse; } /* 调整进程的oom_adj值。didSomething表示在以上流程中是否启动了Acivity或其他组件。 如果启动了任一组件,则didSomething为true。读者以后会知道,这里的启动只是向 应用进程发出对应的指令,客户端进程是否成功处理还是未知数。基于这种考虑,所以此处不宜 马上调节进程的oom_adj。 读者可简单地把oom_adj看做一种优先级。如果一个应用进程没有运行任何组件,那么当内存 出现不足时,该进程是最先被系统杀死的。反之,如果一个进程运行的组件越多,那么它就越不易被 系统杀死以回收内存。updateOomAdjLocked就是根据该进程中组件的情况对应调节进程的 oom_adj值的。 */ if(!didSomething) updateOomAdjLocked(); returntrue; } ~~~ 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(AppBindDatadata) { 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; //设置进程名。从此,之前那个默默无名的进程终于有了自己的名字 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,否则使用 //serializedexecutor。这些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版,则需要记录log信息到dropbox的日志记录 if((data.appInfo.flags & (ApplicationInfo.FLAG_SYSTEM | ApplicationInfo.FLAG_UPDATED_SYSTEM_APP)) != 0) { StrictMode.conditionallyEnableDebugLogging(); } /* 如目标SDK版本大于9,则不允许在主线程使用网络操作(如Socketconnect等),否则抛出 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代理信息 ProxyPropertiesproxyProperties = 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对象 Applicationapp = 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处理。handleBindApplication将为该进程设置进程名,初始化一些策略和参数信息等。另外,它还创建一个Application对象。同时,如果该Application声明了ContentProvider,还需要为该进程安装ContentProvider。 * * * * * **提示**:这个流程有点类似生孩子,一般生之前需要到医院去登记,生完后又需去注册户口,如此这般,这个孩子才会在社会有合法的身份。 * * * * * 6. ActivityStack realStartActivityLocked分析 如前所述,AMS调用完bindApplication后,将通过realStartActivityLocked启动Activity。在此之前,要创建完应用进程并初始化Android运行环境(除此之外,连ContentProvider都安装好了)。 **ActivityStack.java::realStartActivityLocked** ~~~ //注意,在本例中该函数的最后两个参数的值都为true final booleanrealStartActivityLocked(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(); returntrue; } ~~~ 在以上代码中有两个关键函数,分别是:scheduleLaunchActivity和completeResumeLocked。其中,scheduleLaunchActivity用于和应用进程交互,通知它启动目标Activity。而completeResumeLocked将继续AMS的处理流程。先来看第一个关键函数。 (1) scheduleLaunchActivity函数分析 **ActivityThread.java::scheduleLaunchActivity** ~~~ public final void scheduleLaunchActivity(Intentintent, IBinder token, int ident, ActivityInfo info, Configuration curConfig,CompatibilityInfo compatInfo, Bundlestate, List<ResultInfo> pendingResults, List<Intent> pendingNewIntents, boolean notResumed, booleanisForward, StringprofileName, ParcelFileDescriptor profileFd, booleanautoStopProfiler) { 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 voidhandleLaunchActivity(ActivityClientRecord r, Intent customIntent){ unscheduleGcIdler(); if (r.profileFd != null) {......//略去} handleConfigurationChanged(null, null); /* ①创建Activity,通过Java反射机制创建目标Activity,将在内部完成Activity生命周期 的前两步,即调用其onCreate和onStart函数。至此,我们的目标com.dfp.test.TestActivity 创建完毕 */ Activitya = performLaunchActivity(r, customIntent); if (a !=null) { r.createdConfig = new Configuration(mConfiguration); BundleoldState = 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, booleanisForward) { unscheduleGcIdler(); //内部调用目标Activity的onResume函数 ActivityClientRecord r = performResumeActivity(token, clearHide); if (r !=null) { finalActivity 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{ publicfinal boolean queueIdle() { ActivityClientRecord a = mNewActivities; booleanstopProfiling = 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(); returnfalse; }// queueIdle函数结束 } ~~~ 由以上代码可知,Idler将为那些已经完成onResume的Activity调用AMS的activityIdle函数。该函数是Activity成功创建并启动的流程中与AMS交互的最后一步。虽然对应用进程来说,Idler处理的优先级最低,但AMS似乎不这么认为,因为它还设置了超时等待,以处理应用进程没有及时调用activityIdle的情况。这个超时等待即由realStartActivityLocked中最后一个关键点completeResumeLocked函数设置。 (2) completeResumeLocked函数分析 **ActivityStack.java::completeResumeLocked** ~~~ private final voidcompleteResumeLocked(ActivityRecord next) { next.idle = false; next.results = null; next.newIntents = null; //发送一个超时处理消息,默认为10秒。IDLE_TIMEOUT_MSG就是针对acitivityIdle函数的 Messagemsg = 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(IBindertoken, 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; booleanbooting = false; booleanenableScreen = 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(); ...... returnres; } ~~~ 在activityIdleInternal中有一个非常重要的关键点,即处理那些因为本次Activity启动而被暂停的Activity。有两种情况需考虑: - 如果被暂停的Activity处于finishing状态(例如Activity在其onStop中调用了finish函数),则调用finishCurrentActivityLocked。 - 否则,要调用stopActivityLocked处理暂停的Activity。 此处涉及除AMS和目标进程外的第三个进程,即被切换到后台的那个进程。不过至此,我们的目标Activity终于正式登上了历史舞台。 * * * * * **提示**:本例的分析结束了吗?没有。因为am设置了-W选项,所以其实我们还在startActivityAndWait函数中等待结果。ActivityStack中有两个函数能够触发AMS notifyAll,一个是reportActivityLaunchedLocked,另一个是reportActivityVisibleLocked。前面介绍的activityInternal函数只在fromTimeout为true时才会调用reportActivityLaunchedLocked,而本例中fromTimeout为false,如何是好?该问题的解答非常复杂,姑且先一语带过:当Activity显示出来时,其在AMS中对应ActivityRecord对象的windowVisible函数将被调用,其内部会触发reportActivityLaunchedLocked函数,这样我们的startActivityAndWait才能被唤醒。 * * * * * 7. startActivity分析之后半程总结 总结startActivity后半部分的流程,主要涉及目标进程和AMS的交互,如图6-15所示。 :-: ![](http://img.blog.csdn.net/20150803123243623?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 图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(booleanuserLeaving, 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, booleantimeout) { 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); } elseif (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** ~~~ privatefinal 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"); } } elseif (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; StopInfoinfo = 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所示。 :-: ![](http://img.blog.csdn.net/20150803123302838?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 图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先pause当前Activity,然后再创建目标进程并运行Activity的流程。其中牵扯到两个应用进程和AMS的交互,其难度之大可见一斑。 读者在阅读本节时,务必要区分此旅程中两个阶段工作的重点:其一是找到合适的ActivityRecord和TaskRecord;其二是调度相关进程进行Activity切换。在SDK文档中,介绍最为详细的是第一阶段中系统的处理策略,例如启动模式、启动标志的作用等。第二阶段工作其实是与Android组件调度相关的工作。SDK文档只是针对单个Activity进行生命周期方面的介绍。 坦诚地说,这次旅程略过不少逻辑情况。原因有二,一方面受限于精力和篇幅,另方面是作为调度核心类,和AMS相关的代码及处理逻辑非常复杂,而且其间还夹杂了与WMS的交互逻辑,使复杂度更甚。再者,笔者个人感觉这部分代码绝谈不上高效、严谨和美观,甚至有些丑陋(在分析它们的过程中,远没有研究Audio、Surface时那种畅快淋漓的感觉)。 此处列出几个供读者深入研究的点: - 各种启动模式、启动标志的处理流程。 - Configuration发生变化时Activity的处理,以及在Activity中对状态保存及恢复的处理流程。 - Activity生命周期各个阶段的转换及相关处理。Android 2.3以后新增的与Fragment的生命周期相关的转换及处理。 >[info]**建议**:在研究代码前,先仔细阅读SDK文档相关内容,以获取必要的感性认识,否则直接看代码很容易迷失方向。 [^①]:关于Zygote的工作原理,请读者阅读卷I第4章“深入理解Zygote”