Activity 启动流程(二)—— AMS 处理阶段

429 阅读12分钟

前言

启动流程梳理:

  1. Activity 启动流程(一)—— Launcher 阶段
  2. Activity 启动流程(二)—— AMS 处理阶段
  3. Activity 启动流程(三)—— 应用程序进程启动阶段
  4. Activity 启动流程(四)—— ActivityThread 初始化阶段
  5. Activity 启动流程(五)—— Activity 启动阶段
  6. 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 的核心启动逻辑
}

这一层的职责很明确:

  1. 做入口安全收口:校验调用包名、隔离进程、目标用户是否合法。
  2. 把原始启动参数整理成内部请求:例如 SafeActivityOptionsuserIdresultTo
  3. 将启动裁决委托给 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 在初始化阶段计算出的“是否推进前台恢复链”的总开关:满足可见、可前台、无特殊限制(如 launchTaskBehindavoidMoveToFront、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()
  • 它会通过 ClientTransactionActivityResultItemNewIntentItemResumeActivityItem 等事务投递给应用进程。
  • 真正执行生命周期回调的是应用进程里的 ActivityThread,而不是 system_server 直接跨进程调用。

第二条,目标进程还没有准备好:

  • TaskFragment 不直接创建进程,而是交给 ActivityTaskSupervisor.startSpecificActivity() 做最后一次热启动确认。
  • 如果仍然没有可用进程,再进入 ActivityTaskManagerService.startProcessAsync()
  • startProcessAsync() 只是把请求异步转交给 AMS,并不在 ATMS 里直接 fork 进程。

这里最关键的理解是:恢复链和进程链是协作关系,不是混在一个方法里同步硬做到底。

2.4.4 总结

把这一段压成最核心的 6 步,就是:

  1. ActivityStarter.startActivityInner() 先把目标 ActivityRecord 放入正确的任务结构。
  2. RootWindowContainer.resumeFocusedTasksTopActivities() 从全局窗口结构里挑出应该推进的前台对象。
  3. Task 沿任务树继续下钻,定位真正承担恢复动作的叶子容器。
  4. TaskFragment.resumeTopActivity() 统一处理旧页 pause、新页切换、可见性、转场和进程状态判断。
  5. 进程已就绪时,通过 ClientTransaction 把生命周期事务发给应用进程。
  6. 进程未就绪时,通过 ActivityTaskSupervisor -> ATMS -> AMS 切入进程启动链。

3. 关键设计点

3.1 ATMS 做入口收口,ActivityStarter 做启动裁决,RootWindowContainer 做恢复分发

这一阶段最大的设计特点,不是某个单一方法有多复杂,而是职责切分很清楚:

  • ActivityTaskManagerService:负责入口合法性、调用身份、用户校验。
  • ActivityStarter:负责 Task 复用 / 新建、Flag 落地、前台切换。
  • RootWindowContainer:往下分发恢复请求。
  • Task 和 TaskFragment:负责真正的顶部选择、pause / resume 协调、事务投递。

3.2 先把服务端结构摆正,再推进客户端生命周期

这是整个 AMS 处理阶段最核心的设计思想。

顺序一定是:

  1. executeRequest() 先把请求具象化为 ActivityRecord
  2. startActivityInner() 再决定落到哪个 Task
  3. moveToFront() / startActivityLocked() 把容器层级摆正
  4. resumeFocusedTasksTopActivities() 再去恢复真正的顶部 Activity
  5. 必要时才通过 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 / 叶子 Task
  • TaskFragment
  • 多窗口 / 嵌入式 Activity
  • 可见性、焦点、转场、配置更新交叉影响

所以当前版本里,“恢复顶部 Activity”的真实含义是:

  • 找出每个显示区域真正应该在前台的容器
  • 找出该容器里可恢复的顶部 Activity / TaskFragment
  • 协调 pause、visible、config、transition、process、transaction

4. 总结

Launcher 点击桌面图标后,system_server 这一阶段完成的并不是“立刻把 Activity 跑起来”,而是先把这次启动请求裁决成一套完整的服务端执行方案:

  1. ATMS 入口完成调用方、用户和参数收口
  2. ActivityStarter 中把请求具象化为 ActivityRecord
  3. startActivityInner() 中决定复用旧 Task 还是新建 Task
  4. 把目标 ActivityRecord 正式挂入任务结构,并根据 Flag / launchMode / BAL / Dream / 转场条件修正前台行为
  5. 通过 RootWindowContainer -> Task -> TaskFragment 推进顶部恢复链
  6. 若进程已存在,则通过 ClientTransaction 把生命周期事务发给应用进程
  7. 若进程不存在,则在 startProcessAsync() 处异步切换到 AMS 进程启动链

startProcessAsync() 为止,ATMS / WM 的职责基本完成;下一篇再继续看 AMS 如何把这个请求送到 ProcessListZygote,最终创建应用进程。