上一篇讲了 Flutter 互操作,回到 Android 本体。这篇钻进系统服务——WMS 和 AMS。它们管理所有应用的生命周期和窗口,却 99% 的工程师没读过源码。读一次,理解力直接上台阶。
两个最忙的服务
AMS(Activity Manager Service)和 WMS(Window Manager Service)是 Android 系统的"大脑":
它们以 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 请求后执行。具体决策:
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 种情况:
/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 插件与字节码插桩的结合。