startActivity 启动过程分析课程

本届课程主要讲解了 startActivity 启动过程源码分析,引用的源码版本是 android-28。

假设 ActivityA 跳转到另一个App中的 ActivityB,过程如下图所示:

整个 startActivity 的流程分为 3 大部分,也涉及 3 个进程之间的交互:

  1. ActivityA --> ActivityManagerService(简称 AMS)
  2. ActivityManagerService --> ApplicationThread
  3. ApplicationThread --> Activity

ActivityA --> ActivityManagerService 阶段

这一过程并不复杂,用一张图表示具体过程如下:

Activity 的 startActivity

这里最终调用了 startActivityForResult 方法,传入的 -1 表示不需要获取 startActivity 的结果。

Activity 的 startActivityForResult

startActivityForResult 也很简单,调用 Instrumentation.execStartActivity 方法。剩下的交给 Instrumentation 类去处理。

解释说明:

  • Instrumentation 类主要用来监控应用程序与系统交互。
  • 蓝框中的 mMainThread 是 ActivityThread 类型,ActivityThread 可以理解为一个进程,在这就是 A 所在的进程。
  • 通过 mMainThread 获取一个 ApplicationThread 的引用,这个引用就是用来实现进程间通信的,具体来说就是 AMS 所在系统进程通知应用程序进程进行的一系列操作。

Instrumentation 的 execStartActivity

这里获取了AMS,然后调用了startActivity方法。实际上这里就是通过 AIDL 来调用 AMS 的 startActivity 方法,至此,startActivity 的工作重心成功地从进程 A 转移到了系统进程 AMS 中。

ActivityManagerService --> ApplicationThread

接下来就看下在 AMS 中是如何一步一步执行到 B 进程的。

注:刚才在看 Instrumentation 的时候,讲过一个 ApplicationThread 类,这个类是负责进程间通信的,这里 AMS 最终其实就是调用了 B 进程中的一个 ApplicationThread 引用,从而间接地通知 B 进程进行相应操作。

相比于 startActivity-->AMS,AMS-->ApplicationThread 流程看起来复杂好多了,实际上这里面就干了 2 件事:

  1. 综合处理 launchMode 和 Intent 中的 Flag 标志位,并根据处理结果生成一个目标 Activity B 的对象(ActivityRecord)。
  2. 判断是否需要为目标 Activity B 创建一个新的进程(ProcessRecord)、新的任务栈(TaskRecord)。

AMS 的 startActivity

经过多个方法的调用,最终通过 obtainStarter 方法获取了 ActivityStarter 类型的对象,然后调用其 execute 方法。在 execute 方法中,会再次调用其内部的 startActivityMayWait 方法。

ActivityStarter 的 startActivityMayWait

ActivityStarter 这个类看名字就知道它专门负责一个 Activity 的启动操作。它的主要作用包括解析 Intent、创建 ActivityRecord、如果有可能还要创建 TaskRecord。startActivityMayWait 方法的部分实现如下:


这里的mSupervisor主要是负责 Activity 所处栈的管理类。

在上图中的 resolveIntent 中实际上是调用系统 PackageManagerService 来获取最佳 Activity。有时候我们通过隐式 Intent 启动 Activity 时,系统中可能存在多个 Activity 可以处理 Intent,此时会弹出一个选择框让用户选择具体需要打开哪一个 Activity 界面,就是此处的逻辑处理结果。

在 startActivityMayWait 方法中调用了一个重载的 startActivity 方法,而最终会调用的 ActivityStarter 中的 startActivityUnchecked 方法来获取启动 Activity 的结果。

ActivityStarter 的 startActivityUnchecked

  • 图中 1 处计算启动 Activity 的 Flag 值。
  • 注释 2 处处理 Task 和 Activity 的进栈操作。
  • 注释 3 处启动栈中顶部的 Activity。

computeLaunchingTaskFlags 方法具体如下:

这个方法的主要作用是计算启动 Activity 的 Flag,不同的 Flag 决定了启动 Activity 最终会被放置到哪一个 Task 集合中。

  • 图中 1 处 mInTask 是 TaskRecord 类型,此处为 null,代表 Activity 要加入的栈不存在,因此需要判断是否需要新建 Task。
  • 图中 2 处的 mSourceRecord 的类型 ActivityRecord 类型,它是用来描述“初始 Activity”,什么是“初始 Activity”呢?比如 ActivityA 启动了ActivityB,ActivityA 就是初始 Activity。当我们使用 Context 或者 Application 启动 Activity 时,此 SourceRecord 为 null。
  • 图中 3 处表示初始 Activity 如果是在 SingleInstance 栈中的 Activity,这种需要添加 NEW_TASK 的标识。因为 SingleInstance 栈只能允许保存一个 Activity。
  • 图中 4 处表示如果 Launch Mode 设置了 singleTask 或 singleInstance,则也要创建一个新栈。

ActivityStackSupervisor 的 startActivityLocked

方法中会调用 insertTaskAtTop 方法尝试将 Task 和 Activity 入栈。如果 Activity 是以 newTask 的模式启动或者 TASK 堆栈中不存在该 Task id,则 Task 会重新入栈,并且放在栈的顶部。需要注意的是:Task 先入栈,之后才是 Activity 入栈,它们是包含关系。

