第四篇:从点击到显示——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 的舞蹈》

相关推荐
ke_csdn2 小时前
安卓的视频通讯
android·音视频
范特西林2 小时前
第二篇:Java 世界的“创世神”:Zygote 如何一秒孵化一个 App?
android
范特西林2 小时前
第三篇:SystemServer——Android 框架层的大脑
android
robotx2 小时前
aosp单编单刷framework模块以及恢复remount
android
Jomurphys2 小时前
Compose 自定义 - 处理交互 Interaction
android·compose
nbsaas-boot2 小时前
SQL JOIN 图解说明
android·数据库·sql
Albert Tan2 小时前
Oracle EBS PO 报错 -- 非买手
android·数据库·oracle
00后程序员张2 小时前
iOS 应用的 HTTPS 连接端口在网络抓包调试中有什么作用
android·网络·ios·小程序·https·uni-app·iphone
m0_738120722 小时前
网络安全编程——PHP基础Session详细讲解
android·网络·windows·安全·web安全·php