前言
启动流程梳理:
- Activity 启动流程(一)—— Launcher 阶段
- Activity 启动流程(二)—— AMS 处理阶段
- Activity 启动流程(三)—— 应用程序进程启动阶段
- Activity 启动流程(四)—— ActivityThread 初始化阶段
- Activity 启动流程(五)—— Activity 启动阶段
- Activity 启动流程(六)—— Activity 窗口显示
代码基于 android-14.0.0_r9。
1. 一句话总览
这一阶段系统侧 接收启动请求 -> 权限与安全校验 -> 组装 ActivityStarter -> 创建服务端模型 ActivityRecord -> 分配或复用 Task -> 尝试 Resume 目标 -> 若进程未启动,则异步交接给进程启动链路。
主链路如下:
ActivityTaskManagerService.startActivityAsUser()→ActivityStartController.obtainStarter()→ActivityStarter.execute()→executeRequest()→startActivityUnchecked()→startActivityInner()→RootWindowContainer.resumeFocusedTasksTopActivities()→Task.resumeTopActivityUncheckedLocked()→TaskFragment.resumeTopActivity()→ActivityTaskSupervisor.startSpecificActivity()/mAtmService.startProcessAsync()
2. AMS 处理阶段源码分析
2.1 startActivityAsUser
Launcher 跨进程调用进入 ATMS.startActivityAsUser(),ATMS 先做调用方校验,再把请求交给 ActivityStarter:
// frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
private int startActivityAsUser(IApplicationThread caller, String callingPackage,
@Nullable String callingFeatureId, Intent intent, String resolvedType,
IBinder resultTo, String resultWho, int requestCode, int startFlags,
ProfilerInfo profilerInfo, Bundle bOptions, int userId, boolean validateIncomingUser) {
final SafeActivityOptions opts = SafeActivityOptions.fromBundle(bOptions);
assertPackageMatchesCallingUid(callingPackage);
enforceNotIsolatedCaller("startActivityAsUser");
userId = getActivityStartController().checkTargetUser(userId, validateIncomingUser,
Binder.getCallingPid(), Binder.getCallingUid(), "startActivityAsUser");
return getActivityStartController().obtainStarter(intent, "startActivityAsUser")
.setCaller(caller) // 设置调用者线程,用于后续的跨进程通信
.setCallingPackage(callingPackage) // 设置调用者包名,用于权限验证和日志记录
.setCallingFeatureId(callingFeatureId) // 设置调用者功能 ID,细粒度权限控制
.setResolvedType(resolvedType) // 设置解析后的 MIME 类型,用于精确匹配
.setResultTo(resultTo) // 设置结果返回目标,支持 startActivityForResult
.setResultWho(resultWho) // 设置结果标识符,区分多个结果回调
.setRequestCode(requestCode) // 设置请求码,与结果回调配合使用
.setStartFlags(startFlags) // 设置启动标志,控制 Activity 启动行为
.setProfilerInfo(profilerInfo) // 设置性能分析信息,用于调试和监控
.setActivityOptions(opts) // 设置启动选项,包含动画和窗口配置
.setUserId(userId) // 设置目标用户 ID,支持多用户场景
.execute(); // 执行启动流程,返回启动结果;面试要点:这里开始进入 ActivityStarter 的核心启动逻辑
}
这一层的职责很明确:
- 做入口安全收口:校验调用包名、隔离进程、目标用户是否合法。
- 把原始启动参数整理成内部请求:例如
SafeActivityOptions、userId、resultTo。 - 将启动裁决委托给
ActivityStarter。
ActivityStartController 只负责拿到一个可配置的 ActivityStarter:
// frameworks/base/services/core/java/com/android/server/wm/ActivityStartController.java
ActivityStarter obtainStarter(Intent intent, String reason) {
return mFactory.obtain().setIntent(intent).setReason(reason);
}
2.2 executeRequest:ActivityStarter 创建 ActivityRecord
真正的核心从 ActivityStarter.execute() 进入 executeRequest():
// frameworks/base/services/core/java/com/android/server/wm/ActivityStarter.java
int execute() {
...
res = executeRequest(mRequest);
...
}
private int executeRequest(Request request) {
...
final ActivityRecord r = new ActivityRecord.Builder(mService)
.setCaller(callerApp)
.setLaunchedFromPid(callingPid)
.setLaunchedFromUid(callingUid)
.setLaunchedFromPackage(callingPackage)
.setIntent(intent)
.setResolvedType(resolvedType)
.setActivityInfo(aInfo)
.setResultTo(resultRecord)
.setRequestCode(requestCode)
.setActivityOptions(checkedOptions)
.setSourceRecord(sourceRecord)
.build();
mLastStartActivityResult = startActivityUnchecked(r, sourceRecord, voiceSession,
request.voiceInteractor, startFlags, checkedOptions,
inTask, inTaskFragment, balVerdict, intentGrants, realCallingUid);
return mLastStartActivityResult;
}
这里最关键的变化是:一次抽象的启动请求,先被系统侧具象化成一个 ActivityRecord。
ActivityRecord 还不是客户端里的 Activity 实例,它只是 system_server 里对这次启动的服务端描述:调用来源、目标组件、返回链路、启动参数、所属任务关系,后续都围绕它来决策。
下一步才是决定它该挂到哪个 Task、要不要复用已有栈、要不要把任务切到前台。
2.3 任务决策:startActivityInner() 落栈、复用、前台切换
注:
startActivityInner()包含复杂的落栈、复用、前台切换等核心逻辑,为避免篇幅过长,具体的源码细节分析已抽取至另一篇文章:Activity 启动流程扩展篇(一)—— startActivityInner 任务决策。此处仅保留主流程代码与框架讲解。
startActivityUnchecked() 只是把请求继续推进到 startActivityInner();真正决定“点击桌面图标后,这个 Activity 到底落到哪个 Task、是不是复用旧实例、要不要把任务切到前台、什么时候触发 resume”的核心逻辑,都在 startActivityInner() 里。
这里要先明确一个前提:Launcher 点击图标启动应用,绝大多数情况下都带有 FLAG_ACTIVITY_NEW_TASK,而且调用方通常不是目标应用自己的前台 Activity,而是 Launcher 进程。 这会直接影响后续 Task 选择逻辑。
startActivityInner() 的代码非常长,但主干逻辑可以清晰地划分为 8 个核心阶段:
// frameworks/base/services/core/java/com/android/server/wm/ActivityStarter.java
int startActivityInner(final ActivityRecord r, ActivityRecord sourceRecord, ...) {
// 【阶段 1】初始化启动状态:为后续复杂的逻辑计算建立统一的上下文(如 Intent、LaunchMode 等)
setInitialState(...);
// 【阶段 2】统一修正最终 Flag:结合 launchMode、sourceRecord 等,重新计算最终生效的 mLaunchFlags
computeLaunchingTaskFlags();
mIntent.setFlags(mLaunchFlags);
// 【阶段 3】处理 Dream 状态:判断屏保/待机展示界面是否处于活跃状态,避免抢夺焦点
boolean dreamStopping = false;
// ... 检查 mSupervisor.mStoppingActivities
// 【阶段 4】记录启动前现场:保存启动前的焦点 RootTask 和叶子 Task,用于后续判断是否发生了真正的任务切换
final Task prevTopRootTask = mPreferredTaskDisplayArea.getFocusedRootTask();
final Task prevTopTask = prevTopRootTask != null ? prevTopRootTask.getTopLeafTask() : null;
// 【阶段 5】查找可复用 Task:核心方法!判断系统里是否已经存在一个属于该应用、且足够匹配当前启动语义的历史 Task 可以直接复用
final Task reusedTask = getReusableTask();
// 【阶段 6】决定最终目标 Task:如果找到可复用 Task 就用它,否则根据条件推导或准备新建 Task
final Task targetTask = reusedTask != null ? reusedTask : computeTargetTask();
final boolean newTask = targetTask == null;
mTargetTask = targetTask;
// 【阶段 7】计算启动参数与准入校验:总闸门,只要权限、安全策略、后台启动限制中任一条件不满足,直接返回失败
computeLaunchParams(r, sourceRecord, targetTask);
int startResult = isAllowedToStart(r, newTask, targetTask);
if (startResult != START_SUCCESS) {
return startResult;
}
// 【阶段 8】命中目标 Task 后的细化处理:决定是复用旧页面还是创建新实例,并完成入栈、前台切换等
final ActivityRecord targetTaskTop = newTask ? null : targetTask.getTopNonFinishingActivity();
if (targetTaskTop != null) {
// 存在可复用栈顶:走 recycleTask() 尝试复用并处理 singleTop / clearTop 语义
startResult = recycleTask(targetTask, targetTaskTop, reusedTask, intentGrants, balVerdict);
if (startResult != START_SUCCESS) {
return startResult;
}
} else {
// 无可复用栈顶:准备向现有 Task 追加新 ActivityRecord
mAddingToTask = true;
}
// (省略部分)确保 RootTask 存在,执行实际的入栈操作 (setNewTask 或 addOrReparentStartingActivity)
// (省略部分)将目标任务提至前台 (moveToFront)
// 移交控制权:从任务决策阶段进入真正的生命周期和进程启动阶段
mTargetRootTask.startActivityLocked(mStartActivity, prevTopRootTask, newTask, isTaskSwitch, mOptions, sourceRecord);
if (mDoResume) {
mRootWindowContainer.resumeFocusedTasksTopActivities(
mTargetRootTask, mStartActivity, mOptions, mTransientLaunch);
}
return START_SUCCESS;
}
mDoResume 本质是 ActivityStarter 在初始化阶段计算出的“是否推进前台恢复链”的总开关:满足可见、可前台、无特殊限制(如 launchTaskBehind、avoidMoveToFront、overlay/瞬态启动)时才为 true,否则本次启动仅完成入栈与可见性准备,不触发 resume;即便为 true,系统仍会二次校验当前 RootTask 是否具备前台资格——若不可聚焦或被 overlay 阻挡,只做 ensureActivitiesVisible + executeAppTransition 保证先显示和动画执行;只有在具备前台条件时,必要时通过 moveToFront 修正焦点后,才调用 resumeFocusedTasksTopActivities(),正式进入 pause/resume/进程拉起/ClientTransaction 的生命周期调度链路。
一句话概括 startActivityInner() 的本质:它不是直接调用客户端 onCreate() 的地方,而是负责在 system_server 中完成一次 Activity 启动的“任务结构决策”。
经过上述 8 个阶段的处理,系统已经明确了:
- 该把这个
ActivityRecord放进哪个Task里。 - 应该新建实例,还是复用已有的旧实例。
- 需不需要把对应的
Task移到屏幕最前方。 - 最终,将控制权移交给
RootWindowContainer.resumeFocusedTasksTopActivities(),进入真正的页面恢复和进程拉起链路。
2.4 恢复顶部 Activity
startActivityInner() 完成以后,system_server 已经拿到了放入正确 Task / TaskFragment 层级里的 ActivityRecord。但这还只是“服务端结构摆正”,并不等于客户端已经执行到 onResume()。
真正把“谁应该成为前台页面”“是否必须先 pause 旧页面”“目标进程是否已经就绪”“是直接投递生命周期事务还是先拉起进程”串起来的,是 RootWindowContainer 开始的恢复链。
本节只保留主流程。更细的逐行拆解、分支展开与源码细节,已经独立整理到 Activity 启动流程扩展篇(二)—— 恢复顶部 Activity 与进程调度全解析。
2.4.1 主线入口:先由 RootWindowContainer 做全局恢复分发
恢复入口是 RootWindowContainer.resumeFocusedTasksTopActivities()
这一层的职责不是直接恢复某个 Activity,而是先从全局窗口结构里确认“当前到底允许谁进入前台”:
- 先看
mTaskSupervisor.readyToResume(),如果当前处于延迟恢复阶段,整条恢复链直接暂停。 - 优先尝试启动阶段已经选中的
targetRootTask,避免恢复目标在下游再次漂移。 - 再按
Display -> RootTask扫描全局可见且可聚焦的前台候选者,保证多屏场景下的前台状态一致。 - 如果当前
Display上没有合适的恢复对象,还会回退到聚焦RootTask,必要时兜底恢复Home。
因此这一层解决的不是“调哪个生命周期”,而是“当前系统前台应该收敛到哪里”。
2.4.2 向下收敛:Task 负责找目标,TaskFragment 负责做决策
RootWindowContainer 把恢复请求下发以后,流程会继续进入 Task.resumeTopActivityUncheckedLocked() 和 Task.resumeTopActivityInnerLocked()
这两层可以压成一句话理解:
Task先从任务树里找到真正应该承担恢复动作的顶部候选者。mInResumeTopActivity负责拦住递归重入,避免同一个Task在恢复过程中被再次拉进来。Task.resumeTopActivityInnerLocked()会把执行权继续交给目标ActivityRecord所在的TaskFragment。
真正决定“现在能不能恢复”的,是 TaskFragment.resumeTopActivity()
- 它先检查
next是否有效,以及当前是否仍有未完成的pause。 - 如果目标页面已经处于合适状态,也可能只补可见性、转场和状态收敛,而不是重复发起
resume。 - 如果旧前台仍在运行,通常要先把旧页面推进到
pause,再继续新页面恢复。 - 如果系统已经确定目标进程尚未就绪,还可能利用等待
pause的空档提前并行准备进程。
也就是说,Task 负责“选谁”,TaskFragment 负责“怎么切”。
2.4.3 最终分叉:进程已就绪就发事务,未就绪就切进程启动链
当 TaskFragment 完成前台状态裁决以后,后续只剩两条主分支。
第一条,目标进程已经存在并且附着:
- 服务端不会直接调用应用侧
onResume()。 - 它会通过
ClientTransaction把ActivityResultItem、NewIntentItem、ResumeActivityItem等事务投递给应用进程。 - 真正执行生命周期回调的是应用进程里的
ActivityThread,而不是system_server直接跨进程调用。
第二条,目标进程还没有准备好:
TaskFragment不直接创建进程,而是交给 ActivityTaskSupervisor.startSpecificActivity() 做最后一次热启动确认。- 如果仍然没有可用进程,再进入 ActivityTaskManagerService.startProcessAsync()
startProcessAsync()只是把请求异步转交给AMS,并不在ATMS里直接fork进程。
这里最关键的理解是:恢复链和进程链是协作关系,不是混在一个方法里同步硬做到底。
2.4.4 总结
把这一段压成最核心的 6 步,就是:
ActivityStarter.startActivityInner()先把目标ActivityRecord放入正确的任务结构。RootWindowContainer.resumeFocusedTasksTopActivities()从全局窗口结构里挑出应该推进的前台对象。Task沿任务树继续下钻,定位真正承担恢复动作的叶子容器。TaskFragment.resumeTopActivity()统一处理旧页pause、新页切换、可见性、转场和进程状态判断。- 进程已就绪时,通过
ClientTransaction把生命周期事务发给应用进程。 - 进程未就绪时,通过
ActivityTaskSupervisor -> ATMS -> AMS切入进程启动链。
3. 关键设计点
3.1 ATMS 做入口收口,ActivityStarter 做启动裁决,RootWindowContainer 做恢复分发
这一阶段最大的设计特点,不是某个单一方法有多复杂,而是职责切分很清楚:
- ActivityTaskManagerService:负责入口合法性、调用身份、用户校验。
- ActivityStarter:负责
Task复用 / 新建、Flag 落地、前台切换。 - RootWindowContainer:往下分发恢复请求。
- Task 和 TaskFragment:负责真正的顶部选择、pause / resume 协调、事务投递。
3.2 先把服务端结构摆正,再推进客户端生命周期
这是整个 AMS 处理阶段最核心的设计思想。
顺序一定是:
executeRequest()先把请求具象化为ActivityRecordstartActivityInner()再决定落到哪个TaskmoveToFront()/startActivityLocked()把容器层级摆正resumeFocusedTasksTopActivities()再去恢复真正的顶部 Activity- 必要时才通过
ClientTransaction驱动客户端onResume()
也就是说:
- 先有 system_server 里的任务结构
- 后有客户端进程里的生命周期回调
3.3 冷启动优化不是“更快 fork”,而是“并行化 pause 和进程启动”
很多人以为冷启动优化只发生在 AMS 或 Zygote,实际上在本篇涉及的 TaskFragment.resumeTopActivity() 里,Framework 就已经做了一层关键优化:
- 旧前台页面还在 pause
- 新页面还没真正 resume
- 但只要系统已经知道目标进程没起来,就可以提前
startProcessAsync()
这样做的效果是:
- 旧页面 pause 和新进程启动并行进行
- 缩短用户感知到的总启动时长
冷启动优化从哪一层开始体现?
- 不只是
ProcessList.startProcessLocked() - 在
TaskFragment.resumeTopActivity()这里,Framework 就已经开始并行化调度了
3.4 Android 14 的“恢复顶部 Activity”已经不是单栈模型
Android 14 源码里至少有这些复杂性:
- 多 Display
- 多
TaskDisplayArea RootTask/ 叶子TaskTaskFragment- 多窗口 / 嵌入式 Activity
- 可见性、焦点、转场、配置更新交叉影响
所以当前版本里,“恢复顶部 Activity”的真实含义是:
- 找出每个显示区域真正应该在前台的容器
- 找出该容器里可恢复的顶部 Activity / TaskFragment
- 协调 pause、visible、config、transition、process、transaction
4. 总结
Launcher 点击桌面图标后,system_server 这一阶段完成的并不是“立刻把 Activity 跑起来”,而是先把这次启动请求裁决成一套完整的服务端执行方案:
- 在
ATMS入口完成调用方、用户和参数收口 - 在
ActivityStarter中把请求具象化为ActivityRecord - 在
startActivityInner()中决定复用旧Task还是新建Task - 把目标
ActivityRecord正式挂入任务结构,并根据 Flag / launchMode / BAL / Dream / 转场条件修正前台行为 - 通过
RootWindowContainer -> Task -> TaskFragment推进顶部恢复链 - 若进程已存在,则通过
ClientTransaction把生命周期事务发给应用进程 - 若进程不存在,则在
startProcessAsync()处异步切换到 AMS 进程启动链
到 startProcessAsync() 为止,ATMS / WM 的职责基本完成;下一篇再继续看 AMS 如何把这个请求送到 ProcessList、Zygote,最终创建应用进程。