这里一下子涌出了好几个概念 Stack、Task、Activity,其实它们都是在 AMS 内部维护的数据结构,可以用一张图来描述它们之间的关系。


如:这次课程是 ActivityA 跳转到另一个App中的 ActivityB,所以如果ActivityB所在的App没有启动。这里会先在AMS中的 ActivityStackSupervisor 中创建 ActivityStack,并且通过创建的 ActivityStack 来管理 TaskRecord。Task会入栈,然后Activity入栈,不同启动模式所放入的栈的方式也是不一样的。如果是 singleTask 或 singleInstance 启动模式,则会另外创建TaskRecord,并添加到 ActivityStack 进行管理。

ActivityStack 的 resumeFocusedStackTopActivityLocked

经过一系列调用,最终代码又回到了 ActivityStackSupervisor 中的 startSpecificActivityLocked 方法。

ActivityStackSupervisor 的 startSpecificActivityLocked

解释说明:

  • 图中 1 处根据进程名称和 Application 的 uid 来判断目标进程是否已经创建,如果没有则代表进程未创建。
  • 图中 2 处调用 AMS 创建 Activity 所在进程。创建进程也就进而创建了ActivityThread对象,AMS 最终也就是调用了 B 进程中的这个 ApplicationThread 引用。

不管是目标进程已经存在还是新建目标进程,最终都会调用图中红线标记的 realStartActivityLocked 方法来执行启动 Activity 的操作。

ActivityStackSupervisor 的 realStartActivityLocked

这个方法在 android-27 和 android-28 版本的区别很大,从 android-28 开始 Activity 的启动交给了事务(Transaction)来完成。

  • 图中 1 处创建 Activity 启动事务,并传入 app.thread 参数,它是 ApplicationThread 类型。在上文 startActivity 阶段已经提过 ApplicationThread 是为了实现进程间通信的,是 ActivityThread 的一个内部类。
  • 图中 2 处执行 Activity 启动事务。

Activity 启动事务的执行是由 ClientLifecycleManager 来完成的,具体代码如下:

可以看出实际上是调用了启动事务 ClientTransaction 的 schedule 方法,而这个 transaction 实际上是在创建 ClientTransaction 时传入的 app.thread 对象,也就是 ApplicationThread。如下所示:

解释说明:

  • 这里传入的 app.thread 会赋值给 ClientTransaction 的成员变量 mClient,ClientTransaction 会调用 mClient.scheduleTransaction(this) 来执行事务。
  • 这个 app.thread 是 ActivityThread 的内部类 ApplicationThread,所以事务最终是调用 app.thread 的 scheduleTransaction 执行。

到这为止 startActivity 操作就成功地从 AMS 转移到了另一个进程 B 中的ApplicationThread中,剩下的就是 AMS 通过进程间通信机制通知 ApplicationThread 执行 ActivityB 的生命周期方法。而这里的app.thread也是在startSpecificActivityLocked中创建进程时所创建的。

ApplicationThread -> Activity

在 ClientLifecycleManager 中会调用 ClientTransaction的schedule() 方法


而 mClient 是一个 IApplicationThread 接口类型,具体实现是 ActivityThread 的内部类 ApplicationThread。因此后续执行 Activity 生命周期的过程都是由 ApplicationThread 指导完成的,scheduleTransaction 方法如下:



可以看出,这里还是调用了ActivityThread 的 scheduleTransaction 方法。但是这个方法实际上是在 ActivityThread 的父类 ClientTransactionHandler 中实现,具体如下:

调用 sendMessage 方法,向 Handler 中发送了一个 EXECUTE_TRANSACTION 的消息,并且 Message 中的 obj 就是启动 Activity 的事务对象。而这个 Handler 的具体实现是 ActivityThread 中的 mH 对象。具体如下:

最终调用了事务的 execute 方法,execute 方法如下:

在 executeCallback 方法中,会遍历事务中的 callback 并执行 execute 方法

之前添加callback的地方

在创建 ClientTransaction 时,通过 addCallback 方法传入了 Callback 参数,从图中可以看出其实是一个 LauncherActivityItem 类型的对象。

LaunchActivityItem 的 execute()

终于到了跟 Activity 生命周期相关的方法了,图中 client 是 ClientTransationHandler 类型,实际实现类就是 ActivityThread。因此最终方法又回到了 ActivityThread。

ActivityThread 的 handleLaunchActivity

这是一个比较重要的方法,Activity 的生命周期方法就是在这个方法中有序执行,具体如下:

解释说明:

  • 图中 1 处初始化 Activity 的 WindowManager,每一个 Activity 都会对应一个“窗口”,下一节会详细讲解。
  • 图中 2 处调用 performLaunchActivity 创建并显示 Activity。
  • 图中 3 处通过反射创建目标 Activity 对象。
  • 图中 4 处调用 attach 方法建立 Activity 与 Context 之间的联系,创建 PhoneWindow 对象,并与 Activity 进行关联操作,下节会详细讲解。
  • 图中 5 处通过 Instrumentation 最终调用 Activity 的 onCreate 方法。

至此,目标 Activity 已经被成功创建并执行生命周期方法。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,053评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,527评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,779评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,685评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,699评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,609评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,989评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,654评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,890评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,634评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,716评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,394评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,976评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,950评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,191评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,849评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,458评论 2 342