【碎片八股文 #004】Activity 是如何从点击图标到启动界面的?
一、面试题原文
面试官: 从用户点击桌面图标,到 Activity 界面显示出来,中间经历了哪些步骤?
候选人: 先启动应用,然后创建 Activity……具体流程不太清楚。
面试官心里想: 能说出 Launcher、AMS、Zygote fork、ApplicationThread 就算及格了。
二、常见误答
很多人只知道"点击图标会启动 Activity",但说不清楚:
- 谁负责启动 Activity?
- 应用进程是什么时候创建的?
- onCreate() 是谁调用的?
这些都是面试高频追问点。
三、正确理解
Activity 启动的核心角色
启动一个 Activity 涉及多个系统组件协作:
| 角色 | 职责 |
|---|---|
| Launcher | 桌面应用,响应点击事件 |
| ActivityManagerService (AMS) | 系统服务,管理所有 Activity 的生命周期 |
| Zygote | 孵化器进程,负责 fork 新的应用进程 |
| ApplicationThread | 应用进程的 Binder 接口,接收 AMS 的指令 |
| ActivityThread | 应用主线程,实际执行 Activity 创建 |
核心流程可以分为两个阶段:
- 进程启动阶段(如果应用进程不存在)
- Activity 创建阶段
四、完整启动流程
阶段一:进程启动(如果应用未运行)
1. Launcher 点击图标
↓
2. Launcher 通过 Binder 调用 AMS.startActivity()
↓
3. AMS 检查目标应用进程是否存在
↓
4. 进程不存在,AMS 请求 Zygote fork 新进程
↓
5. Zygote fork 出应用进程
↓
6. 新进程启动后,创建 ActivityThread(主线程)
↓
7. ActivityThread 通过 Binder 连接到 AMS
阶段二:Activity 创建
8. AMS 通过 Binder 通知 ApplicationThread 启动 Activity
↓
9. ApplicationThread 向 Handler 发送 LAUNCH_ACTIVITY 消息
↓
10. ActivityThread 处理消息,调用 performLaunchActivity()
↓
11. 通过反射创建 Activity 实例
↓
12. 创建 Application(如果未创建)
↓
13. 创建 ContextImpl 并关联到 Activity
↓
14. 调用 Activity.attach() 初始化 Window
↓
15. 调用 Activity.onCreate()
↓
16. 调用 Activity.onStart()
↓
17. 调用 Activity.onResume()
↓
18. WindowManager 将 DecorView 添加到窗口
↓
19. ViewRootImpl 开始绘制,界面显示
五、图解核心流程
跨进程通信示意图
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Launcher │ │ AMS │ │ Zygote │
│ (进程1) │ │ (系统进程) │ │ (孵化器) │
└──────┬───────┘ └──────┬───────┘ └──────┬───────┘
│ │ │
│ 1. startActivity() │ │
├─────── Binder ──────────→│ │
│ │ 2. 请求创建进程 │
│ ├────── Socket ───────────→│
│ │ │ 3. fork()
│ │ │
│ │ 4. 进程创建成功 │
│ │←────────────────────────┤
│ │ │
┌──────────────────────────────────────────────────────────────────┐
│ 新创建的应用进程 │
│ │
│ ActivityThread.main() │
│ ↓ │
│ 创建 ApplicationThread (Binder 对象) │
│ ↓ │
│ 通过 Binder 注册到 AMS │
│ ↓ │
│ 接收 AMS 的启动 Activity 指令 │
│ ↓ │
│ Handler 处理 LAUNCH_ACTIVITY 消息 │
│ ↓ │
│ performLaunchActivity() │
│ ↓ │
│ 反射创建 Activity 实例 │
│ ↓ │
│ 调用生命周期: onCreate() → onStart() → onResume() │
└──────────────────────────────────────────────────────────────────┘
六、关键技术点解析
1. 为什么要通过 Zygote fork 进程?
Zygote 是预加载进程,启动时已经加载了:
- 系统常用的类和资源
- 共享库(libc、libandroid_runtime)
fork 的好处:
- 通过写时复制(Copy-On-Write)共享内存,启动快
- 新进程继承 Zygote 的类加载器,不需要重新加载系统类
如果不用 Zygote: 每次启动应用都要重新加载系统库,非常慢。
2. AMS 和 ApplicationThread 的通信
这是典型的 双向 Binder 通信:
AMS (System Server 进程) ←─ Binder ─→ ApplicationThread (应用进程)
- AMS 持有 ApplicationThread 的 Binder 引用
- ApplicationThread 持有 AMS 的 Binder 引用
- 双方可以互相调用对方的方法
具体调用:
// AMS 通知应用启动 Activity
app.thread.scheduleLaunchActivity(...); // 跨进程调用
// ApplicationThread 接收指令
public void scheduleLaunchActivity(...) {
sendMessage(H.LAUNCH_ACTIVITY, r); // 发送到 Handler
}
3. 为什么要用 Handler 中转?
ApplicationThread 运行在 Binder 线程池,不是主线程。
但 Activity 的生命周期必须在 主线程 执行,所以通过 Handler 切换到主线程:
// Binder 线程
ApplicationThread.scheduleLaunchActivity()
↓
// 发送消息到主线程
Handler.sendMessage(LAUNCH_ACTIVITY)
↓
// 主线程处理
ActivityThread.handleLaunchActivity()
↓
// 调用生命周期
Activity.onCreate()
4. Activity 实例是如何创建的?
通过 反射 创建:
// ActivityThread.performLaunchActivity()
ClassLoader cl = appContext.getClassLoader();
Activity activity = (Activity) cl.loadClass(className).newInstance();
// 调用 attach() 初始化 Window
activity.attach(appContext, this, ...);
// 调用 onCreate()
instrumentation.callActivityOnCreate(activity, ...);
关键点: Activity 的构造函数不能有参数,因为是反射调用无参构造。
七、延伸提问
1. 如果应用进程已经存在,流程有什么不同?
跳过 进程启动阶段,直接从第 8 步开始:
AMS 通知已有进程的 ApplicationThread 启动新 Activity
↓
ActivityThread 创建 Activity 实例并执行生命周期
这就是为什么后续打开 Activity 比首次启动快。
2. 启动模式(launchMode)在哪里生效?
在 AMS 的 startActivity() 方法中。
AMS 会检查启动模式:
standard:每次创建新实例singleTop:栈顶复用,调用onNewIntent()singleTask:栈内复用,清除上面的 ActivitysingleInstance:独立任务栈
关键代码位置: ActivityStarter.startActivityUnchecked()
3. Application 是什么时候创建的?
在 第一个 Activity 启动前。
// ActivityThread.handleBindApplication()
Application app = instrumentation.newApplication(cl, appClass, appContext);
app.onCreate(); // 调用 Application.onCreate()
结论: Application.onCreate() 在任何 Activity 之前执行。
4. 为什么冷启动慢?
冷启动包含的步骤:
- Zygote fork 进程(耗时)
- 加载 Application 和 Activity 类(耗时)
- 执行 Application.onCreate()(可能有耗时操作)
- 执行 Activity.onCreate()(布局渲染耗时)
优化方向:
- 减少 Application.onCreate() 中的初始化
- 使用启动页占位
- 延迟加载非必要资源
八、记忆口诀
"点图标找 Launcher,Launcher 问 AMS;AMS 叫 Zygote,fork 进程快又稳;ApplicationThread 收指令,Handler 转主线程;反射创建 Activity,生命周期按序行。"
九、碎片笔记
核心关键词: Launcher、AMS、Zygote、ApplicationThread、ActivityThread、反射、Handler
重点记忆:
- Zygote 通过 fork 创建应用进程,利用 Copy-On-Write 提速
- AMS 通过 Binder 与 ApplicationThread 双向通信
- ApplicationThread 在 Binder 线程,通过 Handler 切换到主线程
- Activity 通过反射创建,所以构造函数不能有参数
实际应用:
- 优化冷启动:减少 Application.onCreate() 初始化
- 避免在 Activity.onCreate() 中做耗时操作
- 理解启动模式在 AMS 层生效,不是 Activity 自己控制
今天的碎片,帮你面试少挂一次。
下一篇预告: 【碎片八股文 #005】Hermes 引擎和 JSC 有什么区别?