问题:
下面用一个最典型的场景:启动 Activity,来串一条 app ↔ AMS ↔ WMS ↔ PMS 的调用链,并标出:
- 在 哪个进程 / 哪个线程 执行;
- 大致会涉及到 哪些锁 / 竞争点;
- 这些设计如何避免 system_server 被拖垮。
场景:应用启动一个 Activity( AMS ↔ WMS ↔ PMS)
0. 前提:系统状态
-
进程:
- 应用进程:
com.example.app(app 进程) system_server:AMS / WMS / PMS 都在这个进程里(Java 对象单例)
- 应用进程:
-
关键服务:
- AMS:ActivityManagerService
- WMS:WindowManagerService
- PMS:PackageManagerService
-
app 侧调用 startActivity
进程 / 线程:
- 进程:app 进程(
com.example.app) - 线程:通常是 UI 线程(调用
startActivity())
执行过程:
startActivity(intent);
-
实际会通过
ActivityTaskManager/ActivityManager的 Binder 接口 调用到 system_server:- app → Binder → IActivityTaskManager(ATMS/AMS 的对外接口)
此时还没有 system_server 内部锁竞争,只是一次 Binder 进入。
-
Binder 进入 system_server: AMS /ATMS 接口层
进程 / 线程:
- 进程:system_server
- 线程:某个 Binder 线程池中的线程(例如
Binder:1234_1)
执行大致流程:
- Binder 线程进入
ActivityTaskManagerService/ActivityManagerService的 Stub 方法,比如:
ActivityTaskManagerService#startActivity(...)
-
在这个 Stub 里做:
- 参数校验;
- 权限检查(可能会查 PMS);
- 决定把真正的调度逻辑 post 到内部 Handler 线程 或在当前 Binder 线程上继续执行。
与其他服务通信:
- 例如,需要校验这个 Intent 能否启动目标 Activity,会调用 PMS 获取
ActivityInfo:
ActivityInfo aInfo = mPackageManager.getActivityInfo(...);
这里有两种情况(按实现策略):
- 直接持有 PMS 实例,方法调用(同进程直接调用);
- 通过 AIDL 接口
IPackageManager调用(但在同一进程,属于进程内 Binder)。
常见实现中,AMS 内部往往会直接持有 PackageManagerInternal/PackageManagerService 实例,走的是同进程直接方法调用。
可能涉及的锁:
- AMS 中经常会在类似
synchronized (mGlobalLock)/ActivityTaskManagerService#mGlobalLock这样的全局锁下执行部分逻辑; - 当需要访问/修改全局任务栈、进程记录等共享结构时,一般会先获取 AMS 自己的全局锁。
-
AMS / ATMS 决定要新建 Task / ActivityRecord
进程 / 线程:
- 依然在 system_server → Binder 线程 或切到 内部 HandlerThread
(实际实现里,很多逻辑都会切到 ActivityTaskSupervisor 等内部线程处理,以减轻 Binder 线程压力)
执行大致流程:
- 解析 Intent / ActivityInfo;
- 决定在哪个 Task / Display 上启动;
- 创建/更新
ActivityRecord、Task等对象(需要在 AMS / ATMS 的全局锁保护下进行)。
与其他服务通信:
-
这里可能会调用:
- WMS:计算窗口相关信息(比如 display、rotation 等),或者对窗口栈进行准备;
- PMS:再次核实权限、exported 属性等。
mWindowManager.addAppToken(...); // 旧版本
// or 新版本通过 WindowProcessController 等更抽象的接口跟 WMS 协作
锁与线程竞争点:
- AMS 全局锁:保护 Activity/Task/Process 全局状态。
- 调用 WMS 时要注意锁顺序: 一般会先持有 AMS 的锁,再以固定顺序访问 WMS,避免反向调用时死锁。
Framework 中通常会有注释说明:
“持有 AMS 锁时调用 WMS 的范围要尽量小,或者在调用前释放 AMS 锁。”
-
通知 WMS 准备窗口( AMS ↔ WMS)
当 AMS 确认要启动新 Activity 后,需要让 WMS 为即将出现的界面做窗口层面的准备/更新。
进程 / 线程:
- 仍在 system_server 内;
- 可能仍是同一个 Binder 线程,也可能在某个内部 HandlerThread 上(看具体实现路径)。
执行大致流程:
- 调用 WMS 的接口(同进程直接调用):
mWindowManager.startActivityTransition(...);
或者,旧的路径:
mWindowManager.addAppToken(...);
-
WMS 内部:
- 获取自己的 全局锁,多半是
mGlobalLock/mWindowMap等; - 更新窗口 token 列表、Layer 顺序、布局状态等。
- 获取自己的 全局锁,多半是
锁与线程竞争点:
- WMS 内部常见的做法:
synchronized (mGlobalLock) {
// 修改窗口状态
}
-
若此时其他线程(例如:
- 来自 input / display 线程;
- 来自 SystemUI 交互 )也在尝试持有 WMS 的全局锁,就会出现锁竞争。
避免死锁的关键:
- AMS 锁与 WMS 锁的获取顺序必须统一且谨慎;
- 设计上会尽量避免在持有 AMS 锁的同时长时间持有 WMS 锁,也避免在 WMS 锁内再去回调 AMS。
避免同时持有多个锁,持有了新的,就要释放老的
-
AMS 回到应用进程:让 app 创建 Activity(生命周期)
- 创建 Activity 对象;
- 调用
onCreate()、onStart()、onResume()等。
进程 / 线程:
- 从 system_server 的 Binder 线程 → 通过 Binder 调用 → 应用进程的 Binder 线程 / 主线程;
- 应用侧一般通过
ActivityThread把生命周期分发到主线程的 Looper。
这一段的特点:
-
system_server 端需要等 app 进程响应(生命周期执行),如果 app 卡住(主线程堵塞):
- AMS/WMS 这边会在一定超时后认为 app ANR;
- system_server 自己不会因此被永久卡死(除非持有某些锁时同步等待,设计上尽量避免)。
-
WMS 完成界面可见性、布局与绘制协作
当 app 完成首帧绘制并调用 Surface 提交 buffer 后,WMS 协同 SurfaceFlinger 把新 Activity 的界面显示出来。
进程 / 线程:
-
进程:
- system_server(WMS 逻辑)
- SurfaceFlinger(native 合成服务,单独进程)
-
线程:
- WMS 的 Window/Display 相关线程;
- SurfaceFlinger 的渲染线程。
WMS 需要:
- 根据当前所有窗口状态(含刚加入的新 Activity 窗口)更新布局;
- 通知 SurfaceFlinger 更新 Layer 栈;
- 这中间仍然需要持有 WMS 的全局锁一小段时间。
这条链上的多线程与竞争点小结
在这条 “启动 Activity” 调用链中,关键点可以总结为:
-
线程切换:
- app 主线程 → app Binder 线程 → system_server Binder 线程 →(可能)system_server 内部 HandlerThread → 再回 app 进程;
- 有多次线程切换与进程切换。
-
服务间通信方式:
- AMS ↔ PMS:主要是 同进程直接方法调用(有些通过 internal 接口);
- AMS ↔ WMS:同进程对象调用;
- 对外(app):Binder 调用。
-
多线程 & 争夺:
- AMS 全局锁:多个 Binder 线程 / Handler 线程会争夺;
- WMS 全局锁:来自 AMS 的调用、input/display 线程、SystemUI 交互等都会争夺;
- 锁竞争导致的阻塞如果时间过长,会被 Watchdog 检测到。
-
如何控制风险:
- 严格规范 锁顺序(例如永远先 A 再 B,禁止反向);
- 避免在持锁状态下做耗时操作或跨进程调用;
- 大部分复杂逻辑放在服务内部的 HandlerThread 异步执行;
- Watchdog 定时检查关键线程(main、Binder、某些 HandlerThread)是否卡死。