第四篇:从点击到显示——App 启动与 Activity 生命周期全追踪

2 阅读5分钟

当你手指轻触桌面上的微信图标,屏幕瞬间亮起熟悉的界面。这看似简单的“点击”,背后却是一场跨越多个进程、涉及数十次 Binder 通信的精密接力赛。

AMS(ActivityManagerService) 是总指挥,Zygote 是兵工厂,Binder 是传令兵,而 ActivityThread 则是前线的执行者。

本篇我们将像侦探一样,追踪从“点击图标”到 onResume() 被调用的完整链路,揭开 Android 应用启动的神秘面纱。


第一步:Launcher 发起冲锋 —— 请求启动

一切始于 Launcher(桌面应用)。当你点击图标时,Launcher 并不知道微信的具体代码在哪里,它只知道一个 Intent

代码位置packages/apps/Launcher3/src/com/android/launcher3/Launcher.java

// Launcher 中的点击事件处理
public void onClick(View v) {
    Object tag = v.getTag();
    if (tag instanceof ShortcutInfo) {
        ShortcutInfo info = (ShortcutInfo) tag;
        Intent intent = info.intent;
        
        // 【关键】启动 Activity
        // 这里最终会调用 startActivitySafely -> startActivity
        startActivity(intent); 
    }
}

底层动作
startActivity 最终通过 Binder 调用 AMSstartActivityAsUser 方法。

此时状态:Launcher 进程挂起(等待 AMS 裁决),控制权移交系统服务。


第二步:AMS 的决策 —— 寻找或创建进程

AMS 收到请求后,开始进行复杂的逻辑判断。

代码位置frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

AMS 决策流程图

tongyi-mermaid-2026-03-11-233555.png

关键逻辑伪代码

// AMS 内部简化逻辑
final int startActivityAsUser(...) {
    // 1. 权限检查 (SELinux, Manifest 权限)
    enforceNotIsolatedCaller("startActivity");
    
    // 2. 解析 ResolveInfo (找到具体的 Activity 类名)
    ResolveInfo rInfo = mAppIntents.resolveIntent(intent, ...);
    
    // 3. 获取或创建进程记录 (ProcessRecord)
    ProcessRecord targetApp = getProcessRecordLocked(targetPackage, targetUid);
    
    if (targetApp == null || targetApp.thread == null) {
        // 【核心】进程不存在,需要启动新进程
        startProcessLocked(targetPackage, ...); 
    } else {
        // 进程已存在,直接调度
        targetApp.thread.scheduleLaunchActivity(...);
    }
    
    return START_SUCCESS;
}

第三步:Zygote 再次登场 —— 孵化新进程

如果目标 App 未运行,AMS 会通过 Socket 向 Zygote 发送指令。

通信协议
AMS 写入 Socket 的数据包包含:uid, gid, gids, debugFlags, runtimeFlags, mountExternal, seInfo, niceName, fdsToClose, fdsToIgnore, isTopApp, disabledCompatChanges, pkgDataInfoMap, whitelistedDataInfoMap, bindMountAppDataDirs, bindMountAppStorageDirs, args[] (其中包含 --activity-thread 等参数)。

Zygote 响应
Zygote 收到后,执行 forkAndSpecialize,生成新进程。新进程入口是 ActivityThread.main


第四步:App 进程的自我修养 —— ActivityThread.main

新进程诞生后,它首先是一个普通的 Java 进程,还没有“Application”的概念。它需要自己初始化。

代码位置frameworks/base/core/java/android/app/ActivityThread.java

// 文件:frameworks/base/core/java/android/app/ActivityThread.java
public static void main(String[] args) {
    // 1. 初始化主线程 Looper
    Looper.prepareMainLooper();

    // 2. 实例化 ActivityThread (当前进程的代表)
    ActivityThread thread = new ActivityThread();
    
    // 3. 【关键】绑定 Application
    // 调用 AMS 的 attachApplication,注册自己
    thread.attach(false, initialPid); 

    // 4. 进入消息循环
    Looper.loop();
}

attach 方法的秘密

