本文已参与[新人创作礼]活动,一起开启掘金之路。
之前也写过一些应用冷启动的内容,对着源码走了一遍流程(Activity 启动流程和 UML 时序图)。但总感觉效果不好,源码扯的有点多,对理解 AMS 却没有太大帮助,所以想再梳理一下,源码少点,流程也粗略些,在个别细节处多停留,记录一下自己当时的困惑。 本来想一篇文章说完的,最后发现东西还是有点多了,自身实力搞不定,所以还是拆开,这一篇就当作系列一:主要看看桌面 Activity 是怎么调用到系统进程的。
聊应用冷启动,还是以从桌面点击应用图标冷启动为例。主要过程包括:
- 桌面进程向系统发起启动另一个应用中的 Activity 的请求;
- 系统对发送过来的 intent 参数进行解析,选择启动模式,并校验启动权限、参数合法性。如果校验通过,就通知桌面 pause 当前的 Activity;
- 桌面收到系统暂停当前 Activity 的要求,执行相关过程,并告知系统已暂停;
- 继续执行目标 Activity 启动的过程,首先进行任务栈相关的处理。因为是冷启动,目标 Activity 所在进程不存在,所以首先要通过 AMS 创建 app2 进程,绑定资源和上下文环境;
- 创建并启动目标 Activity。
大概流程就是 Activity--->Instrumentation-->ATMS,我们分步看看。
1 Activity 组合了一个 Instrumentation 对象
在 Activity.java 中,有一个 Instrumentation 的类对象。这个东西翻译为仪表盘,作用是监视 app 进程与系统间的交互。每个 Activity 都持有一个 Instrumentation 对象的引用,但一个应用进程只有一个 Instrumentation 对象,这个特性是如何做到的我们暂时不深入研究。下图第一行的注释说到 mInstrumentation 的初始化时机是在构造完成但 onCreate 方法回调之前。
我们在启动 Activity 的过程中会在 startActivityForResult() 方法中执行 mInstrumentation.execStartActivity,很自然的到了 Instrumentation.java 中。
5399 public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
5400 @Nullable Bundle options) {
5401 if (mParent == null) {
5402 options = transferSpringboardActivityOptions(options);
5403 Instrumentation.ActivityResult ar =
5404 mInstrumentation.execStartActivity(
5405 this, mMainThread.getApplicationThread(), mToken, this,
5406 intent, requestCode, options);
留意一下:Instrumentation.ActivityResult,ActivityResult是 Instrumentation 的静态内部类,用来描述 Activity 的执行结果,返回给原 Activity。
1629 /**
1630 * Description of a Activity execution result to return to the original
1631 * activity.
1632 */
2 Instrumentation 通过 AIDL 调用到 ATMS
下面就一起看看这个工具类Instrumentation.java,官方对这个类作用的介绍:监视应用与系统间的所有交互。
接着上文的分析,看看 execStartActivity 方法。
1709 @UnsupportedAppUsage
1710 public ActivityResult execStartActivity(
1711 Context who, IBinder contextThread, IBinder token, Activity target,
1712 Intent intent, int requestCode, Bundle options) {
1743 int result = ActivityTaskManager.getService().startActivity(whoThread,
1744 who.getOpPackageName(), who.getAttributionTag(), intent,
1745 intent.resolveTypeIfNeeded(who.getContentResolver()), token,
1746 target != null ? target.mEmbeddedID : null, requestCode, 0, null, options);
显然重点在 ActivityTaskManager.getService()上了,一起看看吧。
这是一个获取 binder 服务的标准动作啊,现在我们看看 ACTIVITY_TASK_SERVICE 对应哪个类,通过搜索确定到了 ActivityTaskManagerService.java。其实代码跟到这,我们只需要明白一件事:Instrumentation 是通过 AIDL 获取到了 ActivityTaskManagerService 的对象,实现了跨进程调用 startActivity,而 ActivityTaskManager 封装了 binder 服务的获取。至于 AIDL 服务获取的流程,有兴趣的可以看下这篇文章。AIDL 服务的发布 publishBinderService 和 获取 getService。
桌面 Activity 请求启动 Activity 的关键流程说的差不多了。最后再简单看下 execStartActivity 的参数。
1685 * @param who The Context from which the activity is being started.
1686 * @param contextThread The main thread of the Context from which the activity
1687 * is being started.
1688 * @param token Internal token identifying to the system who is starting
1689 * the activity; may be null.
1690 * @param target Which activity is performing the start (and thus receiving
1691 * any result); may be null if this call is not being made
1692 * from an activity.
1693 * @param intent The actual Intent to start.
1694 * @param requestCode Identifier for this request's result; less than zero
1695 * if the caller is not expecting a result.
1696 * @param options Addition options.
这是 Activity.java 调用时的传参,和上面的参数对照着理解。
- who:发起请求时的 Context,对应 Activity.java 中的 this
- contextThread:who 对应的主线程,mMainThread.getApplication() 获得,mMainThread 是 ActivityThread 的对象
- token:一个 IBinder 类型对象
- target:当前发起请求的 Activity,这里也对应 this。当不是从 Activity 发起启动请求时,这个参数是 null。 后面会接着当前流程,看一下应用冷启动过程中,ATMS 中的流程