Skip to content

Android App 进程启动全流程 (Zygote → ActivityThread → 首帧) ​

回到总览:性能、测试、排障
相关模块:应用启动性能优化实战(拓扑排序、异步初始化与首帧治理) · Android View 绘制流程、Choreographer 与 VSync 帧同步

一句话定义 ​

Android App 启动全流程是指从用户点击桌面 App 图标起,经过 AMS 向 Zygote 申请 fork() 孵化应用进程、加载 ART 虚拟机与 Class、调用 ActivityThread.main() 开启主线程 Looper,到执行 Application.onCreate() 并最终由 ViewRootImpl 完成首帧(First Frame)绘制上屏的完整生命全链路。

代码索引 ​

计划补齐的实验:

  • labs/android/host/topics/performance/startup-tracing-demo/ — 冷启动耗时打点、Trace.beginSection 分析与首帧回调监听

为什么需要 ​

  • 为什么 Android 应用进程的创建必须通过 Zygote 进行 fork,而不是像普通 Linux 进程那样 execve?
    • 一句话答:Zygote 在系统启动时已预加载了 Android 框架公用的类(如 Activity、View)和资源,通过 fork() 创建子进程可以实现 Copy-on-Write(写时复制),极大地缩短了新进程的启动耗时并节省了物理内存。
  • ContentProvider 和 Application 的 onCreate() 谁先执行?为什么?
    • 一句话答:ContentProvider 先执行;在 ActivityThread.handleBindApplication 流程中,实例化 Application 后会立刻初始化并执行所有 Provider 的 onCreate(),之后才调用 Application.onCreate()。
  • 启动耗时分析中 TTID (Time To Initial Display) 应该以哪个节点为准?
    • 一句话答:不能以 Activity 的 onResume() 为准,必须以 ViewRootImpl 收到 VSync 脉冲完成首帧绘制(onDraw / performTraversals)上屏为准。

底层机制 ​

1. App 冷启动全链路六大阶段 ​

图注补充:App: App 进程 (ActivityThread);AMS: 1. 点击图标, 发起 startActivity IPC;Zygote: 2. 通过 Socket 发送 fork 进程请求;App: 3. fork() 孵化新子进程 (继承预加载类与 ART);App: 4. 反射调用 ActivityThread.main(), 开启 Looper;App: 6. bindApplication -> 执行 ContentProvider -> Application.onCreate;App: 7. 调度 Activity.onCreate -> onStart -> onResume;VRImpl: 8. WindowManager.addView -> 触发 performTraversals 首帧绘制上屏。

各阶段关键节点详解 ​

  1. SystemServer 响应阶段:点击图标,Launcher 进程通过 Binder 通知 AMS 启动目标 Activity。AMS 检查该进程是否存在,若不存在则向 Zygote 进程发 Socket 通信。
  2. Zygote 孵化阶段:Zygote 收到 Socket 请求后,调用 fork() 孵化出全新的 App 进程。此时子进程直接共享 Zygote 已预加载的 Java 核心类库与 ART 堆空间。
  3. ActivityThread.main() 入口:子进程反射调用 ActivityThread.main():
    • 实例化 Looper.prepareMainLooper() 建立主线程消息循环。
    • 调用 ActivityThread.attach(false) 向 AMS 注册应用进程。
  4. Application 绑节点:AMS 收到注册后通过 Binder 回调 ActivityThread.H 发送 BIND_APPLICATION 消息:
    • 创建 LoadedApk 与 ClassLoader。
    • 实例化 Application 对象。
    • 执行所有已注册 ContentProvider 的 onCreate()(注意:ContentProvider 优先于 Application.onCreate 执行!)。
    • 执行 Application.onCreate()。
  5. Activity 启动与首帧渲染:
    • AMS 发送 EXECUTE_TRANSACTION 消息,驱动 Activity.onCreate() -> onStart() -> onResume()。
    • 在 onResume() 之后,WindowManagerImpl.addView() 实例化 ViewRootImpl。
    • ViewRootImpl 收到 Choreographer 的 VSync 脉冲,触发首次 performTraversals()(measure/layout/draw)。
    • SurfaceFlinger 合成帧数据,系统将闪屏(SplashWindow)替换为真实的 App 页面。

