💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
startActivityAndWait函数有很多参数,先来认识一下它们。 **ActiivtyManagerService.java::startActivityAndWait原型** ~~~ publicfinal WaitResult startActivityAndWait( /* 在绝大多数情况下,一个Acitivity的启动是由一个应用进程发起的,IApplicationThread是 应用进程和AMS交互的通道,也可算是调用进程的标示,在本例中,AM并非一个应用进程,所以 传递的caller为null */ IApplicationThread caller, //Intent及resolvedType,在本例中,resolvedType为null Intentintent, String resolvedType, //下面两个参数和授权有关,读者可参考第3章对CopyboardService分析中介绍的授权知识 Uri[] grantedUriPermissions,//在本例中为null intgrantedMode,//在本例中为0 IBinder resultTo,//在本例中为null,用于接收startActivityForResult的结果 StringresultWho,//在本例中为null //在本例中为0,该值的具体意义由调用者解释。如果该值大于等于0,则AMS内部保存该值, //并通过onActivityResult返回给调用者 int requestCode, boolean onlyIfNeeded,//本例为false boolean debug,//是否调试目标进程 //下面3个参数和性能统计有关 StringprofileFile, ParcelFileDescriptor profileFd, booleanautoStopProfiler) ~~~ 关于以上代码中一些参数的具体作用,以后碰到时会再作分析。建议读者先阅读SDK文档中关于Activity类定义的几个函数,如startActivity、startActivityForResult及onActivityResult等。 startActivityAndWait的代码如下: **ActivityManagerService.java::startActivityAndWait** ~~~ publicfinal WaitResult startActivityAndWait(IApplicationThread caller, Intent intent, String resolvedType, Uri[]grantedUriPermissions, int grantedMode, IBinder resultTo,StringresultWho, int requestCode, booleanonlyIfNeeded, boolean debug,String profileFile, ParcelFileDescriptor profileFd, booleanautoStopProfiler) { //创建WaitResult对象用于存储处理结果 WaitResult res = new WaitResult(); //mMainStack为ActivityStack类型,调用它的startActivityMayWait函数 mMainStack.startActivityMayWait(caller, -1, intent, resolvedType, grantedUriPermissions, grantedMode, resultTo, resultWho, requestCode, onlyIfNeeded, debug, profileFile, profileFd, autoStopProfiler, res, null);//最后一个参数为Configuration, //在本例中为null returnres; } ~~~ mMainStack为AMS的成员变量,类型为ActivityStack,该类是Activity调度的核心角色。正式分析它之前,有必要先介绍一下相关的基础知识。 1. Task、Back Stack、ActivityStack及Launch mode (1) 关于Task及Back Stack的介绍 先来看图6-10。 :-: ![](http://img.blog.csdn.net/20150803122911609?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 图6-10 用户想干什么 图6-10列出了用户在Android系统上想干的三件事情,分别用A、B、C表示,将每一件事情称为一个Task。一个Task还可细分为多个子步骤,即Activity。 * * * * * **提示**:为什么叫Activity?读者可参考Merrian-Webster词典对Activity的解释[^①]:“an organizational unit forperforming a specific function”,也就是说,它是一个有组织的单元,用于完成某项指定功能。 * * * * * 由图6-10可知,A、B两个Task使用了不同的Activity来完成相应的任务。注意,A、B这两个Task的Activity之间没有复用。 再来看C这个Task,它可细分为4个Activity,其中有两个Activity分别使用了A Task的A1、B Task的B2。C Task为什么不新建自己的Activity,反而要用其他Task的呢?这是因为用户想做的事情(即Task)可以完全不同,但是当细分Task为Activity时,就可能出现Activity功能类似的情况。既然A1、B2已能满足要求,为何还要重复“发明轮子”呢?另外,通过重用Activity,也可为用户提供一致的界面和体验。 了解Android设计理念后,我们来看看Android是如何组织Task及它所包含的Activity的。此处有一个简单的例子,如图6-11所示。 :-: ![](http://img.blog.csdn.net/20150803122942720?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 图6-11 Task及Back Stack示例 由图6-11可知: - 本例中的Task包含4个Activity。用户可单击按钮跳转到下一个Activity。同时,通过返回键可回到上一个Activity。 - 虚线下方是这些Activity的组织方式。Android采用了Stack的方法管理这3个Activity。例如在A1启动A2后,A2入栈成为栈顶成员,A1成为栈底成员,而界面显示的是栈顶成员的内容。当按返回键时,A3出栈,这时候A2成为栈顶,界面显示也相应变成了A2。 以上是一个Task的情况。那么,多个Task又会是何种情况呢?如图6-12所示。 :-: ![](http://img.blog.csdn.net/20150803123111793?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 图6-12 多个Task的情况 由图6-12可知:对多Task的情况来说,系统只支持一个处于前台的Task,即用户当前看到的Activity所属的Task,其余的Task均处于后台,这些后台Task内部的Activity保持顺序不变。用户可以一次将整个Task挪到后台或者置为前台。 * * * * * **提示**:用过Android手机的读者应该知道,长按Home键,系统会弹出近期Task列表,使用户能快速在多个Task间切换。 * * * * * 以上内容从抽象角度介绍了什么是Task,以及Android如何分解Task和管理Activity,那么在实际代码中,是如何考虑并设计的呢? (2) 关于ActivityStack的介绍 通过上述分析,我们对Android的设计有了一定了解,那么如何用代码来实现这一设计呢?此处有两点需要考虑: - Task内部Activity的组织方式。由图6-11可知,Android通过先入后出的方式来组织Activity。数据结构中的Stack即以这种方式工作。 - 多个Task的组织及管理方式。 Android设计了一个ActivityStack类来负责上述工作,它的组成如图6-13所示。 :-: ![](http://img.blog.csdn.net/20150803123156782?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center) 图6-13 ActivityStack及相关成员 由图6-13可知: - Activity由ActivityRecord表示,Task由TaskRecord表示。ActivityRecord的task成员指向该Activity所在的Task。state变量用于表示该Activity所处的状态(包括INITIALIZING、RESUMED、PAUSED等状态)。 - ActivityStack用mHistory这个ArrayList保存ActivityRecord。令人大跌眼镜的是,该mHistory保存了系统中所有Task的ActivityRecord,而不是针对某个Task进行保存。 - ActivityStack的mMainStack成员比较有意思,它代表此ActivityStack是否为主ActivityStack。有主必然有从,但是目前系统中只有一个ActivityStack,并且它的mMainStack为true。从ActivityStack的命名可推测,Android在开发之初也想用ActivityStack来管理单个Task中的ActivityRecord(在ActivityStack.java的注释中说过,该类为“State and management of asingle stack of activities”),但不知何故,在现在的代码实现将所有Task的ActivityRecord都放到mHistory中了,并且依然保留mMainStack。 - ActivityStack中没有成员用于保存TaskRecord。 由上述内容可知,ActivityStack采用数组的方式保存所有Task的ActivityRecord,并且没有成员保存TaskRecord。这种实现方式有优点亦有缺点: - 优点是少了TaskRecord一级的管理,直接以ActivityRecord为管理单元。这种做法能降低管理方面的开销。 - 缺点是弱化了Task的概念,结构不够清晰。 下面来看ActivityStack中几个常用的搜索ActivityRecord的函数,代码如下: **ActivityStack.java::topRunningActivityLocked** ~~~ /* topRunningActivityLocked: 找到栈中第一个与notTop不同的,并且不处于finishing状态的ActivityRecord。当notTop为 null时,该函数即返回栈中第一个需要显示的ActivityRecord。提醒读者,栈的出入口只能是栈顶。 虽然mHistory是一个数组,但是查找均从数组末端开始,所以其行为也粗略符合Stack的定义 */ final ActivityRecordtopRunningActivityLocked(ActivityRecord notTop) { int i =mHistory.size()-1; while (i>= 0) { ActivityRecord r = mHistory.get(i); if (!r.finishing && r != notTop) return r; i--; } returnnull; } ~~~ 类似的函数还有: **ActivityStack.java::topRunningNonDelayedActivityLocked** ~~~ /* topRunningNonDelayedActivityLocked 与topRunningActivityLocked类似,但ActivityRecord要求增加一项,即delayeResume为 false */ final ActivityRecordtopRunningNonDelayedActivityLocked(ActivityRecord notTop) { int i =mHistory.size()-1; while(i >= 0) { ActivityRecord r = mHistory.get(i); //delayedResume变量控制是否暂缓resume Activity if (!r.finishing && !r.delayedResume&& r != notTop) return r; i--; } returnnull; } ~~~ ActivityStack还提供findActivityLocked函数以根据Intent及ActivityInfo来查找匹配的ActivityRecord,同样,查找也是从mHistory尾端开始,相关代码如下: **ActivityStack.java::findActivityLocked** ~~~ private ActivityRecord findActivityLocked(Intentintent, ActivityInfo info) { ComponentName cls = intent.getComponent(); if(info.targetActivity != null) cls= new ComponentName(info.packageName, info.targetActivity); final intN = mHistory.size(); for (inti=(N-1); i>=0; i--) { ActivityRecord r = mHistory.get(i); if (!r.finishing) if (r.intent.getComponent().equals(cls))return r; } return null; } ~~~ 另一个findTaskLocked函数的返回值是ActivityRecord,其代码如下: **ActivityStack.java::findTaskLocked** ~~~ private ActivityRecord findTaskLocked(Intentintent, ActivityInfo info) { ComponentName cls = intent.getComponent(); if(info.targetActivity != null) cls= new ComponentName(info.packageName, info.targetActivity); TaskRecord cp = null; final intN = mHistory.size(); for (inti=(N-1); i>=0; i--) { ActivityRecord r = mHistory.get(i); //r.task!=cp,表示不搜索属于同一个Task的ActivityRecord if(!r.finishing && r.task != cp && r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE) { cp = r.task; //如果Task的affinity相同,则返回这条ActivityRecord if(r.task.affinity != null) { if(r.task.affinity.equals(info.taskAffinity)) return r; }else if (r.task.intent != null &&r.task.intent.getComponent().equals(cls)) { //如果Task Intent的ComponentName相同 return r; }else if (r.task.affinityIntent != null &&r.task.affinityIntent.getComponent().equals(cls)) { //Task affinityIntent考虑 return r; }//if (r.task.affinity != null)判断结束 }//if(!r.finishing && r.task != cp......)判断结束 }//for循环结束 returnnull; } ~~~ 其实,findTaskLocked是根据mHistory中ActivityRecord所属的Task的情况来进行相应的查找工作。 以上这4个函数均是ActivityStack中常用的函数,如果不需要逐项(case by case)地研究AMS,那么读者仅需了解这几个函数的作用即可。 (3) 关于Launch Mode的介绍 Launch Mode用于描述Activity的启动模式,目前一共有4种模式,分别是standard、singleTop、singleTask和singleInstance。初看它们,较难理解,实际上不过是Android玩的一个“小把戏“而已。启动模式就是用于控制Activity和Task关系的。 - standard:一个Task中可以有多个相同类型的Activity。注意,此处是相同类型的Activity,而不是同一个Activity对象。例如在Task中有A、B、C、D4个Activity,如果再启动A类Activity, Task就会变成A、B、C、D、A。最后一个A和第一个A是同一类型,却并非同一对象。另外,多个Task中也可以有同类型的Activity。 - singleTop:当某Task中有A、B、C、D4个Activity时,如果D想再启动一个D类型的Activity,那么Task将是什么样子呢?在singleTop模式下,Task中仍然是A、B、C、D,只不过D的onNewIntent函数将被调用以处理这个新Intent,而在standard模式下,则Task将变成A、B、C、D、D,最后的D为新创建的D类型Activity对象。在singleTop这种模式下,只有目标Acitivity当前正好在栈顶时才有效,例如只有处于栈顶的D启动时才有用,如果D启动不处于栈顶的A、B、C等,则无效。 - singleTask:在这种启动模式下,该Activity只存在一个实例,并且将和一个Task绑定。当需要此Activity时,系统会以onNewIntent方式启动它,而不会新建Task和Activity。注意,该Activity虽只有一个实例,但是在Task中除了它之外,还可以有其他的Activity。 - singleInstance:它是singleTask的加强版,即一个Task只能有这么一个设置了singleInstance的Activity,不能再有别的Activity。而在singleTask模式中,Task还可以有其他的Activity。 * * * * * **注意**,Android建议一般的应用开发者不要轻易使用最后两种启动模式。因为这些模式虽然名意上为Launch Mode,但是它们也会影响Activity出栈的顺序,导致用户按返回键返回时导致不一致的用户体验。 * * * * * 除了启动模式外,Android还有其他一些标志用于控制Activity及Task之间的关系。这里只列举一二,详细信息请参阅SDK文档中Intent的相关说明。 - FLAG_ACTIVITY_NEW_TASK:将目标Activity放到一个新的Task中。 - FLAG_ACTIVITY_CLEAR_TASK:当启动一个Activity时,先把和目标Activity有关联的Task“干掉“,然后启动一个新的Task,并把目标Activity放到新的Task中。该标志必须和FLAG_ACTIVITY_NEW_TASK标志一起使用。 - FLAG_ACTIVITY_CLEAR_TOP:当启动一个不处于栈顶的Activity时候,先把排在它前面的Activity“干掉”。例如Task有A、B、C、D4个Activity,要要启动B,直接把C、D“干掉”,而不是新建一个B。 * * * * * **提示**:这些启动模式和标志,在笔者看来很像洗扑克牌时的手法,因此可以称之为小把戏。虽是小把戏,但是相关代码的逻辑及分支却异常繁杂,我们应从更高的角度来看待它们。 * * * * * 介绍完上面的知识后,下面来分析ActivityStack的startActivityMayWait函数。 2. ActivityStack的startActivityMayWait函数分析 startActivityMayWait函数的目标是启动com.dfp.test.TestActivity,假设系统之前没有启动过该Activity,本例最终的结果将是: - 由于在am中设置了FLAG_ACTIVITY_NEW_TASK标志,因此除了会创建一个新的ActivityRecord外,还会新创建一个TaskRecord。 - 还需要启动一个新的应用进程以加载并运行com.dfp.test.TestActivity的一个实例。 - 如果TestActivity不是Home,还需要停止当前正在显示的Activity。 好了,将这个函数分三部分进行介绍,先来分析第一部分。 (1) startActivityMayWait分析之一 **ActivityStack.java::startActivityMayWait** ~~~ final int startActivityMayWait(IApplicationThreadcaller, int callingUid, Intentintent, String resolvedType, Uri[] grantedUriPermissions, intgrantedMode, IBinder resultTo, StringresultWho, int requestCode, boolean onlyIfNeeded, booleandebug, String profileFile, ParcelFileDescriptor profileFd, booleanautoStopProfiler, WaitResult outResult, Configuration config) { ...... //在本例中,已经指明了Component,这样可省去为Intent匹配搜索之苦 booleancomponentSpecified = intent.getComponent() != null; //创建一个新的Intent,防止客户传入的Intent被修改 intent =new Intent(intent); //查询满足条件的ActivityInfo,在resolveActivity内部和PKMS交互,读者不妨自己 //尝试分析该函数 ActivityInfoaInfo = resolveActivity(intent, resolvedType, debug, profileFile, profileFd,autoStopProfiler); synchronized(mService) { int callingPid; if (callingUid >= 0) { callingPid= -1; } else if (caller == null) {//本例中,caller为null callingPid= Binder.getCallingPid();//取出调用进程的Pid //取出调用进程的Uid。在本例中,调用进程是am,它由shell启动 callingUid= Binder.getCallingUid(); } else { callingPid= callingUid = -1; }// if (callingUid >= 0)判断结束 //在本例中config为null mConfigWillChange= config != null && mService.mConfiguration.diff(config) != 0; finallong origId = Binder.clearCallingIdentity(); if (mMainStack && aInfo != null&& (aInfo.applicationInfo.flags& ApplicationInfo.FLAG_CANT_SAVE_STATE) != 0){ /* AndroidManifest.xml中的Application标签可以声明一个cantSaveState 属性,设置了该属性的Application将不享受系统提供的状态保存/恢复功能。 当一个Application退到后台时,系统会为它保存状态,当调度其到前台运行时, 将恢复它之前的状态,以保证用户体验的连续性。声明了该属性的Application被称为 “heavy weight process”。可惜系统目前不支持该属性,因为PackageParser 将不解析该属性。详情请见PackageParser.java parseApplication函数 */ } ......//待续 ~~~ startActivityMayWait第一阶段的工作内容相对较简单: - 首先需要通过PKMS查找匹配该Intent的ActivityInfo。 - 处理FLAG_CANT_SAVE_STATE的情况,但系统目前不支持此情况。 - 另外,获取调用者的pid和uid。由于本例的caller为null,故所得到的pid和uid均为am所在进程的uid和pid。 下面介绍startActivityMayWait第二阶段的工作。 (2) startActivityMayWait分析之二 **ActivityStack.java::startActivityMayWait** ~~~ //调用此函数启动Activity,将返回值保存到res int res = startActivityLocked(caller, intent,resolvedType, grantedUriPermissions, grantedMode, aInfo, resultTo, resultWho, requestCode, callingPid, callingUid, onlyIfNeeded, componentSpecified, null); //如果configuration发生变化,则调用AMS的updateConfigurationLocked //进行处理。关于这部分内容,读者学完本章后可自行分析 if(mConfigWillChange && mMainStack) { mService.enforceCallingPermission( android.Manifest.permission.CHANGE_CONFIGURATION, "updateConfiguration()"); mConfigWillChange= false; mService.updateConfigurationLocked(config,null, false); } ~~~ 此处,启动Activity的核心函数是startActivityLocked,该函数异常复杂,将用一节专门分析。下面先继续分析startActivityMayWait第三阶段的工作。 (3) startActivityMayWait分析之三 **ActivityStack.java::startActivityMayWait** ~~~ if(outResult != null) { outResult.result = res;//设置启动结果 if(res == IActivityManager.START_SUCCESS) { //将该结果加到mWaitingActivityLaunched中保存 mWaitingActivityLaunched.add(outResult); do { try { mService.wait();//等待启动结果 } } while (!outResult.timeout && outResult.who == null); }else if (res == IActivityManager.START_TASK_TO_FRONT) { ......//处理START_TASK_TO_FRONT结果,读者可自行分析 } }//if(outResult!= null)结束 return res; } } ~~~ 第三阶段的工作就是根据返回值做一些处理,那么res返回成功(即res== IActivityManager.START_SUCCESS的时候)后为何还需要等待呢? 这是因为目标Activity要运行在一个新的应用进程中,就必须等待那个应用进程正常启动并处理相关请求。注意,只有am设置了-W选项,才会进入wait这一状态。 [^①]:http://www.merriam-webster.com/dictionary/activity中第六条解释。