Android 应用从桌面点击之后 Activity 是怎么启动的?

32 阅读7分钟

一、Activity 启动核心流程(基于 startActivity 触发)

1.1 启动入口:跨进程调用的发起

Launcher 启动应用时,通过 Context#startActivity 触发,核心调用如下跨进程方法,将启动参数传递至系统服务端:

ActivityTaskManager.getService().startActivity(whoThread,
        who.getBasePackageName(), who.getAttributionTag(), intent,
        intent.resolveTypeIfNeeded(who.getContentResolver()), token,
        target != null ? target.mEmbeddedID : null, requestCode, 0, null, options)   

1.2 系统服务层转发:AMS 到 ActivityTaskManagerService

1.2.1 AMS 的转发逻辑

AMS(ActivityManagerService)的 startActivity 方法,实际将请求转发至 ActivityTaskManagerService(以下简称ATMS):

@Deprecated
@Override
public int startActivity(IApplicationThread caller, String callingPackage,
        Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
        int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {
    return mActivityTaskManager.startActivity(caller, callingPackage, null, intent,
            resolvedType, resultTo, resultWho, requestCode, startFlags, profilerInfo, bOptions);
}
public ActivityTaskManagerService mActivityTaskManager;

1.2.2 ATMS 的参数封装

ATMS 接收请求后,通过多轮 startActivityAsUser 重载方法做参数补充与校验(如用户 ID 校验、权限检查),最终通过 Builder 模式封装启动信息:

private int startActivityAsUser(...) {
    // 校验包名、用户 ID 等基础信息
    assertPackageMatchesCallingUid(callingPackage);
    userId = getActivityStartController().checkTargetUser(...);
    // 生成 ActivityStarter 对象,封装启动参数
    return getActivityStartController().obtainStarter(intent, "startActivityAsUser")
            .setCaller(caller)
            .setCallingPackage(callingPackage)
            .setResolvedType(resolvedType)
            .setResultTo(resultTo)
            .setRequestCode(requestCode)
            .setUserId(userId)
            .execute(); // 执行启动逻辑
}

1.3 启动逻辑处理核心:ActivityStarter

1.3.1 ActivityStarter#execute:启动入口

初始化参数后,通过同步锁保证线程安全,核心逻辑委托给 executeRequest

int execute() {
    try {
        ...
        synchronized (mService.mGlobalLock) {
            ...
            res = resolveToHeavyWeightSwitcherIfNeeded();
            if (res != START_SUCCESS) return res;
            ...
            res = executeRequest(mRequest);
            ...
            mSupervisor.getActivityMetricsLogger().notifyActivityLaunched(...);
            return getExternalResult(...);
        }
    } finally {
        onExecutionComplete();
    }
}

1.3.2 ActivityStarter#executeRequest:权限与参数校验

前置校验阶段,完成以下核心操作:

  1. 参数合法性校验:检查启动原因、意图组件(intent.getComponent())、Activity 信息(aInfo)是否存在,若不合法返回对应错误码(如 START_INTENT_NOT_RESOLVED);

  2. 权限与场景校验

    1. 后台启动权限检查(shouldAbortBackgroundActivityStart);
    2. 语音启动适配(校验 Activity 是否支持语音场景);
    3. 意图防火墙与权限策略校验(mIntentFirewall.checkStartActivity、checkStartActivity);
  3. 结果返回处理:若校验失败,向结果接收者(resultRecord)发送取消结果,并终止启动;

  4. 特殊场景处理

    1. 权限审核场景:跳转权限审核页面,重新构造启动意图;
    2. 辅助 Activity 场景:基于 rInfo.auxiliaryInfo 重新生成启动意图;
  5. 创建 ActivityRecord:封装 Activity 元数据(调用者、包名、意图等),最终调用 startActivityUnchecked 进入无校验启动阶段。

1.3.3 ActivityStarter#startActivityUnchecked:校验后启动

控制窗口布局流程,委托 startActivityInner 处理核心逻辑:

private int startActivityUnchecked(...) {
    int result = START_CANCELED;
    try {
        mService.deferWindowLayout(); // 延迟窗口布局
        Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "startActivityInner");
        result = startActivityInner(...); // 核心启动逻辑
    } finally {
        Trace.traceEnd(...);
        startedActivityStack = handleStartResult(r, result);
        mService.continueWindowLayout(); // 恢复窗口布局
    }
    postStartActivityProcessing(...); // 启动后后续处理
    return result;
}

