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

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

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

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


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

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

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

ini 复制代码
// 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 决策流程图

关键逻辑伪代码

scss 复制代码
// 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

arduino 复制代码
// 文件: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 方法的秘密

java 复制代码
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 启动全流程

核心代码模拟 (ActivityThread.handleLaunchActivity)

scss 复制代码
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,调用 startActivity Binder (to AMS)
决策 AMS 检查进程,决定 Fork 或复用 内存/锁
孵化 Zygote Fork 新进程,设置 UID/GID Socket (from AMS)
绑定 ActivityThread 调用 attach,注册到 AMS Binder (to AMS)
调度 AMS 发送 scheduleLaunchActivity Binder (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 的舞蹈》

相关推荐
饭小猿人5 小时前
Android 腾讯X5WebView如何禁止系统自带剪切板和自定义剪切板视图
android·java
_李小白5 小时前
【android opencv学习笔记】Day 8: remap(像素位置重映射)
android·opencv·学习
美狐美颜SDK开放平台5 小时前
多场景美颜SDK解决方案:直播APP(iOS/安卓)开发接入详解
android·人工智能·ios·音视频·美颜sdk·第三方美颜sdk·短视频美颜sdk
嗷o嗷o5 小时前
Android BLE 里,MTU、分包和长数据发送到底该怎么处理
android
Gary Studio7 小时前
Android AIDL HAL工程结构示例
android
y = xⁿ8 小时前
MySQL八股知识合集
android·mysql·adb
andr_gale8 小时前
04_rc文件语法规则
android·framework·aosp
祖国的好青年9 小时前
VS Code 搭建 React Native 开发环境(Windows 实战指南)
android·windows·react native·react.js
黄林晴10 小时前
警惕!AGP 9.2 别只改版本号,R8 规则与构建链路全线收紧
android·gradle