启动 Activity的系统服务流程

22 阅读5分钟

问题:

下面用一个最典型的场景:启动 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
  1. app 侧调用 startActivity

进程 / 线程:

  • 进程:app 进程(com.example.app
  • 线程:通常是 UI 线程(调用 startActivity()

执行过程:


startActivity(intent);
  • 实际会通过 ActivityTaskManager / ActivityManagerBinder 接口 调用到 system_server:

    • app → Binder → IActivityTaskManager(ATMS/AMS 的对外接口)

此时还没有 system_server 内部锁竞争,只是一次 Binder 进入。

  1. 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(...);

这里有两种情况(按实现策略):

  1. 直接持有 PMS 实例,方法调用(同进程直接调用);
  2. 通过 AIDL 接口 IPackageManager 调用(但在同一进程,属于进程内 Binder)。

常见实现中,AMS 内部往往会直接持有 PackageManagerInternal/PackageManagerService 实例,走的是同进程直接方法调用

可能涉及的锁:

  • AMS 中经常会在类似 synchronized (mGlobalLock) / ActivityTaskManagerService#mGlobalLock 这样的全局锁下执行部分逻辑;
  • 当需要访问/修改全局任务栈、进程记录等共享结构时,一般会先获取 AMS 自己的全局锁。
  1. AMS / ATMS 决定要新建 Task / ActivityRecord

进程 / 线程:

  • 依然在 system_server → Binder 线程 或切到 内部 HandlerThread

(实际实现里,很多逻辑都会切到 ActivityTaskSupervisor 等内部线程处理,以减轻 Binder 线程压力)

执行大致流程:

  • 解析 Intent / ActivityInfo;
  • 决定在哪个 Task / Display 上启动;
  • 创建/更新 ActivityRecordTask 等对象(需要在 AMS / ATMS 的全局锁保护下进行)。

与其他服务通信:

  • 这里可能会调用:

    • WMS:计算窗口相关信息(比如 display、rotation 等),或者对窗口栈进行准备;
    • PMS:再次核实权限、exported 属性等。
mWindowManager.addAppToken(...);  // 旧版本
// or 新版本通过 WindowProcessController 等更抽象的接口跟 WMS 协作

锁与线程竞争点:

  • AMS 全局锁:保护 Activity/Task/Process 全局状态。
  • 调用 WMS 时要注意锁顺序: 一般会先持有 AMS 的锁,再以固定顺序访问 WMS,避免反向调用时死锁。

Framework 中通常会有注释说明:

“持有 AMS 锁时调用 WMS 的范围要尽量小,或者在调用前释放 AMS 锁。”

  1. 通知 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。

避免同时持有多个锁,持有了新的,就要释放老的

  1. AMS 回到应用进程:让 app 创建 Activity(生命周期)

  • 创建 Activity 对象;
  • 调用 onCreate()onStart()onResume() 等。

进程 / 线程:

  • system_server 的 Binder 线程 → 通过 Binder 调用 → 应用进程的 Binder 线程 / 主线程
  • 应用侧一般通过 ActivityThread 把生命周期分发到主线程的 Looper。

这一段的特点:

  • system_server 端需要等 app 进程响应(生命周期执行),如果 app 卡住(主线程堵塞):

    • AMS/WMS 这边会在一定超时后认为 app ANR;
    • system_server 自己不会因此被永久卡死(除非持有某些锁时同步等待,设计上尽量避免)。
  1. WMS 完成界面可见性、布局与绘制协作

当 app 完成首帧绘制并调用 Surface 提交 buffer 后,WMS 协同 SurfaceFlinger 把新 Activity 的界面显示出来。

进程 / 线程:

  • 进程:

    • system_server(WMS 逻辑)
    • SurfaceFlinger(native 合成服务,单独进程)
  • 线程:

    • WMS 的 Window/Display 相关线程;
    • SurfaceFlinger 的渲染线程。

WMS 需要:

  • 根据当前所有窗口状态(含刚加入的新 Activity 窗口)更新布局;
  • 通知 SurfaceFlinger 更新 Layer 栈;
  • 这中间仍然需要持有 WMS 的全局锁一小段时间。

这条链上的多线程与竞争点小结

在这条 “启动 Activity” 调用链中,关键点可以总结为:

  1. 线程切换:

    1. app 主线程 → app Binder 线程 → system_server Binder 线程 →(可能)system_server 内部 HandlerThread → 再回 app 进程;
    2. 有多次线程切换与进程切换。
  2. 服务间通信方式:

    1. AMS ↔ PMS:主要是 同进程直接方法调用(有些通过 internal 接口);
    2. AMS ↔ WMS:同进程对象调用;
    3. 对外(app):Binder 调用
  3. 多线程 & 争夺:

    1. AMS 全局锁:多个 Binder 线程 / Handler 线程会争夺;
    2. WMS 全局锁:来自 AMS 的调用、input/display 线程、SystemUI 交互等都会争夺;
    3. 锁竞争导致的阻塞如果时间过长,会被 Watchdog 检测到。
  4. 如何控制风险:

    1. 严格规范 锁顺序(例如永远先 A 再 B,禁止反向);
    2. 避免在持锁状态下做耗时操作或跨进程调用;
    3. 大部分复杂逻辑放在服务内部的 HandlerThread 异步执行;
    4. Watchdog 定时检查关键线程(main、Binder、某些 HandlerThread)是否卡死。