十九、startActivity过程分析

概述

本文基于android-28版本分析 startActivity 的启动流程。

整体流程

当我们点击手机桌面上的一个应用图标时,最终是通过 startActivity 方法去打开一个 Activity页面。

Android中每一个App都是一个进程。

startActivity的操作,必须先判定当前应用进程是否存在,如果不存在则需要先创建。

在这里我们必须清楚的是,Activity在整个安卓系统中不是孤立存在的,我们创建一个 应用A,在其中自定义一个Activity,这个Activity不一定就出现在 应用A的进程中,它可以通过 taskAffinity 出现在其他应用的Activity栈内。 由此可见,Activity并没有死死绑在 某一个App进程中。

startActivity的流程如图所示:

startActiviy的核心动作,其实只是把一个跳转意图Intent转发给了 AndroidSystem(更具体来说是ActivityManagerService,简称AMS),AMS 来解析这个Intent,然后根据情况看是否需要创建 应用进程,确保应用进程存在的情况下,再去把这个Activity放入该进程的Activity栈中。

步骤分析

整个流程分为三大步骤:

  • Activity A ------> ActivityManagerService
  • ActivityManagerService ------> ApplicationThread
  • ApplicationThread ------> ActivityB

第一步 Activity A ------> ActivityManagerService

示意图

源码分析

  1. 执行 startActivity 后,转到 startActivityForResult,这个-1表示不需要关注跳转的结果。

  2. 跳转的实际动作,委托给了 Instrumention 。 注意:Instrumention这个类,主要用于当前进程与系统进程的交互。你看它在 startActivity时传入了一个 applicationThread 对象,它就是 我们自己app所在的进程,我们可以通过 这个application对象实现 我们自己的进程与系统进程之间的通信,通信方式是 Binder AIDL 。

  3. Instrumention 中的实际跳转动作

具体实现为,调用 AMS实例 (ActivityManager.getService())来执行系统级别的 startActivity跳转动作。

至此,startActivity的第一阶段,从当前app进程,工作重心,转移到了 系统进程中的AMS服务。

第二步 ActivityManagerService ------> ApplicationThread

首先剧透一下:每一个app,都会有 ApplicationThread实例保存在 系统进程中。当我们从ActivityA 跳转到 ActivityB,首先会通知到ActivityB所在进程的 ApplicationThread实例,最终由它去执行跳转动作。