Android / Flutter / Web / Backend 对照 ​

阶段Android 原生Flutter 嵌入Web 浏览器
进程初始化Zygote fork() 秒级孵化依赖宿主 Android 进程浏览器 Renderer 进程创建
引擎预热ART 虚拟机预加载FlutterEngine 初始化、加载 libapp.soV8 引擎解析 JS Bundle
首帧指标TTID (Time To Initial Display)Flutter First Frame 回调FCP (First Contentful Paint)
容易踩坑点ContentProvider 被三方 SDK 滥用耗时FlutterEngine 未提前预热/双引擎开销JS 文件体积过大阻断 HTML 解析

常见场景 ​

1. 正确监听“首帧渲染完成” (Time To Initial Display, TTID) ​

不能以 Activity.onResume() 作为启动结束节点,因为此时 View 尚未完成测量和绘制上屏。

正确打点方式:

java
public class MainActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 监听 ViewRootImpl 首帧绘制完成
        getWindow().getDecorView().getViewTreeObserver().addOnDrawListener(new ViewTreeObserver.OnDrawListener() {
            private boolean mHandled = false;
            @Override
            public void onDraw() {
                if (!mHandled) {
                    mHandled = true;
                    // 在此上报首帧渲染耗时 TTID!
                    Log.i("Startup", "Time To Initial Display (TTID) Completed!");
                }
            }
        });
    }
}

常见误配、事故后果与排障 ​

1. 事故:三方 SDK 在 ContentProvider 中隐式耗时 ​

  • 误配原因:许多三方 SDK(如 WorkManager、Firebase、各种统计 SDK)为了实现“无侵入初始化”,在 AndroidManifest.xml 中注册 ContentProvider,并在其 onCreate() 中做耗时初始化。
  • 后果:ContentProvider 的 onCreate() 在 Application.onCreate() 之前执行!所有 Provider 的耗时都会直接拉长冷启动时间,导致启动优化的 Application 改造收效甚微。
  • 排障与修法:使用 Android App Startup 库统一收口管理,或在 Manifest 中禁用 Provider 自动初始化,改为手动延迟初始化。

与相近概念对比 ​

启动类型是否存在进程是否重新创建 Application平均耗时
冷启动 (Cold Start)否是最慢(完整经历 Zygote fork 到首帧)
温启动 (Warm Start)是否 (已存在)中等(进程在,但 Activity 被回收重建)
热启动 (Hot Start)是否最快(Activity 仅切到前台 onResume)

对应实验 ​

计划补齐的实验:

  • labs/android/host/topics/performance/startup-tracing-demo/ — 冷启动耗时打点、Trace.beginSection 分析与首帧回调监听

复习检查题 ​

  1. ContentProvider 的 onCreate() 与 Application 的 onCreate() 谁先执行?

    答:ContentProvider 的 onCreate() 先执行。在 ActivityThread.handleBindApplication() 流程中,系统先实例化 Application,接着立刻初始化并执行所有 ContentProvider 的 onCreate(),最后才调用 Application.onCreate()。

  2. 为什么 Android 应用进程 fork 自 Zygote 能够显著加快进程启动速度?

    答:Zygote 在 Android 系统启动时已经初始化了 ART 虚拟机,并提前加载了大多数常用的 Android 框架类(如 View、Activity、Resources 等)。当 fork() 产生新进程时,子进程直接继承了 Zygote 的内存空间与预加载资源(通过 Linux 的 Copy-on-Write 机制共享物理内存),避免了重新启动虚拟机和加载基础类的巨大开销。

速记 ​

  • 六步链路:AMS 调度 → Zygote fork → ActivityThread → ContentProvider → Application → 首帧绘制。
  • 陷阱提示:ContentProvider 优先于 Application.onCreate 执行,严禁在 Provider 中做耗时操作。
  • 指标核心:TTID (Time To Initial Display) 以 ViewRootImpl 首帧绘制为准,非 onResume。

站点构建时间:2026/8/24 23:43:17