Activity 系统调度与生命周期源码探秘
当你调用 startActivity() 时,发生的事情远比"反射实例化一个 Activity 然后回调 onCreate()"要复杂。
Activity 本质上是 App 进程与 system_server 进程之间协同工作的产物。理解生命周期,就是理解系统核心服务(ATMS)如何跨进程管控 App 的内存状态、UI 可见性和输入焦点。
本文将深入 AOSP 源码,拆解一个 Activity 从 startActivity() 调用到 onCreate() 回调的完整跨进程链路。
1. 设计哲学:控制反转
为什么 Android 不把 main() 直接暴露给开发者,让你自己用 while(true) 驱动渲染和输入?
移动操作系统的内存和焦点资源高度受限,系统必须保有强制调度权——随时可以在后台杀死进程回收内存,或在来电时剥夺前台焦点。为了实现这种强管控,Android 采用了**控制反转(IoC)**架构:App 代码只负责实现回调,系统决定何时调用。
onCreate、onResume 这些方法不是你主动调用的,而是系统按照自己的状态机节奏主动下发的指令。
2. 宏观架构:跨进程调度的类图全景
在深入源码之前,先从类图视角理清 system_server 进程与 App 进程之间的职责分工:
┌──────────────────────────────────────────────────────────────────────────┐
│ 系统进程 (system_server) │
└──────────────────────────────────────────────────────────────────────────┘
[ActivityTaskManagerService] (ATMS:统筹所有 Activity 的全局调度者)
│
│ (1. 委派 ActivityStarter 处理启动请求)
▼
[ActivityStarter] (解析 Manifest、处理 LaunchMode,创建 ActivityRecord)
│
│ (2. 触发焦点切换与栈操作)
▼
[RootWindowContainer] (管理所有显示容器)
│
│ (3. 调度任务栈)
▼
[Task] (Activity 任务栈)
│
│ (4. 系统侧持有 ActivityRecord,不直接持有 Activity 实例)
▼
[ActivityRecord] (Activity 在系统侧的状态记录,持有 IApplicationThread Binder 引用)
│
│ (5. 通过 IApplicationThread 跨进程下发 ClientTransaction)
┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈│┈┈┈┈┈┈┈┈ Binder IPC ┈┈┈┈┈┈┈┈┈┈┈┈┈┈
│
┌─────────────────│────────────────────────────────────────────────────────┐
│ ▼ 应用进程 (App Process) │
└─────────────────│────────────────────────────────────────────────────────┘
[ApplicationThread] (IApplicationThread 的 Binder 服务端,接收系统指令)
│
│ (6. 通过 Handler 切换到主线程)
▼
[ActivityThread] (主线程消息循环,分发 performLaunchActivity 等方法)
│
│ (7. 委托 Instrumentation 完成实例化)
▼
[Instrumentation] (负责 Activity 对象的创建和生命周期分发,也是测试切入点)
│
│ (8. ClassLoader.loadClass().newInstance() 反射调用无参构造)
▼
[Activity] (Activity 实例,此时尚未 attach,无法访问 Context)
│
│ (9. 调用 attach() 建立上下文关联)
▼
[ContextImpl] & [PhoneWindow] (赋予资源访问能力和窗口绘制能力)
关键设计点:系统持有的是 ActivityRecord,而不是 Activity 实例本身。ActivityRecord 通过 IApplicationThread 这个 Binder 接口向 App 进程发令。系统和 App 之间的通信是双向的 Binder 调用。
3. 全链路源码拆解
阶段一:App 进程发起跨进程请求
一切从 App 侧的调用开始,最终通过 Binder 把请求发给系统进程。
调用链:
Activity.startActivity(Intent)Activity.startActivityForResult()Instrumentation.execStartActivity():获取 ATMS 的 Binder ProxyActivityTaskManager.getService().startActivity(...):跨进程调用
源码:
// frameworks/base/core/java/android/app/Instrumentation.java
public ActivityResult execStartActivity(...) {
try {
// 通过 Binder 跨进程调用 ATMS
int result = ActivityTaskManager.getService().startActivity(...);
// 如果 Activity 未在 Manifest 注册,ATMS 返回错误码
// 这里会抛出 ActivityNotFoundException
checkStartActivityResult(result, intent);
} catch (RemoteException e) { ... }
}
为何这么设计:所有组件启动都必须经过系统服务,系统负责权限校验、内存管理和焦点仲裁。App 不能绕过系统自行创建可见的 UI 组件。
阶段二:system_server 执行调度逻辑
请求到达 system_server 后,ATMS 将实际工作委派给 ActivityStarter。
调用链:
ATMS.startActivity():入口接收ActivityStarter.execute():查询 PMS 验证 Manifest,创建ActivityRecordActivityStarter.startActivityUnchecked():处理LaunchMode和Flags,决定目标任务栈RootWindowContainer.resumeFocusedTasksTopActivities():触发前台恢复流程Task.resumeTopActivityUncheckedLocked():发现当前栈顶有正在显示的 Activity
源码:
// frameworks/base/services/core/java/com/android/server/wm/ActivityStarter.java
int execute() {
// 向 PackageManagerService 查询,验证 Activity 是否在 Manifest 中声明
ActivityInfo aInfo = mService.resolveActivityInfoForIntent(...);
// 在系统侧创建 ActivityRecord,此时 App 进程的 Activity 类还未被加载
ActivityRecord r = new ActivityRecord(mService, ...);
// 根据 LaunchMode 和 Task Flags 决定栈位置
return startActivityUnchecked(r, ...);
}
// frameworks/base/services/core/java/com/android/server/wm/Task.java
boolean resumeTopActivityInnerLocked(...) {
// 当前栈顶已有正在 Resume 状态的 Activity(即前一个页面)
if (mResumedActivity != null) {
// 必须先让前一个 Activity 执行 onPause,才能启动新的 Activity
// 这里向 App 进程下发 PauseActivityItem
startPausingLocked(userLeaving, false /* uiSleeping */, top);
// 关键点:下发 Pause 指令后直接返回,挂起新 Activity 的启动流程
// 等 App 进程回报 activityPaused() 后,才会继续
return true;
}
}
为何这么设计:单屏设备同一时刻只有一个 Activity 处于 Resumed 状态。必须等前一个 Activity 完成 onPause 并回报系统,才能启动新 Activity,防止两个 Activity 同时持有输入焦点和渲染资源。
阶段三:前台 Activity 执行 onPause 并回报系统
系统通过 Binder 向 App 进程下发 PauseActivityItem。
调用链:
Task.startPausingLocked()→ 跨进程调用ApplicationThread.scheduleTransaction():Binder 服务端接收指令ActivityThread.H:通过 Handler 切换到主线程TransactionExecutor.execute():解析并执行事务ActivityThread.handlePauseActivity()Activity.performPause()→onPause():前台 Activity 执行暂停ActivityTaskManagerService.activityPaused():App 主动回调系统,解除挂起
源码:
// frameworks/base/core/java/android/app/ActivityThread.java
@Override
public void handlePauseActivity(...) {
// 触发 Activity.onPause()
performPauseActivity(r, finished, reason, pendingActions, ...);
// onPause() 执行完毕后,必须主动通知 ATMS
// 这一步解除了阶段二中 system_server 侧的等待状态
ActivityTaskManager.getService().activityPaused(r.token);
}
重要的性能陷阱:onPause() 里做磁盘 I/O 或耗时同步操作,会直接延迟 activityPaused() 的回调,而 ATMS 在等待这个回调期间是挂起的,整个新 Activity 的启动流程都被阻塞。这就是为什么官方文档严格要求 onPause() 必须快速完成。
阶段四:ATMS 解除等待,下发启动指令
收到 activityPaused() 回调后,ATMS 恢复执行,向 App 进程下发启动新 Activity 的事务包。
调用链:
ActivityStackSupervisor.realStartActivityLocked():进入实际启动逻辑ClientLifecycleManager.scheduleTransaction():封装ClientTransaction,跨进程下发
源码:
// frameworks/base/services/core/java/com/android/server/wm/ActivityStackSupervisor.java
void realStartActivityLocked(ActivityRecord r, WindowProcessController proc, ...) {
// 封装一个原子事务包(ClientTransaction)
final ClientTransaction clientTransaction = ClientTransaction.obtain(
proc.getThread(), r.appToken);
// 指令 1:LaunchActivityItem —— 要求 App 进程创建 Activity 实例并执行 onCreate
clientTransaction.addCallback(LaunchActivityItem.obtain(...));
// 指令 2:ResumeActivityItem —— 声明最终目标状态必须到达 onResume
// 中间缺失的 onStart 由 TransactionExecutor 自动补全
clientTransaction.setLifecycleStateRequest(ResumeActivityItem.obtain(...));
// 通过 Binder 跨进程下发整个事务包
mService.getLifecycleManager().scheduleTransaction(clientTransaction);
}
为何用事务包而不是分散的多次 Binder 调用:Android 8 之前,每个生命周期回调是单独的 Binder 调用,在极端情况下(如 CPU 抢占导致包乱序)会引发生命周期状态错乱。ClientTransaction 将一次完整的生命周期转换封装为原子包,由 App 端的 TransactionExecutor 按顺序执行,从根本上消除了顺序问题。
4. App 进程内:从拆包到 onCreate()
LaunchActivityItem 回到 App 进程的 ActivityThread 后,由 performLaunchActivity() 方法完成 Activity 的实际创建。分三个关键步骤:
① 反射实例化
// frameworks/base/core/java/android/app/ActivityThread.java
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
// 通过 ClassLoader 反射调用无参构造器,得到 Activity 实例
activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
}
注意:此时的 Activity 实例没有 Context,没有 Window,调用任何涉及资源或 UI 的方法都会崩溃。
② attach():建立上下文和窗口关联
// 为这个 Activity 创建专属的 ContextImpl 实例
ContextImpl appContext = createBaseContextForActivity(r);
// attach() 是让 Activity 具备完整能力的关键方法
activity.attach(appContext, this, getInstrumentation(), r.token, ...);
attach() 内部做了三件事:
- 注入 Context:将
ContextImpl设置为基类ContextWrapper的mBase,Activity 从此具备访问资源、启动组件等能力。 - 创建 PhoneWindow:
mWindow = new PhoneWindow(...),这是后续setContentView()能够工作的前提。 - 保存 IBinder Token:
r.token是系统颁发的唯一身份标识,Activity 与系统之间的所有通信都通过这个 Token 关联。
③ callActivityOnCreate():触发 onCreate
// 至此 Activity 具备完整的运行环境,可以安全触发 onCreate
mInstrumentation.callActivityOnCreate(activity, r.state);
当你在 onCreate() 里调用 setContentView() 时,PhoneWindow 已经就绪,View 层级得以正常构建。
5. 自动补全生命周期路径的状态机
你可能注意到,realStartActivityLocked() 里只设置了 LaunchActivityItem(对应 onCreate)和最终目标 ResumeActivityItem(对应 onResume),中间的 onStart 是怎么触发的?
这由 TransactionExecutor 中的路径补全逻辑处理:
// frameworks/base/core/java/android/app/servertransaction/TransactionExecutor.java
private void executeLifecycleState(ClientTransaction transaction) {
// 拿到目标状态(ResumeActivityItem 对应 ON_RESUME)
final ActivityLifecycleItem lifecycleItem = transaction.getLifecycleStateRequest();
// 查询 Activity 当前所处状态(刚执行完 onCreate,处于 ON_CREATE)
int currentState = r.getLifecycleState();
// 目标状态(ON_RESUME)
int targetState = lifecycleItem.getTargetState();
// 计算从当前状态到目标状态需要经过的中间节点
// 结果:[ON_START, ON_RESUME]
final IntArray lifecycleItemRequest = mHelper.getLifecyclePath(
currentState, targetState, excludeLastState);
// 依次执行每个中间状态的回调
performLifecycleSequence(r, lifecycleItemRequest);
}
performLifecycleSequence() 内部遍历路径数组,依次触发 handleStartActivity()(onStart)和 handleResumeActivity()(onResume)。
这个设计解决的问题:跨进程通信存在丢包风险,如果每个生命周期回调都需要系统单独发包,丢一个包就会导致 Activity 卡在中间状态。声明式的目标状态 + App 端自动补全路径,使得整个生命周期转换具有幂等性,只要最终目标状态正确,中间路径的执行就一定完整。
6. LMK 与 onDestroy 的可靠性边界
一个常见的错误认知:把重要的数据保存逻辑放在 onDestroy() 里,认为 Activity 销毁时一定会被调用。
事实是:系统不保证 onDestroy() 一定会被执行。
只有在以下两种情况下,onDestroy() 才会被正常调用:
- 代码主动调用
finish() - 系统因配置变更(如旋转屏幕)重建 Activity
在内存紧张时,情况完全不同。当 App 进程的 oom_adj 值因后台运行被系统调低,一旦有前台进程需要内存,Linux 内核的 LMK(Low Memory Killer) 会直接发送 SIGKILL(kill -9)终止进程。
SIGKILL 是无法被拦截的,它直接在内核层面强制终止进程,ART 虚拟机没有机会执行任何 Java 代码,包括 onDestroy()、ShutdownHook 等所有清理逻辑。进程内的所有内存状态瞬间消失。
实践结论:
- 需要持久化的数据,必须在
onPause()或onStop()中保存,不能依赖onDestroy()。 - UI 状态(如滚动位置、输入内容)使用
ViewModel+onSaveInstanceState()保存,能在进程被杀后重建时恢复。 - 网络请求、数据库操作等异步任务,应当在
onStop()时取消或持久化状态,不依赖onDestroy()的清理时机。
理解了 LMK 的工作机制,在遇到 App 被强杀后状态丢失的问题时,才能从正确的方向定位和解决。