这一步骤比较复杂,但是整体来说分为两个过程:

  1. 处理intent中的 launchModeintent中的flag标志位, 并通过结果生成一个 ActivityB的实例对象(ActivityRecord
  2. 判断是否需要为目标ActivityB创建新的 进程(processRecord) 和 新的Activity任务栈 (TaskRecord

源码分析

  • 以下是 ActivityManagerService.java中 跟踪startActivity方法到达的最终代码位置:

它最终通过 obtainStarter方法获取了 ActivityStarter类型的对象,然后执行了 execute() 方法来执行跳转。 ActivityStarter这个类专门用于Activity的启动工作。主要作用 为 :解析Intent创建ActivityRecord,如有必要还要创建TaskRecord对象。

  • 以下是 ActivityStarterstartActivityMayWait方法,

它其中调用了 mSuperVisorresolveActivity 方法。mSuperVisor 的类型是 ActivityStackSuperVisor, 它负责Activity所处栈的管理。上图中还调用了它的 resloveIntent()实际上是根据传入的intent,调用了系统的PackageManagerService来获取最佳Activity。 有时候我们通过隐式Intent来启动Activity时,系统中可能存在多个符合条件的Activity,此时会弹出一个选择框,让用户选择启动哪一个Activity

  • 在 startActivityMayWait() 中最后调用了 一个重载的startActivity(),它其中实际上走到了下图的代码: startActivityUnchecked(),返回值int表示启动Activity的结果。 上图中3个关键代码分别代表的3个关键步骤:

1. computeLaunchingTaskFlags

计算Activity启动的Flag值

  • 不同的Flag将会决定启动Activity放置在哪一个Task集合中
  • 图中1处的 mInTaskTaskRecord 类型,此处为 null,表示 Activity要加入的栈不存在,因此需要判断是否需要新建Task。
  • 图中2处的 mSourceRecordActivityRecord 类型,它是用来描述初始Activity,比如 由ActivityA启动了ActivityB,那么ActivityA就是 初始Activity。当我们用 Context或者Application启动Activity时,mSourceRecord就是null
  • 图中3处表示 初始Activity如果是在 SINGLE_INTANCE(Activity容量为1),那么必须添加NEW_TASK标识。
  • 图中4处,如果我们启动Activity时设置了launchMode设置了 SINGLE_INTANCE 或者 SINGLE_TASK, 则也要创建一个新栈。

2. mTargetStack.startActivityLocked()

处理Task和Activity的入栈操作 图中的 insertTaskAtTop(),尝试将 task和Activity入栈。如果Activity是以 NEW_TASK的模式启动,或者 task堆栈中不存在该taskId,则task会重新入栈,并且放在栈的顶部。 需要注意的是,task先入栈,然后才是Activity入栈。 上图是 Task和Activity的关系,Task属于Activity的父集。ActivityStack是系统级别的容器,存放所有Activity,但是存放的过程有2个层次的,最大的是ActivityStack,接下来是 Task,而最小的是 Activity。

3. mSupervisor.resumeFocusedStackTopActivityLocked()

启动栈中顶部的Activity

mSuperVisor 的类型是 ActivityStackSuperVisor, 这个方法实际上走到了ActvityStack的resumeFocusedStackTopActivityLocked() 最终走到了 ActivityStackSuperVisorstartSpecificActivityLocked() 图中1处,根据进程名称和 applicaiton的uid来判定目标进程是否已经创建。

图中2处,调用AMS创建Activity所在进程。

不管目标进程是否存在,最终都会调用, realStartActivityLocked() 来启动Activity

这个 realStartActivityLocked() 的实现,从android-28开始交给了事务(Transaction)来处理。 图中1处,创建了Activity启动事务,传入了App.thread对象。它是ApplicationThread类型。这个ApplicationThread是用于处理进程间(Binder)通信的,它是 ActivityThread的内部类。

图中2处,执行activity的启动事务。

Activity的启动事务是由 ClientLifecycleManager 来完成。 上图中的 transaction对象的创建,则是通过传入的app.thread来创建的。 所以 事务最终是调用了 ApplicationThread的 scheduleTranscation 来执行。

至此,startActivity的流程就从 AMS转移到了 另一个进程的ApplicationThread中。

第三步 ApplicationThread ------> ActivityB

刚才我们提到了 AMSstartActivity 的操作当做一个事务,传递给了ActivityB的ApplicationThreadscheduleTranscation 去完成. 后续的Activity生命周期过程都是 ApplicationThread这个内部类去完成的。

看到这个Stub,就应该能想起来,这是AIDL的一个抽象类。上图中的 scheduleTransaction 中,虽然是调用了 ActivityThread.this.schedueTransaction() 看着像是它在执行外部 ActivityThread的某个过程. 但是实际上,过程在 ActivityThread 父类 ClientTransactionHandler中,如下图:

通过handler发送了 EXECUTE_TRANSACTION 消息。 而这里的 大写 H,是 ActivityThread的内部类:

这里调用了 mTransactionExecutor的 execute方法,参数则是外部传入的 事务对象。

TransactionExecutor源代码如下:

execute()最终走到了 executeCallbacks()。上图中,对多个事务进行了遍历,逐个执行它们的 execute方法。这些callback是如何添加的呢? 其实是我们创建事务的时候,我们通过addCallback方法传入了 callback对象。如下图:

这个callback,其实是 LaunchActivityItem 类型的对象。 那么我们跟踪的重点就是 LaunchActivityItem 的 execute(),

终于到了和Activity生命周期相关的方法。

图中的 client是 ClientTransactionHandler类型,实际实现类是ActivityThread

下图是 本次startActivity的重点内容 ActivityThread 源代码如下:

图中1处,初始化Activity的WindowManager。每一个Activity都会对应一个窗口。 图中2处,创建并显示Activity。 图中3处,通过反射创建 目标Activity对象。 图中4处,建立Activity与context之间的联系。创建PhoneWindow对象并与Activity进行关联。 图中5处,通过 Instrumention调用activity的onCreate方法。

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

总结

本文详细跟踪了 Activity 跳转的详细源码流程。

整个流程涉及到3个进程间通信:

  • 进程A通过Binder调用了AMS的startActivity
  • AMS通过一系列计算 构造目标intent,然后 ActivityStack与 ActivityStackSupervisor中处理了Task和Activity的入栈操作
  • AMS通过 Binder机制 调用了 目标进程的 ApplicationThread方法来执行Activity的生命周期方法。ApplicationThrad是ActivityThread一个内部类,所以这一过程最终都执行到了 ActivityThread中。
相关推荐
Lee川1 天前
优雅进化的JavaScript:从ES6+新特性看现代前端开发范式
javascript·面试
Lee川1 天前
从异步迷雾到优雅流程:JavaScript异步编程与内存管理的现代化之旅
javascript·面试
晴殇i1 天前
揭秘JavaScript中那些“不冒泡”的DOM事件
前端·javascript·面试
绝无仅有1 天前
Redis过期删除与内存淘汰策略详解
后端·面试·架构
绝无仅有1 天前
Redis大Key问题排查与解决方案全解析
后端·面试·架构
AAA梅狸猫1 天前
Looper.loop() 循环机制
面试
AAA梅狸猫1 天前
Handler基本概念
面试
Wect2 天前
浏览器缓存机制
前端·面试·浏览器
掘金安东尼2 天前
Fun with TypeScript Generics:玩转 TS 泛型
前端·javascript·面试
掘金安东尼2 天前
Next.js 企业级落地
前端·javascript·面试