第四篇:从点击到显示——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 调用 AMS 的 startActivityAsUser 方法。

此时状态: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 的舞蹈》

相关推荐
芝麻别开门7 小时前
gst-shark安卓编译使用方法
android·gstreamer·gst-shark
2501_9159214311 小时前
iOS应用耗电测试:使用Energy Log、Keymob和Instruments进行性能监控与能源消耗分析
android·ios·小程序·https·uni-app·iphone·webview
蒲公英内测分发11 小时前
热成像仪交付客户后,Android 配套 App 不上架,怎么持续下载安装和更新
android·机器人·项目管理·智能家居·智能硬件
脚踏实地,坚持不懈!12 小时前
Android 常见性能问题总结
android
高旭的旭13 小时前
图解 Android 系统架构:从 App、Framework、HAL 到 Linux 驱动
android·linux·系统架构
hunterandroid13 小时前
Navigation Component 多模块导航与深度链接实战
android
快乐非自愿13 小时前
低代码落地费控系统:从报销流程到业财闭环的实操方案
android·低代码·rxjava·ai低代码
摇滚侠14 小时前
《Java并发编程之美》第 2 章 并发编程的其他基础知识 阅读笔记 3
android·java·笔记
YB137514 小时前
Jetpack Compose 动画updateTransition
android
用户20187928316714 小时前
给主厨装一台打卡机:一文搞懂 Android APM 性能监控原理
android