在 Android 系统的窗口管理框架中,WindowSurfacePlacer 是负责窗口布局计算和 Surface 分配的关键组件,而 deferLayout() 方法的主要作用是延迟执行窗口布局计算

具体来说,deferLayout() 的工作机制和目的如下:

  1. 布局计算的延迟触发窗口系统中,窗口的位置、大小、层级等属性变化(如窗口打开 / 关闭、分辨率调整、旋转屏幕等)会频繁触发布局重新计算。deferLayout() 会将这些布局请求暂时缓存起来,而不是立即执行计算,避免短时间内多次重复计算导致的性能损耗。
  2. 批量处理布局请求当系统调用 deferLayout() 后,会等待合适的时机(通常是在当前消息循环结束前,或通过 Choreographer 同步到下一次屏幕刷新时)一次性处理所有缓存的布局请求,实现批量布局更新,减少不必要的中间计算步骤。
  3. 避免布局抖动如果多个窗口操作同时触发布局请求,不加以控制可能会导致布局计算交错执行,出现视觉上的抖动或闪烁。deferLayout() 通过延迟合并处理,保证布局计算的原子性和一致性。

简单来说,deferLayout() 是一种性能优化手段,通过「延迟 + 批量」的方式处理窗口布局请求,平衡系统响应速度和资源消耗,确保窗口显示的流畅性。

1.3.4 ActivityStarter#startActivityInner:任务栈与启动模式处理

解读 launchMode(singleTop/singleTask 等),确定 Activity 所属任务栈(新建或加入已有栈,含栈内 Activity 回收逻辑),调用 mTargetStack.startActivityLocked 完成栈内定位:

mTargetStack.startActivityLocked(mStartActivity, topStack.getTopNonFinishingActivity(),
        newTask, mKeepCurTransition, mOptions);

紧接着根据 目标栈焦点状态 分情况处理:

分支 1:目标栈无法获取焦点(仅确保可见)

触发条件(满足任一):

  • 目标栈顶 Activity 不可聚焦(如 PIP 模式、后台状态);
  • 目标栈顶有覆盖层 Activity(如对话框),且新启动 Activity 非覆盖层。

执行逻辑

  1. mTargetStack.ensureActivitiesVisible(...):触发 Activity onStart 生命周期,确保可见但不执行 onResume;
  2. mTargetStack.getDisplay().mDisplayContent.executeAppTransition():手动触发过渡动画(因无 onResume 触发)。
分支 2:目标栈可获取焦点(激活并获取焦点)

执行逻辑

  1. 移至前台:若目标栈非显示焦点栈,调用 mTargetStack.moveToFront("startActivityInner") 移到前台;
  2. 触发 Resume:调用 mRootWindowContainer.resumeFocusedStacksTopActivities(...),激活栈顶 Activity 并执行 onResume。

1.3.5 RootWindowContainer#resumeFocusedStacksTopActivities:全局栈 Resume 调度

顶层容器 → DisplayContent(显示器)→ TaskDisplayArea(任务区域)→ ActivityStack(栈)→ TaskRecord(任务)→ ActivityRecord(Activity) 层级,优先处理目标栈 Resume,再遍历所有显示器 / 任务区域处理其他栈:

  • 核心调用:ActivityStack#resumeTopActivityUncheckedLocked。

1.3.6 ActivityStack#resumeTopActivityInnerLocked:Resume 核心逻辑

  1. 前置校验:确保系统与栈状态合法;

  2. 暂停前序 Activity

    1. 因 Android 同一时间仅一个 Activity 可处于 RESUMED 状态,需暂停当前 mResumedActivity;
    2. 调用 startPausingLocked 发送暂停事务(触发 Activity onPause);
    3. 若需暂停,提前准备新应用进程(避免后续启动阻塞);
  3. Resume 分支处理

    1. 场景 A:Activity 已附加进程:直接发送 RESUME 指令,触发 onResume;

    2. 场景 B:Activity 未附加进程

      • 标记启动状态(next.hasBeenLaunched = true);
      • 显示启动预览窗口(next.showStartingWindow(...));
      • 调用 mStackSupervisor.startSpecificActivity(...) 启动进程,进程启动后重新触发 Resume。

二、应用进程初始化流程(ActivityThread 核心)

2.1 进程入口:ActivityThread#main

新进程创建后,执行主线程初始化:

public static void main(String[] args) {
    // 1. 初始化主线程 Looper
    Looper.prepareMainLooper();
    
    // 2. 创建 ActivityThread 实例并绑定进程
    ActivityThread thread = new ActivityThread();
    thread.attach(false, startSeq);
    
    // 3. 启动 Looper 消息循环
    Looper.loop();
}