private void attach(boolean system, long startSeq) {
    // 获取 AMS 的代理对象 (IActivityManager)
    final IActivityManager mgr = ActivityManager.getService();
    
    try {
        // 告诉 AMS:“我启动了,这是我的 Binder 代理”
        // AMS 会保存这个代理,以便后续直接回调我们
        mgr.attachApplication(mAppThread, startSeq); 
    } catch (RemoteException ex) {
        throw ex.rethrowFromSystemServer();
    }
    
    // 等待 AMS 下发指令 (如 bindApplication, launchActivity)
    // 这些指令会通过 Binder 异步到达,放入 Handler 队列
}

Binder 魔法:此时,AMS 手中持有了新进程的 IApplicationThread 代理。以后 AMS 想启动 Activity,只需调用 appThread.scheduleLaunchActivity(),请求就会瞬间发送到新进程的 Binder 线程池,再转发给主线程 Handler。


第五步:生命周期的大合唱 —— 从 onCreate 到 onResume

AMS 确认进程就绪后,开始编排 Activity 的生命周期。这是一次典型的 RPC(远程过程调用) 舞蹈。

Activity 启动全流程

tongyi-mermaid-2026-03-11-233625.png

核心代码模拟 (ActivityThread.handleLaunchActivity)

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    // 1. 加载类
    Class<?> clazz = r.loadClass(mClassLoader);
    
    // 2. 实例化 Activity
    Activity activity = (Activity) clazz.newInstance();
    
    // 3. 创建 Context (包裹 Activity)
    ContextImpl appContext = createBaseContextForActivity(r, activity);
    
    // 4. 调用生命周期
    Instrumentation inst = mInstrumentation;
    
    // --- 用户代码执行点 ---
    activity.attach(appContext, ...);
    inst.callActivityOnCreate(activity, r.state); // 调用 onCreate
    activity.performStart();                      // 调用 onStart
    inst.callActivityOnResume(activity);          // 调用 onResume
    // ----------------------
    
    return activity;
}

关键点:所有的生命周期回调(onCreate, onStart, onResume)都是由 Instrumentation 类辅助调用的。这也是为什么我们可以用测试框架(如 Espresso)拦截这些回调的原因。


🔹 第六步:窗口绘制 —— 终于看到了界面

onResume 返回后,Activity 认为自己是“可见”的,但屏幕上可能还是黑的。真正的绘制由 WindowManagerService (WMS)SurfaceFlinger 完成。

  1. Activity 创建 PhoneWindow,设置 ContentView
  2. ViewRootImpl 请求 WMS 添加窗口 (addToDisplay)。
  3. WMS 分配一个 Surface (图形缓冲区)。
  4. App 进程 在 Surface 上绘制 UI (Canvas/OpenGL)。
  5. SurfaceFlinger 合成所有图层,输出到屏幕。

这个过程是异步的,通常在 onResume 之后的几帧内完成。


本篇小结

阶段关键角色核心动作通信方式
发起Launcher构建 Intent,调用 startActivityBinder (to AMS)
决策AMS检查进程,决定 Fork 或复用内存/锁
孵化ZygoteFork 新进程,设置 UID/GIDSocket (from AMS)
绑定ActivityThread调用 attach,注册到 AMSBinder (to AMS)
调度AMS发送 scheduleLaunchActivityBinder (to App)
执行ActivityThread反射创建 Activity,调用生命周期本地调用
回调ActivityThread通知 AMS "Resumed"Binder (to AMS)

技术洞察

  • 双向 Binder:AMS 持有 App 的代理(用于下发指令),App 持有 AMS 的代理(用于上报状态)。
  • Handler 机制:所有跨进程的生命周期回调,最终都变成了主线程 MessageQueue 中的一条消息,保证了串行执行。
  • Instrumentation:它是 Hook 生命周期的关键入口,也是单元测试的基石。

下篇预告

App 启动了,界面显示了。但如果你快速滑动列表,或者打开相机,系统如何保证不卡顿?

  • Choreographer 是如何同步 VSync 信号的?
  • View 的测量、布局、绘制 三部曲究竟发生了什么?
  • GPU 是如何介入渲染流程的?

下篇我们将深入 UI 渲染管线,揭秘 Android 流畅度背后的“帧”的秘密。
敬请期待:《60FPS 的秘密:UI 渲染管线与 Choreographer 的舞蹈》