3. AMS核心数据结构
3.1 模块边界与层次划分
AMS 对 Android 四大组件的管理依赖四个核心数据对象,它们构成了从“进程 → 栈 → 任务 → 活动”的逐层嵌套关系。以下表格定义了各对象的职能范围与生命周期归属:
| 数据结构 | 所属层级 | 实例化时机 | 销毁时机 | 关键标识 |
|---|---|---|---|---|
| ProcessRecord | 进程级 | 应用进程创建/绑定 | 进程死亡或被杀 | pid, processName |
| ActivityStack | 显示级 | 系统启动/创建多屏幕栈 | 栈被移除或销毁 | stackId, mStackState |
| TaskRecord | 任务级 | 启动 Activity(新任务) | 任务内 Activity 全部 finish | taskId, affinity |
| ActivityRecord | 活动级 | 每次启动/恢复 Activity | Activity 完全销毁 | intent, activityType |
3.2 核心组件职责
3.2.1 ProcessRecord —— 进程生命周期的“护照”
ProcessRecord 记录一个 Android 应用进程的所有元信息,包括 pid / uid、已注册的 BroadcastReceiver、正在运行的服务列表、LRU 排名等。AMS 通过它判断进程的“重要程度”以执行 LMK(Low Memory Killer)策略。
其关键字段在源码中可视为:
// 简化后的 ProcessRecord 核心成员
public final class ProcessRecord {
final String processName; // 进程名
int pid; // 进程 ID
int uid; // 用户 ID
boolean killed; // 是否已杀
final ArrayList<ActivityRecord> activities; // 该进程下的 Activity 列表
final ArrayList<ServiceRecord> services; // 已注册的服务
final ProcessStateRecord state; // 进程状态
}
3.2.2 ActivityStack —— 显示器抽象的“栈容器”
每个物理显示器(或虚拟屏幕)对应一个 ActivityStack,负责管理该屏幕上的所有 TaskRecord 栈。Stack 提供 resumeTopActivityInnerLocked() 核心方法,决定哪个 Activity 获得焦点。栈分为全屏栈、分屏栈、自由窗口栈等类型,每个类型有不同的布局策略。
3.2.3 TaskRecord —— 用户任务的“活动历史链”
TaskRecord 维护一个 ActivityRecord 的链表(mActivities),按启动顺序排列。它负责返回栈(back stack)的导航逻辑,决定按 Back 键时回到哪个 Activity。每个 TaskRecord 还有自己的 affinity、launchMode、taskId,用于匹配 Intent 的 FLAG_ACTIVITY_NEW_TASK 行为。
3.2.4 ActivityRecord —— Activity 实例的“身份证”
每个 Activity 在 AMS 中唯一对应一个 ActivityRecord,持有 Intent、launchMode、processName、状态(initialized→resumed→paused→stopped→destroyed)等。它是 AMS 与 WMS 交互的最小单元,所有生命周期回调(onCreate/onResume…)都通过它触发。
3.3 数据流 / 控制流 / 信任链路
一次 Activity 的启动请求(例如从 Launcher 点击图标)会依次经过:
- Launcher 进程向 AMS 发送
startActivity()Binder 调用 - AMS 根据 Intent 解析目标 ActivityRecord,查找或创建 TaskRecord
- 确定 TaskRecord 所属的 ActivityStack(当前焦点栈或指定栈)
- 通过 ProcessRecord 判断目标进程是否已启动,若未启动则 fork 新进程
- 新进程初始化后,AMS 通过 ApplicationThread 回调 ActivityThread 创建 Activity 实例
以下 SVG 图展示了四者在一次启动流程中的关系与数据流向:
ActivityManagerService.startActivity() 时,必须持有全局锁(mServiceLock)才能修改这些数据结构。启动链中每个环节都需验证 caller 的权限(如 ACTIVITY_NEW_TASK 标志是否合法),信任边界从 SystemServer 进程延伸至目标进程的 ApplicationThread。
3.4 设计权衡
- ActivityRecord 粒度 vs 内存开销:每个 Activity 实例都维系一个完整的状态机对象,在低端设备上大量 Activity 回退栈会导致内存暴涨。Android 12 引入的
ActivityRecordPersistent优化了冷启动恢复场景,但未改变核心设计。 - ActivityStack 多栈 vs 焦点管理:多窗口、多显示器场景需要多个栈共存,但焦点栈的切换逻辑必须兼顾动画、输入法、窗口层级,这些逻辑全部集中在
ActivityStackSupervisor中,导致单点复杂度。 - TaskRecord 的 affinity 爆炸:每个应用可以声明多个 affinity,AMS 需要匹配 Intent 的 taskAffinity 和栈顶 Activity 的 affinity,这种匹配在多任务启动时可能引起 O(n²) 遍历查找。工程上通过
mTaskHistory哈希表缓解,但嵌套启动场景仍存在性能风险。
3.5 风险点
| 风险场景 | 涉及数据结构 | 后果 | 防范措施 |
|---|---|---|---|
| 进程被 LMK 杀掉后 ActivityRecord 残留 | ProcessRecord, ActivityRecord | Activity 无法恢复,界面白屏 | AMS 在进程死亡时调用 cleanUpApplicationRecordLocked() 清理关联的 ActivityRecord |
| 栈顶 Activity 被意外关闭导致 TaskRecord 空栈 | TaskRecord, ActivityStack | 返回栈断裂,用户无法返回 | AMS 在移除最后一个 ActivityRecord 时同时移除整个 TaskRecord |
| 多用户场景下 ActivityStack 混淆 | ActivityStack | 显示异常(例如副用户界面出现在主用户) | 按 userId 隔离 stack 创建,StackId 包含用户标识 |
| ActivityRecord 的 state 被异步并发修改 | ActivityRecord | 生命周期错乱,crash | 必须持有 mServiceLock 单线程修改,内部使用事务计数器保证一致性 |
3.6 落地步骤(面向系统开发者的理解路径)
- 从源码 trace 入手:在
ActivityManagerService.java中搜索new ActivityRecord(和new TaskRecord(,观察构造参数,理解各字段来源。 - 调试启动流程:使用
adb shell dumpsys activity activities实时查看当前栈中 ActivityRecord 的 taskId 和 stackId,与源码对照。 - 手动构造分屏场景:通过 ADB 启动两个应用并进入分屏模式,抓取
dumpsys activity stack确认 ActivityStack 新增实例,观察 TaskRecord 被复用的次数。 - 实现自定义 LaunchMode 验证:编写一个测试 App,设置
singleInstance或singleTask,在 AMS 关键路径日志中跟踪 ProcessRecord 的 activities 列表变化。 - 压力测试多任务:使用
MonkeyRunner快速启动 100 个 Activity,观察 AMS heap 中 ActivityRecord 和 TaskRecord 的数量与 GC 频率。
3.7 验收标准
- ✅ 能准确画出 ProcessRecord / ActivityStack / TaskRecord / ActivityRecord 之间的 UML 关联(组合 vs 聚合)关系。
- ✅ 通过
dumpsys输出的栈信息,可反向推断出当前前台 Activity 的 ProcessRecord pid 和所属 TaskRecord taskId。 - ✅ 在一个模拟的 AMS 环境中,成功插入一条新的 ActivityRecord 到指定 TaskRecord 并触发其 Resume 状态,而不引发死锁或空指针。
- ✅ 针对上述风险点表,针对每一条给出至少一种可复现的测试用例(如通过 ADB 模拟进程被杀)。