2.2 ActivityThread#attach:进程绑定与配置

2.2.1 非系统进程处理(应用进程)

  1. 基础配置:设置应用名、绑定 Application 对象(RuntimeInit.setApplicationObject);
  2. 绑定 AMS:获取 AMS 代理(IActivityManager mgr),调用 mgr.attachApplication(mAppThread, startSeq) 完成进程绑定;
  3. 资源管理:添加 GC 监听(内存紧张时调用 releaseSomeActivities 清理资源);
  4. 配置 回调:添加 ViewRootImpl 配置变化回调(处理屏幕旋转、语言切换等)。

2.2.2 系统进程处理

初始化 Instrumentation、系统上下文与 InitialApplication,并调用 mInitialApplication.onCreate()。

2.2.3 IApplicationThread.Stub 核心接口

IApplicationThread 是应用进程与 AMS 通信的 Binder 接口,核心方法如下(多数通过 ActivityThread.H Handler 处理消息):

方法名核心作用
bindApplication绑定 Application 实例,ContentProvider 初始化早于 Application.onCreate 的依据(installContentProviders 早于 mInstrumentation.callApplicationOnCreate(app))
scheduleTransaction调度组件生命周期事务(如 onCreate/onResume/onDestroy),通过 ClientTransaction 按序执行
scheduleLowMemory通知应用内存不足
scheduleInstallProvider调度 ContentProvider 安装
requestAssistContextExtras语音助手相关数据交互

2.3 AMS#attachApplication:进程绑定核心逻辑

mgr.attachApplication(mAppThread, startSeq) 是 AMS 与应用进程绑定的核心,分 10 步执行:

  1. 进程记录匹配与校验

    1. 按 PID 从 mPidsSelfLocked 查找 ProcessRecord(进程元数据);
    2. 校验 startUid/startSeq,不匹配则清理旧记录;
    3. 未找到时从 mProcessList.mPendingStarts 按 startSeq 匹配;
  2. 异常处理

    1. 无合法 ProcessRecord 时,杀死独立进程(killProcessQuiet)或让匿名线程退出;
  3. 清理旧进程关联

    1. 若 ProcessRecord 已绑定其他进程,调用 handleAppDiedLocked 清理旧资源;
  4. 绑定进程死亡监听

    1. 为 IApplicationThread 注册 AppDeathRecipient 回调,进程意外死亡时触发清理;
  5. 初始化进程状态

    1. 重置 ProcessRecord 关键属性(如 app、thread、pid),影响进程调度与内存管理;
  6. 准备 ContentProvider

    1. 生成进程需加载的 ContentProvider 列表(generateApplicationProvidersLocked);
    2. 设置 Provider 发布超时消息(CONTENT_PROVIDER_PUBLISH_TIMEOUT_MSG);
  7. 配置调试 / Profiling/Instrumentation

    1. 调试模式:设置 testMode(DEBUG_WAIT/DEBUG_ON);
    2. Profiling:构建 ProfilerInfo 配置性能分析;
    3. Instrumentation:关联 ActiveInstrumentation 支持测试;
  8. 绑定应用进程

    1. 调用 IApplicationThread.bindApplication,通知应用进程执行:

      • 创建 Application 实例;
      • 安装并发布 ContentProvider;
      • 初始化应用上下文与兼容性配置;
  9. 触发后续组件启动

    1. Activity:调用 mAtmInternal.attachApplication 启动栈顶待显示 Activity;
    2. Service:调用 mServices.attachApplicationLocked 启动已注册 Service;
    3. 广播:调用 sendPendingBroadcastsLocked 分发待处理广播;
    4. 备份代理:调用 scheduleCreateBackupAgent 启动 BackupAgent;
  10. 收尾与统计

    1. 移除待启动进程标记(mPersistentStartingProcesses/mProcessesOnHold);
    2. 更新进程 OOM 优先级(updateOomAdjLocked);
    3. 记录进程启动时间(FrameworkStatsLog.write)。

附网上常见的图,总结一下就是 Launcher 向 ams 发送启动 Activity 的请求,ams 判断是否有进程存在,如果没有进程则会 fork 进程,fork 进程后反射调用 ActivityThread 的 main 方法,main 方法中会调用 attachapplication,这个方法走进 ams 里面,ams 处理完逻辑后通过发送 bind_application 指令启动和 realStartActivityLocked 来触发 Application 的相关生命周期和启动 Activity。