WMS 与 SurfaceFlinger 概述
内容从系统架构出发,按逻辑顺序梳理 WMS 和 SurfaceFlinger 的职责、协作、关键机制等 内容可能有错漏,请以源码为准。
目录
- 系统整体架构
- WMS(WindowManagerService)
- SurfaceFlinger
- WMS 与 SurfaceFlinger 的协作关系
- 窗口绘制状态机
- 合成路径:HWC vs GPU
- VSync 机制
1. 系统整体架构
Android 图形显示系统由三个核心进程协同工作:
┌──────────────────────────────────────────────────────────────┐
│ App / SystemUI │ ← 各自独立进程
│ 内容绘制 & 提交 Buffer │
└────────────────┬─────────────────────────────────────────────┘
│ WindowSession (Binder)
┌────────────▼────────────┐
│ WindowManagerService │ ← SystemServer 进程
│ 窗口管理 & 层级控制 │
└────────────┬────────────┘
│ SurfaceControl (Binder)
┌────────────▼────────────┐
│ SurfaceFlinger │ ← Native 进程
│ 图层合成 & 屏幕输出 │
└────────────┬────────────┘
│ 最终帧
┌────────────────▼─────────────────────────────────────────────┐
│ 物理屏幕 / 显示屏 │
└──────────────────────────────────────────────────────────────┘
三者分工:
- App/SystemUI:负责"画什么"——View 树渲染到 GraphicBuffer
- WMS:负责"怎么摆"——窗口位置、大小、层级、可见性、动画
- SurfaceFlinger:负责"怎么合"——将多个 Buffer 合成为一帧输出
2. WMS(WindowManagerService)
2.1 定位
WMS 是运行在 SystemServer 进程中的核心系统服务,负责所有应用窗口的管理和调度,是连接应用层 UI 和底层 SurfaceFlinger、InputFlinger 的桥梁。
2.2 核心职责
| 职责 | 说明 |
|---|---|
| 窗口管理 | 窗口的创建/销毁、层级排序、可见性控制、焦点分配 |
| 输入事件分发 | 与 IMS 协作,将触控/按键事件分发到正确窗口 |
| 动画与过渡 | 应用切换、屏幕旋转、折叠屏动画的过渡控制,与shell transition配合 |
| 显示与任务 | 与DMS配合 管理多显示设备,与ATMS管理Task 任务栈,与IMS配合控制 IME 输入法 |
2.3 WindowContainer 层级树
WMS 内部管理窗口的数据结构是一棵Z 序排列的容器树:
RootWindowContainer ← 全局唯一根节点
│
└── DisplayContent (每个屏幕一个)
│
├── DisplayArea.Tokens (BELOW_TASKS) ← 壁纸等系统窗口
│ └── WindowToken → WindowState
│
├── TaskDisplayArea ← 应用窗口区域
│ └── Task → ActivityRecord → WindowState
│
├── DisplayArea.Tokens (ABOVE_TASKS) ← 状态栏、导航栏等
│ └── WindowToken → WindowState
│
└── ImeContainer ← 输入法窗口
└── WindowToken → WindowState
核心类关系:
| 类 | 角色 |
|---|---|
WindowContainer | 所有容器的抽象基类 |
DisplayContent | 一个物理/虚拟屏幕 |
TaskDisplayArea | 包含 Task 的显示区域 |
Task | 任务栈(原 ActivityStack) |
ActivityRecord | 一个 Activity 实例(即 AppWindowToken) |
WindowToken | 一组相关窗口的容器 |
WindowState | 叶子节点:一个具体窗口 |
2.4 performSurfacePlacement(窗口大循环)
WindowSurfacePlacer.performSurfacePlacement() 是 WMS 中窗口布局和 Surface 放置的主循环入口。
核心调用链:
WindowSurfacePlacer.performSurfacePlacement()
→ RootWindowContainer.performSurfacePlacement()
├── applySurfaceChangesTransaction() ← 计算帧、放置 Surface、处理 draw state
├── handleResizingWindows() ← 处理 resize 中的窗口
├── checkDrawnWindowsLocked() ← 检查窗口绘制完成
├── 更新输入窗口、亮度、动画调度等
为什么叫"大循环": 一次 traversal 可能因窗口位置变化引发连锁重布局,最多循环 6 次:
int loopCount = 6;
do {
performSurfacePlacementLoop();
loopCount--;
} while (mTraversalScheduled && loopCount > 0);
2.5 与 SurfaceFlinger 的连接
WMS 中每个 WindowContainer 都对应一个 SurfaceControl,通过 SurfaceComposerClient(Binder)控制 SurfaceFlinger 中的 Layer。
两棵树是镜像关系:
WMS WindowContainer 树 SurfaceFlinger Layer 树
┌─────────────────────┐ ┌─────────────────────┐
│ DisplayContent │───────>│ ContainerLayer │
│ └── Task │ │ └── BufferLayer │
│ └── Activity │ │ └── BufferLayer│
│ └── WS │ │ │
└─────────────────────┘ └─────────────────────┘
逻辑管理层 渲染执行层
(决策者) (执行者)
3. SurfaceFlinger
3.1 定位
SurfaceFlinger 是 Android 中专职图层合成与屏幕输出的 Native 进程(进程名 surfaceflinger)。它不决定"谁该显示",只负责"把已有的 Buffer 合成为最终帧"。
3.2 核心职责
第一:基于 BufferQueue 的生产者-消费者模型。 在 Android 中,每个窗口(Surface)背后都有一个 BufferQueue(缓冲队列)。应用进程是"生产者",负责通过 CPU/GPU 绘制画面并填满 Buffer;SurfaceFlinger 是"消费者",负责从队列中取出这些画好的 Buffer 进行处理。这种机制实现了渲染和合成的解耦。
第二:接收 WMS 指令与图层合成(Composition)策略。 当 SF 拿到所有可见图层的 Buffer 后,需要把它们叠在一起。但怎么叠、放在屏幕的哪个位置?这就依赖 WMS 作为"总导演",通过 Transaction 跨进程发来的"空间指导书"(包含了计算好的 frame 坐标、Z-Order 层级、透明度等)。SF 会严格按照这些指令将图层摆放好。在具体合成时,有两种主要方式:
- Device 合成(硬件合成):SF 优先把图层交给底层的 HWC(Hardware Composer,硬件合成器)处理,HWC 是专门的显示硬件,处理合成速度极快且极其省电
- Client 合成(GPU 合成):如果图层太多、带有复杂特效,或者超出了 HWC 的处理能力,SF 就会调用 GPU(通过 OpenGL ES 或 Vulkan)先把这些图层合成好,再交给硬件
- 系统优化的核心目标之一就是尽量让图层走 HWC 合成,减少 GPU 的介入以降低功耗
第三:Vsync(垂直同步)信号的节拍器。 SurfaceFlinger 内部维护着 DispSync 机制,它是整个 Android 渲染管线的"心脏"。它负责接收硬件发出的 Vsync 信号,并将其分发给 App(触发 Choreographer 开始下一帧绘制)和 SF 自身(触发合成)。这保证了软件绘制与屏幕硬件刷新的同频共振,是避免画面撕裂(Tearing)和卡顿的关键。
3.3 Layer 层级
SurfaceFlinger
│
└── DisplayDevice
│
└── Layer 树 (与 WMS WindowContainer 树对应)
├── BufferLayer ← 有实际 GraphicBuffer 的层
├── ContainerLayer ← 容器层(无 Buffer,分组管理)
├── ColorLayer ← 纯色层
└── FBTLayer ← Framebuffer Target(调试)
3.4 Buffer 机制
每个 BufferLayer 背后是一个 BufferQueue:
┌──────────────────────────────────────────┐
│ BufferQueue │
│ │
│ Producer (App) ──写入──> GraphicBuffer │
│ │ │
│ Consumer (SF) ──读取──> 合成到屏幕 │
└──────────────────────────────────────────┘
- Producer 端:App 通过 RenderThread 将内容绘制到 GraphicBuffer
- Consumer 端:SurfaceFlinger 读取 Buffer 进行合成
- 共享内存:Buffer 在 App 和 SF 之间通过共享内存传递,零拷贝
4. WMS 与 SurfaceFlinger 的协作关系
4.1 职责边界
WMS(指挥者) SurfaceFlinger(执行者)
──────────────────────── ────────────────────────
决定窗口显示/隐藏 实际将像素输出到屏幕
计算窗口位置/大小 执行 Layer 的变换(translate/scale)
分配 Z 序层级 按 Z 序合成 Buffer
控制透明度/裁剪 应用透明度/裁剪到合成
创建 SurfaceControl 创建对应的 Layer
管理动画策略 执行 Surface 级别的动画变换
4.2 完整数据流(以 Activity 启动为例)
1. AMS 启动 Activity → App 进程创建 DecorView
2. ViewRootImpl 向 WMS 申请 addWindow
3. WMS 创建 WindowState
4. WMS 通过 SurfaceControl 向 SurfaceFlinger 创建对应 Layer
5. SurfaceFlinger 创建 BufferLayer + BufferQueue
6. WMS 将 Surface 返回给 App 进程
7. App 通过 Canvas/RenderThread 绘制到 GraphicBuffer
8. App 调用 queueBuffer → BufferQueue 通知 SF 有新帧
9. 下一次 VSync 到来 → SF 合成所有 Layer → 输出到屏幕
10. WMS performSurfacePlacement → 窗口 draw state 变为 HAS_DRAWN
4.3 一句话总结
WMS 管"逻辑",SF 管"像素"。WMS 通过 SurfaceControl 指挥 SF,两者是命令者与执行者的关系。
5. 窗口绘制状态机
5.1 五阶段状态机
NO_SURFACE (0)
│
│ createSurface()
▼
DRAW_PENDING (1) ← 窗口 Surface 已创建,等待客户端绘制
│
│ 客户端完成绘制,调用 finishDrawing()
▼
COMMIT_DRAW_PENDING (2) ← 客户端已绘制完成,WMS 待确认
│
│ performSurfacePlacement → applySurfaceChangesTransaction
│ → commitFinishDrawingLocked()
▼
READY_TO_SHOW (3) ← 窗口准备好,可以显示
│
│ performShowLocked() 检查所有条件(可见性、Token 状态等)
▼
HAS_DRAWN (4) ← 窗口已在屏幕上显示
5.2 为什么必须经过 performSurfacePlacement
Draw state 的转换不能跳过 performSurfacePlacement 直接完成,原因有三:
(1)同步性
客户端完成绘制后,WMS 还不知道窗口的最终位置、大小、Z 序——这些需要在 performSurfacePlacement 的 computeFrames + layoutWindow 阶段计算。只有计算完帧,才能确认窗口"真的准备好"。
(2)一致性
commitFinishDrawingLocked() 会检查同一 Activity 下所有窗口是否都绘制完成。如果条件不满足,状态会停留在 READY_TO_SHOW,直到下一次 performSurfacePlacement。这保证了同一 Activity 的所有窗口一起显示,不会部分窗口先闪出来。
(3)架构统一
performSurfacePlacement 是 WMS 唯一的全局决策点。除了 draw state 转换,它同时处理窗口位置、Surface 层级、焦点更新、输入窗口更新、亮度控制、动画调度等。绕过它会导致窗口状态和 Surface 状态不一致。
6. 合成路径:HWC vs GPU
6.1 两种合成路径对比
| 维度 | HWC(硬件合成) | GPU 合成(GLES/Vulkan) |
|---|---|---|
| 原理 | 显示控制器直接叠加多个 Buffer | GPU 先将多个 Layer 画到 offscreen buffer |
| 功耗 | 极低(仅传指针) | 较高(像素级运算) |
| 灵活性 | 受硬件限制(Layer 数、分辨率) | 几乎无限制 |
| 延迟 | 低 | 有额外合成延迟 |
| 适用场景 | 静态界面、Layer 少 | 复杂混合模式、特效 |
6.2 SF 的自动选择策略
SurfaceFlinger 每一帧都会通过 HWComposer.prepare() 询问 HWC 驱动:
SF: "这些 Layer 你能硬件叠加吗?"
HWC: "Layer 1-4 可以(DEVICE),Layer 5-8 太多了,退回 GPU(CLIENT)"
SF: 好的,1-4 走 HWC,5-8 走 GPU 合成
分配规则:
- Layer 数量 ≤ HWC 硬件上限(通常 4-8 个)→ 全部 HWC
- Layer 数量 > 上限 → 优先级高的走 HWC,其余走 GPU
- 有复杂混合模式/特效 → GPU(HWC 不支持)
- 视频播放 → SIDEBAND 模式
6.3 开发者选项"强制 GPU 合成"
"启用 GPU 呈现模式分析"或"强制 GPU 合成"是调试开关,打开后 SF 会忽略 HWC 能力判断,所有 Layer 强制走 GPU 路径。仅用于开发调试,正常使用会显著增加功耗和发热。
6.4 Trace 分析方法
| 观察目标 | 怎么看 | 说明 |
|---|---|---|
| GPU 合成 | SurfaceFlinger 主线程 RenderEngine::drawLayers 耗时 | 耗时长 = 大量 GPU 合成 |
| GPU 负载 | Trace 的 GPU 轨道(Frequency / Load) | 波峰 = GPU 活跃 |
| HWC 合成 | commit / prepare / HWComposer 耗时 | 耗时极短 = 正常 HWC 路径 |
判断标准: 滑动桌面或普通 App 时,如果 Trace 里
RenderEngine耗时很长,说明没有充分利用 HWC,是性能 Bug,会导致无谓的发热和耗电。
7. VSync 机制
7.1 基础概念
VSync(Vertical Synchronization)是硬件发出的垂直同步信号,驱动整个系统的绘制节奏。
| 刷新率 | VSync 间隔 | 说明 |
|---|---|---|
| 60Hz | ~16.67ms | 传统标准 |
| 90Hz | ~11.11ms | 中高端机 |
| 120Hz | ~8.33ms | 高刷旗舰 |
| 1Hz | 1000ms | Always On Display |
7.2 VSync 不是无脑发送的
VSync 是动态调度的:
画面静止 ──→ SF 停止分发 VSync ──→ App 不被唤醒绘制 ──→ 省电
↑ ↓
└──── 触摸事件唤醒 ──→ VSync 恢复分发 ──→ App 开始绘制 ←┘
| 状态 | VSync 行为 | 目的 |
|---|---|---|
| 静止 | 停止分发 | 省电,RenderThread 休眠 |
| 交互开始 | 触摸事件提前唤醒 | 降低首帧延迟 |
| 持续交互 | 按刷新率规律发送 | 保证流畅度 |
| 可变刷新率 | 动态调整频率 | 兼顾流畅和功耗 |
8. 部分问题回答
8.1 "怎么理解 WMS"
"WMS 是 Android 系统服务的核心组件,运行在 SystemServer 进程中,负责所有应用窗口的生命周期管理、层级控制、动画过渡和输入事件路由,是应用 UI 和 SurfaceFlinger 之间的桥梁。"
"从架构上看,WMS 通过 WindowContainer 容器树来组织窗口,顶层是 DisplayContent(显示设备),下面是 Task(任务栈)、ActivityRecord(Activity 实例),最底层是 WindowState(具体窗口)。每个节点都对应 SurfaceFlinger 中的一个 Layer,两棵树是镜像关系。"
"AOSP 12+ 引入了 Shell Transition 架构,将动画策略从 WMS 内部剥离到 WMShell 层,WMS 只负责 Surface 级别的执行。这个设计让动画策略可以独立迭代,SystemUI 也能参与控制过渡行为。"
8.2 "怎么理解 SurfaceFlinger"
"SurfaceFlinger 是 Android 的图形合成引擎,运行在独立 native 进程中。核心职责是把各客户端(App、SystemUI、壁纸等)提交的 GraphicBuffer 按 Z 序、透明度合成为一帧输出到屏幕。"
"WMS 负责'指挥'——谁显示、在哪显示、多大、什么层级,通过 SurfaceControl 控制;SF 负责'搬砖'——把实际的像素合成输出。App 通过 BufferQueue 提交数据,WMS 不碰像素。"
"SF 每一帧都会问 HWC 驱动能不能硬件叠加,能就走 HWC(省电),不能就退到 GPU 合成。正常情况下桌面滑动应该全部走 HWC,如果 Trace 里看到 RenderEngine 耗时,说明合成路径有问题。"
8.3 "VSync 是怎么工作的"
"VSync 是整个系统的节拍器。120Hz 就是 8.33ms 一次信号,60Hz 就是 16.67ms。但它不是无脑固定时间发送的——画面静止时 SF 会停止 VSync 分发,App 就不会被唤醒绘制,这是功耗优化的核心。交互触摸事件会提前唤醒 VSync。"
"VSync 和 Shell Transition 不直接相关,但 Shell Transition 的动画帧是靠 VSync 驱动的 AnimationHandler 推进的。VSync 是底层信号,Shell Transition 是上层的动画策略。"
"好的系统要在 120Hz 下保证 VSync 准时送达保证流畅,也要在静止时果断停掉 VSync 节省续航。性能和功耗的平衡,是系统工程师需要持续关注的全局问题。"
文档创建:2026-06-13