Administrator
发布于 2026-08-25 / 16 阅读
0
0

系列文章《Android 工程师的 100 篇进阶笔记》第 55 篇:WMS/AMS 系统服务原理:理解 Activity 的"幕后老板"

上一篇讲了 Flutter 互操作,回到 Android 本体。这篇钻进系统服务——WMS 和 AMS。它们管理所有应用的生命周期和窗口,却 99% 的工程师没读过源码。读一次,理解力直接上台阶。

两个最忙的服务

AMS(Activity Manager Service)和 WMS(Window Manager Service)是 Android 系统的"大脑":

服务

职责

AMS

Activity/Service/Broadcast/Provider 生命周期管理

WMS

窗口、Surface、动画、Input 事件分发

PKMS

Package 扫描、权限授予

PMS

电源管理

它们以 SystemServer 进程里的线程运行,每个 App 通过 Binder IPC 和它们通信。

AMS 启动 Activity 的真实链路

我们调 startActivity(intent),背后的真实流程远超想象:

App Process (Main Thread)
  ↓ startActivity(Intent)
  ↓
ActivityTaskManager(IActivityTaskManager)
  ↓ Binder IPC
  ↓
system_server 进程
  ├─ ActivityTaskManagerService.startActivity()
  ├─ ActivityStartController
  ├─ RootActivityContainer
  │  └─ ActivityStack
  │     └─ 创建 ActivityRecord
  ├─ 调用应用进程 ApplicationThread
  ├─ scheduleLaunchActivity()
  ↓ Binder IPC
  ↓
App Process (ActivityThread)
  ├─ main() 入口
  ├─ H(Handler)收到 LAUNCH_ACTIVITY 消息
  ├─ ActivityThread.performLaunchActivity()
  │   ├─ 通过反射创建 Application(如果是首次)
  │   ├─ 通过反射创建 Activity
  │   ├─ 调用 Activity.attach()
  │   └─ 调用 Activity.onCreate()

一次 startActivity 调用,从应用进程到 system_server,再回应用进程,至少经历 4 次 Binder IPC。这也是为什么"启动慢"。

WMS 创建窗口的链路

每个 Activity 都对应一个 Window:

Activity.setContentView(view)
  ↓
PhoneWindow.setContentView()
  ├─ installDecor() 创建 DecorView
  ├─ 解析 R.layout.xxx 到 View
  ↓
WindowManagerImpl.addView(view, layoutParams)
  ├─ WindowManagerGlobal.getInstance()
  ├─ ViewRootImpl (核心类)
  │  ├─ 持有 mWindowSession(IWindowSession)
  │  └─ 持有 mAttachInfo(View.AttachInfo)
  ↓ 通过 mWindowSession 跨进程
  ↓
system_server 进程
  ├─ WindowManagerService.addWindow()
  │  ├─ 校验权限
  │  ├─ 创建 WindowState
  │  ├─ 记录到 mWindowMap
  │  └─ 计算窗口层级 Z 轴
  ├─ 通过 IWindow 回调到应用进程
  ↓
应用进程的 ViewRootImpl
  ├─ performTraversals()
  │  ├─ performMeasure() 测量
  │  ├─ performLayout() 布局
  │  ├─ performDraw() 绘制
  │  └─ IPC 提交给 RenderThread 用硬件加速渲染

ViewRootImpl 是 View 体系和 WMS 的桥梁。理解它,就理解了为什么:

  • 触摸事件先到 WMS 再到 View

  • 旋转屏幕需要 WMS 触发新一次 performTraversals

  • Choreographer 是 ViewRootImpl 的"心跳"

AMS 启动模式背后的原理

startActivity(Intent(this, BActivity::class.java)
    .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK))

AMS 内部维护 ActivityRecord、CWMS、Lifecycle 等数据。栈结构:

TaskStack
  ├─ Task (launcher taskAffinity)
  │   ├─ ActivityRecord (MainActivity)
  │   └─ ActivityRecord (BActivity)
  └─ Task (另一个 taskAffinity)
      └─ ActivityRecord (CActivity)

四种 launchMode(standard、singleTop、singleTask、singleInstance)的判断逻辑在 AMS 收到 startActivity 请求后执行。具体决策:

launchMode

行为

standard

每次都创建新实例

singleTop

栈顶已有则 onNewIntent,否则创建

singleTask

整个栈找已有则 onNewIntent,不存在则创建

singleInstance

独占一个 task

singleTask 还会触发 task 切换,这一点对小红点、深链很重要。

进程模型的真相

App 进程 ≠ 应用进程。AMS 根据需要启动进程:

adb shell ps -A | grep com.example.app

App 的所有进程:

  • 主进程(pid 不同)

  • :push 子进程(:name 后缀,android:process)

  • webview 进程

  • render 进程(Renderer,原因硬件加速)

每个进程独立创建 ActivityThread.main() → Looper.prepare() → 进入消息循环。Application 实例在第一个进程启动时创建一次。

调试 AMS/WMS 的方法

# 看应用栈
adb shell dumpsys activity activities

# 看窗口
adb shell dumpsys window windows

# 实时看 activity 状态变化(开发者选项里开"显示布局边界")

# Power 用户
adb shell dumpsys activity intents      # 看 intent
adb shell dumpsys activity providers   # 看 provider

dumpsys 输出太长的过滤技巧:

adb shell dumpsys window windows | grep -A 5 "Window #"

真实问题排查实例

问题:ANR 但没卡死

ANR 出现的 4 种情况:

场景

时间

触发

主线程 5s 无响应

5s

Touch/Key 事件

BroadcastReceiver 处理超时

10s(前台)

onReceive

Service onCreate/onStartCommand

20s

启动 Service

ContentProvider 启动阻塞

10s+

ContentProvider.create

/data/anr/traces.txt 里有所有线程的 dump,看主线程堆栈就能定位:

"main" prio=5 tid=1 Sleeping
  | group="main" sCount=1
  | sysTid=12345
  at java.lang.Thread.sleep(Native method)
  at com.example.MyWorker.run(MyWorker.kt:42)
  at android.os.Handler.handleCallback(Handler.java:790)
  at android.os.Handler.dispatchMessage(Handler.java:99)
  at android.os.Looper.loop(Looper.java:223)
  ...

问题:LeakedWindow 异常

字面意思:Dialog/Dialog 持有的 Window 在 Activity 销毁后还没释放。

// 不要
override fun onCreate() {
    showProgressDialog() // 立即显示,里面持有当前 Activity 的 Window
}

// 推荐
private var dialog: ProgressDialog? = null

override fun onStart() {
    dialog = ProgressDialog(this).also {
        it.setMessage("Loading")
        it.show()
    }
}

override fun onStop() {
    dialog?.dismiss() // onStop 里销毁
    dialog = null
}

不需要读懂,但要知道在哪

AMS 源码的 99% 用不上。真正需要排查的是:

  • dumpsys 输出 → AMS 状态

  • 启动流程卡顿 → AMS/Binder 链路

  • 窗口显示问题 → WMS 流程

  • ANR → traces.txt + AMS 触发记录

读一遍 AOSP 的 ActivityTaskManagerService.java 主要流程就够了。

小结

AMS/WMS 是 Android 框架的脊梁。不需要读完源码,但要理解它们的工作方式:App 通过 Binder 调用系统服务,系统服务通过 IApplicationThread/IWindow 回调到应用进程。理解这套模型,绝大多数诡异的 crash、ANR、白屏都能定位。

下一篇讲 AGP Transform API,再进一个编译期生态——自定义 Gradle 插件与字节码插桩的结合。


评论