Activity启动流程分析
我们来看一下 startActivity 过程的具体流程。在手机桌面应用中点击某一个 icon 之后,实际上最终就是通过 startActivity 去打开某一个 Activity 页面。我们知道 Android 中的一个 App 就相当于一个进程,所以 startActivity 操作中还需要判断,目标 Activity 的进程是否已经创建,如果没有,则在显示 Activity 之前还需要将进程 Process 提前创建出来。假设是从 ActivityA 跳转到另一个 App 中的 ActivityB,过程如下图所示:
整个 startActivity 的流程分为 3 大部分,也涉及 3 个进程之间的交互:
- ActivityA --> ActivityManagerService(简称 AMS)
- ActivityManagerService --> ApplicationThread
- ApplicationThread --> Activity
ActivityA --> ActivityManagerService 阶段
这一过程并不复杂,用一张图表示具体过程如下:
接下来看下源码中做了哪些操作。
Activity 的 startActivity方法
@Override
public void startActivity(Intent intent, @Nullable Bundle options) {
if (options != null) {
startActivityForResult(intent, -1, options);
} else {
// Note we want to go through this call for compatibility with
// applications that may have overridden the method.
startActivityForResult(intent, -1);
}
}
最终调用了 startActivityForResult 方法,传入的 -1 表示不需要获取 startActivity 的结果。
Activity 的 startActivityForResult
具体代码如下所示:
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
@Nullable Bundle options) {
if (mParent == null) {
options = transferSpringboardActivityOptions(options);
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this,
intent, requestCode, options);
if (ar != null) {
mMainThread.sendActivityResult(
mToken, mEmbeddedID, requestCode, ar.getResultCode(),
ar.getResultData());
}
if (requestCode >= 0) {
// If this start is requesting a result, we can avoid making
// the activity visible until the result is received. Setting
// this code during onCreate(Bundle savedInstanceState) or onResume() will keep the
// activity hidden during this time, to avoid flickering.
// This can only be done when a result is requested because
// that guarantees we will get information back when the
// activity is finished, no matter what happens to it.
mStartedActivity = true;
} cancelInputsAndStartExitTransition(options);
// TODO Consider clearing/flushing other event sources and events for child windows.
} else {
if (options != null) {
mParent.startActivityFromChild(this, intent, requestCode, options);
} else {
// Note we want to go through this method for compatibility with
// existing applications that may have overridden it.
mParent.startActivityFromChild(this, intent, requestCode);
}
}
}
startActivityForResult 也很简单,调用 Instrumentation.execStartActivity 方法。剩下的交给 Instrumentation 类去处理。
解释说明:
- Instrumentation 类主要用来监控应用程序与系统交互。
- 代码中的mMainThread 是 ActivityThread 类型,ActivityThread 可以理解为一个进程,在这就是 A 所在的进程。
- 通过 mMainThread 获取一个 ApplicationThread 的引用,这个引用就是用来实现进程间通信的,具体来说就是 AMS 所在系统进程通知应用程序进程进行的一系列操作。
Instrumentation 的 execStartActivity
方法如下:
try {
intent.migrateExtraStreamToClipData();
intent.prepareToLeaveProcess(who);
int result = ActivityTaskManager.getService()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options);
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
return null;
通过ActivityTaskManager.getService()获取获取ATMS的服务代理,跨进程调用ATMS的startActivity方法,下一步就ATMS内部startActivityAsUser()方法处理。
int startActivityAsUser(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId,
boolean validateIncomingUser) {
enforceNotIsolatedCaller("startActivityAsUser"); userId = getActivityStartController().checkTargetUser(userId, validateIncomingUser,
Binder.getCallingPid(), Binder.getCallingUid(), "startActivityAsUser"); // TODO: Switch to user app stacks here.
return getActivityStartController().obtainStarter(intent, "startActivityAsUser")
.setCaller(caller)
.setCallingPackage(callingPackage)
.setResolvedType(resolvedType)
.setResultTo(resultTo)
.setResultWho(resultWho)
.setRequestCode(requestCode)
.setStartFlags(startFlags)
.setProfilerInfo(profilerInfo)
.setActivityOptions(bOptions)
.setMayWait(userId)
.execute(); }
经过多个方法的调用,最终通过 obtainStarter 方法获取了 ActivityStarter 类型的对象,然后调用其 execute 方法。在 execute 方法中,会再次调用其内部的 startActivityMayWait 方法。
ActivityStarter 的 startActivityMayWait
ActivityStarter 这个类看名字就知道它专门负责一个 Activity 的启动操作。它的主要作用包括解析 Intent、创建 ActivityRecord、如果有可能还要创建 TaskRecord。startActivityMayWait 方法的部分实现如下:
private int startActivityMayWait(IApplicationThread caller, int callingUid,
String callingPackage, int requestRealCallingPid, int requestRealCallingUid,
Intent intent, String resolvedType, IVoiceInteractionSession voiceSession,
IVoiceInteractor voiceInteractor, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, WaitResult outResult,
Configuration globalConfig, SafeActivityOptions options, boolean ignoreTargetSecurity,
int userId, TaskRecord inTask, String reason,
boolean allowPendingRemoteAnimationRegistryLookup,
PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart) {
// Refuse possible leaked file descriptors
if (intent != null && intent.hasFileDescriptors()) {
throw new IllegalArgumentException("File descriptors passed in Intent");
}
...
ResolveInfo rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId,
0 /* matchFlags */,
computeResolveFilterUid(
callingUid, realCallingUid, mRequest.filterCallingUid));
if (rInfo == null) {
UserInfo userInfo = mSupervisor.getUserInfo(userId);
if (userInfo != null && userInfo.isManagedProfile()) {
// Special case for managed profiles, if attempting to launch non-cryto aware
// app in a locked managed profile from an unlocked parent allow it to resolve
// as user will be sent via confirm credentials to unlock the profile.
UserManager userManager = UserManager.get(mService.mContext);
boolean profileLockedAndParentUnlockingOrUnlocked = false;
long token = Binder.clearCallingIdentity();
try {
UserInfo parent = userManager.getProfileParent(userId);
profileLockedAndParentUnlockingOrUnlocked = (parent != null)
&& userManager.isUserUnlockingOrUnlocked(parent.id)
&& !userManager.isUserUnlockingOrUnlocked(userId);
} finally {
Binder.restoreCallingIdentity(token);
}
if (profileLockedAndParentUnlockingOrUnlocked) {
rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId,
PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
computeResolveFilterUid(
callingUid, realCallingUid, mRequest.filterCallingUid));
}
}
}
// Collect information about the target of the Intent.
ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);
...
从上图可以看出获取目标 Activity 信息的操作由 mSupervisor 来实现,它是 ActivityStackSupervisor 类型,从名字也能猜出它主要是负责 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 内部维护的数据结构,可以用一张图来描述它们之间的关系。
ActivityStack 的 resumeFocusedStackTopActivityLocked
经过一系列调用,最终代码又回到了 ActivityStackSupervisor 中的 startSpecificActivityLocked 方法。
ActivityStackSupervisor 的 startSpecificActivityLocked
解释说明:
- 图中 1 处根据进程名称和 Application 的 uid 来判断目标进程是否已经创建,如果没有则代表进程未创建。
- 图中 2 处调用 AMS 创建 Activity 所在进程。
不管是目标进程已经存在还是新建目标进程,最终都会调用图中红线标记的 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 的生命周期方法。
ApplicationThread -> Activity
刚才我们已近分析了 AMS 将启动 Activity 的任务作为一个事务 ClientTransaction 去完成,在 ClientLifecycleManager 中会调用 ClientTransaction的schedule() 方法,如下:
public void schedule() throws RemoteException {
mClient.scheduleTransaction(this);
}
而 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 方法,这些 callbacks 是何时被添加的呢?
还记得 ClientTransaction 是如何创建被创建的吗?重新再看一遍:
在创建 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 已经被成功创建并执行生命周期方法。
总结
详细查看了 Activity 的启动在源码中的实现流程(不同版本sdk源码还是挺大变化的,自己可以去查看)。这一过程主要涉及 3 个进程间的通信过程:
1》首先进程 A 通过 Binder 调用 AMS 的 startActivity 方法。
2》然后 AMS 通过一系列的计算构造目标 Intent,然后在 ActivityStack 与 ActivityStackSupervisor 中处理 Task 和 Activity 的入栈操作。
3》最后 AMS 通过 Binder 机制,调用目标进程中 ApplicationThread 的方法来创建并执行 Activity 生命周期方法,实际上 ApplicationThread 是 ActivityThread 的一个内部类,它的执行最终都调用到了 ActivityThread 中的相应方法。
————来自拉勾教育笔记
Activity启动流程分析的更多相关文章
- Cocos2d-x3.3RC0的Android编译Activity启动流程分析
本文将从引擎源代码Jni分析Cocos2d-x3.3RC0的Android Activity的启动流程,以下是具体分析. 1.引擎源代码Jni.部分Java层和C++层代码分析 watermark/2 ...
- Activity的启动流程分析
Activity是Android应用程序的四大组件之中的一个,负责管理Android应用程序的用户界面,一般一个应用程序中包括非常多个Activity,他们可能执行在一个进程中.也可能执行在不同的进程 ...
- 《转》深入理解Activity启动流程(四)–Activity Task的调度算法
本文原创作者:Cloud Chou. 出处:本文链接 本系列博客将详细阐述Activity的启动流程,这些博客基于Cm 10.1源码研究. 深入理解Activity启动流程(一)--Activity启 ...
- 《转》深入理解Activity启动流程(三)–Activity启动的详细流程1
本文原创作者:Cloud Chou. 出处:本文链接 本系列博客将详细阐述Activity的启动流程,这些博客基于Cm 10.1源码研究. 深入理解Activity启动流程(一)--Activity启 ...
- 深入理解Activity启动流程(四)–Activity Task的调度算法
本系列博客将详细阐述Activity的启动流程,这些博客基于Cm 10.1源码研究. 深入理解Activity启动流程(一)--Activity启动的概要流程 深入理解Activity启动流程(二)- ...
- 庖丁解牛 Activity 启动流程
前言 这是 Android 9.0 AOSP 系列 的第五篇了,先来回顾一下前面几篇的大致内容. Java 世界的盘古和女娲 -- Zygote 主要介绍了 Android 世界的第一个 Java 进 ...
- 史上最全且最简洁易懂的Activity启动流程解析
Activity的启动流程是一个资深Android工程师必须掌握的内容,也是高职级面试中的高频面试知识点,无论是从事应用层开发,还是Framework开发,其重要性都无需我多言.而要真正理解它,就不可 ...
- u-boot启动流程分析(2)_板级(board)部分
转自:http://www.wowotech.net/u-boot/boot_flow_2.html 目录: 1. 前言 2. Generic Board 3. _main 4. global dat ...
- u-boot启动流程分析(1)_平台相关部分
转自:http://www.wowotech.net/u-boot/boot_flow_1.html 1. 前言 本文将结合u-boot的“board—>machine—>arch—> ...
随机推荐
- 心有 netty 一点通!
一.标准的netty线程模型 双池合璧: 1.连接线程池: 连接线程池专门负责监听客户端连接请求,并完成连接的建立(包括诸如握手.安全认证等过程). 连接的建立本身是一个极其复杂.损耗性能的过程,此处 ...
- 测试-spring源码摘取
首次加载idea是真的慢... 也许是我电脑性能太差... 我分析他内部有一套索引的机制,需要每次打开现建立... 没有固态的话,首次打开还不如eclipse 第一次使用博客园,以前都在csdn混~ ...
- 一台电脑如何使用多个git账号?
git相信大家都在用,一般公司有一个账号,放公司自己架的服务器中,员工自己还有一个github或者gitee的账号,存放自己的一些私有代码.本篇文章总结一下,本人在公司开发机上,使用多个git账号的干 ...
- kafka架构、基本术语、消息存储结构
1.kafka架构 kafka处理消息大概流程 生产者发送消息给kafka服务器 消费者从kafka服务器(broker)读取消息 kafka服务器依靠zookeeper集群进行服务协调管理 2.ka ...
- JPA 中 find() 和 getReference() 的区别
在查询的时候有两个方法:find()和getReference(),这两个方法的参数以及调用方式都相同.那么这两个方法有什么不一样的呢? find()称为 立即加载,顾名思义就是在调用的时候立即执行查 ...
- 驱动开发 —— 从零开始(1) 配置vs20xx+wdkxx环境
网上教程很多.如何去安装如何去配置 但是也有些坑感觉并不是那么的完善 wdk+vs下载链接:https://docs.microsoft.com/zh-cn/windows-hardware/driv ...
- python基础整理(2)
布尔表达式与其余值的替换 值的测试 Python不仅仅可以使用布尔型变量作为条件,它可以直接在if中使用任何表达式作为条件: 大部分表达式的值都会被当作True,但以下表达式值会被当作False: F ...
- Mac 电脑查看 pkg包的安装路径
pkgutil --pkgspkgutil --infopkgutil --files
- Mac OS 生成 icon 和 ico 文件
[本文版权归微信公众号"代码艺术"(ID:onblog)所有,若是转载请务必保留本段原创声明,违者必究.若是文章有不足之处,欢迎关注微信公众号私信与我进行交流!] 1. 生成 IC ...
- SpringBoot -- 项目结构+启动流程
一.简述: 项目结构 二.简述:启动流程 说springboot的启动流程,当然少不了springboot启动入口类 @SpringBootApplication public class Sprin ...