8、Activity 生命周期管理:AMS如何调度onCreate/onStart/onResume/onPause/onStop/onDestroy
生命周期,说白了就是 Activity 从生到死的完整过程。很多开发者觉得这很简单——不就是那几个回调方法吗?嗯,表面上看确实如此。但如果你深入到 AMS 层面,会发现事情远没那么简单。
我个人习惯把生命周期调度比作一场精心编排的舞台剧。AMS 是导演,Activity 是演员,而各种 Binder 调用就是幕后工作人员传递的指令。导演说“该你上场了”,演员才开始表演。今天我们就来拆解一下,AMS 这位导演到底是怎么调度这六场戏的。
8.1 生命周期的“总开关”:LaunchActivityItem
先问一个问题:AMS 怎么通知 App 进程去执行 onCreate?
答案是通过 ApplicationThread。AMS 持有 App 进程的 ApplicationThread 代理,通过 Binder 跨进程调用,把生命周期事件“塞”过去。
具体来说,AMS 会构造一个 LaunchActivityItem,然后通过 ClientLifecycleManager 发送给 App 进程。App 进程的主线程 Handler(也就是 ActivityThread.H)收到这个消息后,开始执行真正的生命周期方法。
核心流程:
- AMS 调用
ActivityStackSupervisor.startSpecificActivityLocked() - 检查进程是否存在,不存在则先创建进程
- 通过
ClientLifecycleManager.scheduleTransaction()发送事务 - App 端
ActivityThread.TransactionExecutor.execute()执行
// AMS 端伪代码
void startSpecificActivityLocked(ActivityRecord r, ...) {
ProcessRecord app = mService.getProcessRecordLocked(r.processName, ...);
if (app != null && app.thread != null) {
// 直接调度生命周期
realStartActivityLocked(r, app, ...);
} else {
// 先启动进程
mService.startProcessLocked(r.processName, ...);
}
}
void realStartActivityLocked(ActivityRecord r, ProcessRecord app, ...) {
// 构造 LaunchActivityItem
LaunchActivityItem item = new LaunchActivityItem(...);
// 通过 ClientLifecycleManager 发送
ClientLifecycleManager.scheduleTransaction(app.thread, item);
}
我曾经在调试一个启动闪退问题时,发现 AMS 发送了 LaunchActivityItem,但 App 进程根本没收到。查了半天,原来是进程被杀后,AMS 持有的 ApplicationThread 代理已经失效了。嗯,这里要注意:AMS 不会主动清理失效的代理,需要你自己在 onServiceDisconnected 里做处理。
8.2 从 onCreate 到 onStart:谁在中间插了一脚?
很多人以为 onCreate 执行完就直接 onStart 了。其实不是。中间 AMS 还做了一件重要的事——窗口添加。
流程是这样的:
onCreate执行完毕,Activity 调用handleResumeActivity()- 在
handleResumeActivity中,会调用WindowManager.addView() - WMS 处理窗口添加,然后回调 AMS 说“窗口准备好了”
- AMS 才继续调度
onStart和onResume
说白了,onStart 并不是紧跟在 onCreate 后面的。AMS 要等窗口系统确认一切就绪,才会让 Activity 进入“可见”状态。
避坑指南: 我曾经在 onCreate 里做耗时操作,结果导致窗口添加被延迟。用户看到的就是白屏。后来我改成在 onResume 之后才做初始化,体验好多了。
8.3 onResume 的“真假”含义
你想想看,onResume 真的是“恢复”吗?从名字上看是的。但从 AMS 的角度看,onResume 意味着 Activity 已经处于栈顶,并且获得了焦点。
AMS 内部有一个 mResumedActivity 变量,指向当前正在前台运行的 Activity。当 AMS 调度 onResume 时,它会:
- 把当前 Activity 设置为
mResumedActivity - 通知 WMS 这个 Activity 获得了焦点
- 更新
ActivityRecord的状态为RESUMED
这里有个细节:AMS 调度 onResume 是异步的。它发送 ResumeActivityItem 后,不会等 App 执行完再返回。所以如果你在 onResume 里做耗时操作,AMS 那边早就认为你已经“恢复”了。
注意: 不要在 onResume 里做超过 5ms 的操作。否则会影响下一个 Activity 的启动速度。我见过一个案例,某应用在 onResume 里做了网络请求,导致页面切换卡顿严重。
8.4 onPause 和 onStop:谁先谁后?
这个问题我面试时经常问。很多人答不上来。其实规则很简单:
- onPause:当前 Activity 失去焦点时调用(新 Activity 还没完全显示)
- onStop:当前 Activity 完全不可见时调用(新 Activity 已经覆盖了它)
AMS 的调度顺序是:
- 先让旧 Activity
onPause - 再启动新 Activity(
onCreate→onStart→onResume) - 最后让旧 Activity
onStop
为什么会这样?因为 AMS 要保证 新 Activity 尽快显示出来。如果等旧 Activity 完全停止再启动新的,用户会感觉到明显的延迟。
// AMS 调度 onPause 的核心逻辑
void startPausingLocked(ActivityRecord prev, ...) {
// 发送 PauseActivityItem
schedulePauseActivity(prev, ...);
// 等待 App 端执行完毕(有超时机制)
mService.mHandler.sendMessageDelayed(PAUSE_TIMEOUT_MSG, ...);
}
// App 端执行完 onPause 后回调
void activityPaused(IBinder token) {
// AMS 收到回调,继续启动新 Activity
completePauseLocked();
}
我记得有一次线上问题,某个 Activity 的 onPause 里做了数据库写入,导致超时。AMS 等了 500ms 没收到回调,直接强制把 Activity 置为 PAUSED 状态。结果就是——旧 Activity 的状态丢失了。
8.5 onDestroy:最后的告别
onDestroy 是 Activity 的临终遗言。AMS 什么时候会触发它?
- 用户按返回键(
finish()被调用) - 配置变更(如屏幕旋转)导致 Activity 重建
- 系统内存不足,需要回收 Activity
AMS 调度 onDestroy 时,会发送 DestroyActivityItem。App 端执行完后,会回调 AMS 的 activityDestroyed()。这时 AMS 才会真正从栈中移除这个 ActivityRecord。
关键点: AMS 不会等 onDestroy 执行完才做其他事。它发送指令后就返回了。所以如果你在 onDestroy 里做清理工作,要确保这些工作不会影响其他 Activity 的启动。
8.6 生命周期调度的完整流程图
下面这张图展示了 AMS 调度 Activity 生命周期的完整流程。我建议你仔细看看,尤其是跨进程调用的部分。
8.7 生命周期调度的几个关键点
| 生命周期方法 | AMS 发送的事务 | App 端执行线程 | 是否同步等待 |
|---|---|---|---|
| onCreate | LaunchActivityItem | 主线程 | 否(异步) |
| onStart | (包含在 LaunchActivityItem 中) | 主线程 | 否 |
| onResume | ResumeActivityItem | 主线程 | 否 |
| onPause | PauseActivityItem | 主线程 | 是(有超时) |
| onStop | StopActivityItem | 主线程 | 否 |
| onDestroy | DestroyActivityItem | 主线程 | 否 |
注意看 onPause 那一行——它是唯一一个 AMS 会同步等待的方法。为什么?因为 AMS 需要确保旧 Activity 释放了资源(比如相机、传感器),新 Activity 才能正常使用。我曾经遇到一个 bug,某个应用在 onPause 里做了 10 秒的同步网络请求,结果导致整个系统卡死。嗯,后来 Google 在 Android 12 里加了一个限制:onPause 最多只能执行 500ms。
个人建议: 如果你需要做生命周期相关的调试,可以用 adb shell dumpsys activity activities 查看当前所有 Activity 的状态。AMS 会打印每个 ActivityRecord 的 state 字段,比如 RESUMED、PAUSED、STOPPED 等。这个命令我几乎每天都会用。
最后说一句:生命周期调度看似简单,但 AMS 在背后做了大量的状态管理和跨进程通信工作。理解这些细节,能帮你写出更健壮的应用,也能在遇到疑难问题时快速定位根因。