319 lines
17 KiB
Markdown
319 lines
17 KiB
Markdown
|
|
好的,遵照您的指示,我将基于您提供的详细提示词和搜索材料,为您创作一份《Android WindowManagerService核心原理深度解析》培训课件。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
# Android WindowManagerService核心原理深度解析
|
|||
|
|
|
|||
|
|
## 目录
|
|||
|
|
1. **引言**:为什么需要深入理解WMS?
|
|||
|
|
2. **WMS概览与架构定位**
|
|||
|
|
- 系统服务中的WMS
|
|||
|
|
- 核心概念定义 (`Window`, `Token`, `State`)
|
|||
|
|
- WMS核心职责
|
|||
|
|
3. **窗口的生命周期管理**
|
|||
|
|
- 从`ViewRootImpl`到`WindowState`的旅程
|
|||
|
|
- 关键流程序列图:添加窗口
|
|||
|
|
- 核心数据结构:`WindowState` 解析
|
|||
|
|
4. **布局、动画与Surface管理**
|
|||
|
|
- 布局循环的核心:`performSurfacePlacement`
|
|||
|
|
- 窗口动画的管理者:`WindowAnimator`
|
|||
|
|
- Surface的申请与合成协调
|
|||
|
|
5. **输入事件的中转与分发**
|
|||
|
|
- WMS作为IMS的中转站
|
|||
|
|
- 关键流程序列图:触摸事件分发
|
|||
|
|
- 命中测试(Hit Test)与`InputChannel`
|
|||
|
|
6. **与关键系统服务的协同**
|
|||
|
|
- WMS & AMS:Activity状态与窗口的同步
|
|||
|
|
- WMS & SurfaceFlinger:通过`Transaction`提交变更
|
|||
|
|
- UML简化类图:核心协作关系
|
|||
|
|
7. **高级主题与案例分析**(可选)
|
|||
|
|
- 从`dumpsys window`看窗口状态
|
|||
|
|
- 常见问题分析:窗口泄漏、事件丢失
|
|||
|
|
8. **总结与Q&A**
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 1. 引言:为什么需要深入理解WMS?
|
|||
|
|
|
|||
|
|
各位同事,在日常开发中,我们面对的挑战往往不止于业务逻辑的实现。当遇到**多窗口适配的诡异布局**、**悬浮窗权限的反复横跳**、**应用启动或转场时的莫名卡顿**,甚至是**触摸事件偶尔“灵异”丢失**的问题时,我们是否感到束手无策,只能依靠试错或搜索碎片化的解决方案?
|
|||
|
|
|
|||
|
|
这些复杂UI场景和性能瓶颈的根源,往往深藏在Android Framework的核心——**WindowManagerService(WMS)** 之中。WMS是Android图形系统的“总调度中心”和“交通警察”,它管理者所有窗口的创建、布局、显示顺序以及它们与输入系统的交互。
|
|||
|
|
|
|||
|
|
本次培训旨在穿透API层面,深入Framework核心,系统性地揭示从**应用视图请求**到**最终像素显示**的完整链路。我们将聚焦WMS在其中扮演的核心角色,帮助大家建立起完整的窗口系统知识体系,从而在面对疑难杂症时,能够**定位根因、精准施策**。
|
|||
|
|
|
|||
|
|
## 2. WMS概览与架构定位
|
|||
|
|
|
|||
|
|
### 2.1 系统服务中的WMS
|
|||
|
|
|
|||
|
|
WMS是System Server中最早启动的核心服务之一。它在SystemServer的`startOtherServices`方法中被创建和启动。
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
// frameworks/base/services/java/com/android/server/SystemServer.java
|
|||
|
|
private void startOtherServices() {
|
|||
|
|
// ...
|
|||
|
|
traceBeginAndSlog("StartWindowManagerService");
|
|||
|
|
// 注意:WMS的main方法运行在"android.display"线程,优先级高于"system_server"线程
|
|||
|
|
wm = WindowManagerService.main(context, inputManager, ...);
|
|||
|
|
traceEnd();
|
|||
|
|
// ...
|
|||
|
|
// 将WMS和IMS注册到ServiceManager
|
|||
|
|
ServiceManager.addService(Context.WINDOW_SERVICE, wm);
|
|||
|
|
ServiceManager.addService(Context.INPUT_SERVICE, inputManager);
|
|||
|
|
// ...
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
*代码示例:WMS在SystemServer中的启动 *
|
|||
|
|
|
|||
|
|
WMS的启动涉及多线程同步(`system_server`, `android.display`, `android.ui`),体现了其初始化对UI相关线程的优先级要求。
|
|||
|
|
|
|||
|
|
### 2.2 核心概念定义
|
|||
|
|
|
|||
|
|
在深入代码前,我们必须统一对以下几个核心抽象的理解:
|
|||
|
|
|
|||
|
|
- **`Window`(窗口)**:一个抽象的概念,不是实体,可以理解为**绘制表面(Surface)** 的载体或容器。每一个窗口都对应着一个用于绘制的`Surface`。在应用层,`PhoneWindow`是`Window`的唯一实现类。
|
|||
|
|
- **`WindowManager`**:应用层与WMS通信的接口。它是一个Binder客户端,应用通过它发送添加、删除窗口的请求。其实现类`WindowManagerImpl`将工作委托给单例的`WindowManagerGlobal`。
|
|||
|
|
- **`View`**:窗口的实体表现形式。窗口的内容正是通过`View`树来填充和呈现的。可以说,**Window是View的载体,View是Window的内容**。
|
|||
|
|
- **`WindowToken`**:一个**令牌**。用于建立Binder通信的映射关系,并**将一组相关的窗口(例如同一个Activity的所有窗口,包括主窗口和子窗口)组织在一起**。对于Activity,它是`AppWindowToken`。
|
|||
|
|
- **`WindowState`**:WMS内部用于**描述一个窗口所有状态**的真实对象。它保存了窗口的尺寸、位置、Z-order、是否可见、对应的`Session`、`InputChannel`等所有信息。它是WMS管理窗口的“活档案”。
|
|||
|
|
- **`DisplayContent`**:用于**描述一块逻辑屏幕**。它管理着显示在这个屏幕上的所有窗口的根层级。WMS通过`DisplayContent`来管理多屏(如双屏、折叠屏)的窗口。
|
|||
|
|
|
|||
|
|
### 2.3 WMS核心职责
|
|||
|
|
|
|||
|
|
1. **窗口管理**:负责窗口的添加、删除、Z-order排序、焦点管理。
|
|||
|
|
2. **Surface管理**:作为“仲裁者”,为窗口分配`Surface`,并协调应用与`SurfaceFlinger`的关系。
|
|||
|
|
3. **窗口动画**:管理所有窗口切换、移动时的动画效果。
|
|||
|
|
4. **输入事件中转**:配合`InputManagerService`,将触摸或按键事件准确地分发给合适的窗口。
|
|||
|
|
|
|||
|
|
## 3. 窗口的生命周期管理
|
|||
|
|
|
|||
|
|
### 3.1 从`ViewRootImpl`到`WindowState`的旅程
|
|||
|
|
|
|||
|
|
以一个典型的Activity窗口创建为例,看看它是如何被WMS纳入管理的。
|
|||
|
|
|
|||
|
|
**核心流程序列图:添加窗口**
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
sequenceDiagram
|
|||
|
|
participant App as 应用进程(UI Thread)
|
|||
|
|
participant ViewRoot as ViewRootImpl
|
|||
|
|
participant WMS as WindowManagerService
|
|||
|
|
participant SF as SurfaceFlinger
|
|||
|
|
|
|||
|
|
App->>ViewRoot: 1. requestLayout()
|
|||
|
|
ViewRoot->>ViewRoot: 2. scheduleTraversals()
|
|||
|
|
ViewRoot->>WMS: 3. relayoutWindow() (Binder调用)
|
|||
|
|
Note over WMS: 4. 创建/获取Surface
|
|||
|
|
WMS->>SF: 5. createSurface() (通过SurfaceSession)
|
|||
|
|
SF-->>WMS: 返回SurfaceControl
|
|||
|
|
WMS-->>ViewRoot: 返回relayoutResult & Surface (Binder返回)
|
|||
|
|
Note over ViewRoot: 6. 将Surface保存到mSurface
|
|||
|
|
ViewRoot->>ViewRoot: 7. performMeasure/Layout/Draw
|
|||
|
|
ViewRoot->>SF: 8. 通过Canvas/Skia渲染到Surface的BufferQueue
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**关键步骤详解:**
|
|||
|
|
|
|||
|
|
1. **发起请求**:当View树需要布局时,`ViewRootImpl`的`requestLayout`被调用,最终通过Binder调用进入WMS的`relayoutWindow`方法。
|
|||
|
|
2. **WMS处理 (核心逻辑)**:
|
|||
|
|
- **权限与合法性检查**:WMS首先检查调用者的权限和窗口参数(`LayoutParams`)是否合法。
|
|||
|
|
- **创建/获取WindowState**:如果是首次添加,WMS会创建一个`WindowState`对象,并将其添加到全局的`mWindowMap`中,同时根据`WindowToken`建立层级关系。
|
|||
|
|
- **Surface分配**:如果窗口需要新的绘图表面,WMS会通过`Session`(内部封装了与SurfaceFlinger的`SurfaceSession`连接)请求`SurfaceFlinger`创建一个`Layer`(在WMS侧对应为`SurfaceControl`)。
|
|||
|
|
3. **返回与绘制**:WMS将分配好的`Surface`(实际上是`SurfaceControl`的句柄)通过Binder返回给应用层的`ViewRootImpl`。应用层拿到`Surface`后,便可以开始渲染内容。
|
|||
|
|
|
|||
|
|
### 3.2 核心数据结构:`WindowState` 解析
|
|||
|
|
|
|||
|
|
`WindowState`是WMS中最重要的数据结构,我们可以通过简化源码来理解其核心成员:
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
// frameworks/base/services/core/java/com/android/server/wm/WindowState.java
|
|||
|
|
class WindowState extends WindowContainer<WindowState> {
|
|||
|
|
// 指向持有该窗口的客户端Session (每个应用进程一个)
|
|||
|
|
final Session mSession;
|
|||
|
|
// 应用层 ViewRootImpl 的 W Binder对象,用于向应用进程通信
|
|||
|
|
final IWindow mClient;
|
|||
|
|
// 与该窗口关联的窗口令牌 (如 AppWindowToken)
|
|||
|
|
final WindowToken mToken;
|
|||
|
|
// 窗口的布局参数 (x, y, width, height, type, flags...)
|
|||
|
|
final WindowManager.LayoutParams mAttrs;
|
|||
|
|
// 窗口所属的逻辑屏幕
|
|||
|
|
final DisplayContent mDisplayContent;
|
|||
|
|
// 窗口在WMS侧控制的Surface句柄,用于设置位置、大小、层级等
|
|||
|
|
SurfaceControl mSurfaceControl;
|
|||
|
|
// 接收输入事件的通道
|
|||
|
|
InputChannel mInputChannel;
|
|||
|
|
// 窗口的帧大小和位置信息 (包含四个矩形:包含框架、内容框架、可见框架等)
|
|||
|
|
final Rect mFrame = new Rect();
|
|||
|
|
final Rect mContentFrame = new Rect();
|
|||
|
|
// 窗口当前的Z-order 值
|
|||
|
|
int mLayer;
|
|||
|
|
// ... 以及其他大量状态和方法
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
*代码示例:WindowState核心成员变量 *
|
|||
|
|
|
|||
|
|
`WindowState`不仅保存了窗口的属性,其本身也参与到`WindowContainer`的层级管理中,构成了WMS内部对窗口树的描述。
|
|||
|
|
|
|||
|
|
## 4. 布局、动画与Surface管理
|
|||
|
|
|
|||
|
|
### 4.1 布局循环的核心:`performSurfacePlacement`
|
|||
|
|
|
|||
|
|
WMS的核心工作引擎是`performSurfacePlacement`(旧称`performLayoutAndPlaceSurfacesLocked`)。这个方法会在窗口状态发生变化(如添加窗口、更改大小、动画更新)时被触发。
|
|||
|
|
|
|||
|
|
**它的主要任务**是遍历所有`DisplayContent`及其下的`WindowState`,计算每个窗口最终的**位置、大小和Z-order**。然后,它会将这些变更通过`SurfaceControl.Transaction`提交给SurfaceFlinger。
|
|||
|
|
|
|||
|
|
### 4.2 窗口动画的管理者:`WindowAnimator`
|
|||
|
|
|
|||
|
|
WMS内部有一个`WindowAnimator`对象,专门负责管理所有窗口动画。
|
|||
|
|
- 当一个窗口需要执行动画(如启动、退出、旋转)时,WMS会创建相应的动画对象(如`MoveAnimation`, `AlphaAnimation`)。
|
|||
|
|
- `WindowAnimator`会定期(在VSync信号驱动下)更新动画状态,并再次触发`performSurfacePlacement`,将动画过程中的新位置、透明度等属性通过`Transaction`设置给SurfaceFlinger,从而实现流畅的动画效果。
|
|||
|
|
|
|||
|
|
### 4.3 Surface的申请与合成协调
|
|||
|
|
|
|||
|
|
WMS并不负责绘制,它只负责**“布局”和“定序”**。
|
|||
|
|
- **应用侧Surface**:由`ViewRootImpl`持有,用于应用线程绘制UI。
|
|||
|
|
- **WMS侧SurfaceControl**:由`WindowState`持有,用于WMS控制该窗口在屏幕上的最终呈现属性。
|
|||
|
|
- **与SurfaceFlinger的交互**:WMS通过一系列的`Transaction`,将窗口的**位置、大小、旋转、透明度、Z-order**等信息同步给SurfaceFlinger。SurfaceFlinger则根据这些信息,将所有窗口的Surface(图形缓冲区)进行混合(Compose),最终输出到屏幕硬件。
|
|||
|
|
|
|||
|
|
## 5. 输入事件的中转与分发
|
|||
|
|
|
|||
|
|
### 5.1 WMS作为IMS的中转站
|
|||
|
|
|
|||
|
|
`InputManagerService`(IMS)负责从内核读取原始输入事件,但它不知道这些事件该发给谁。这时,WMS作为窗口的管理者,就成为了理想的“导航员”。
|
|||
|
|
|
|||
|
|
### 5.2 关键流程序列图:触摸事件分发
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
sequenceDiagram
|
|||
|
|
participant IMS as InputManagerService
|
|||
|
|
participant WMS as WindowManagerService
|
|||
|
|
participant Dispatcher as InputDispatcher (Native)
|
|||
|
|
participant AppView as 目标窗口 (View)
|
|||
|
|
|
|||
|
|
IMS->>Dispatcher: 1. 读取并派发事件
|
|||
|
|
Dispatcher->>WMS: 2. 查询目标窗口 (通过InputMonitor)
|
|||
|
|
WMS-->>Dispatcher: 3. 返回焦点窗口及其InputChannel
|
|||
|
|
Dispatcher->>Dispatcher: 4. 命中测试,确定目标
|
|||
|
|
Dispatcher->>AppView: 5. 通过InputChannel发送事件 (Socket)
|
|||
|
|
AppView->>AppView: 6. ViewRootImpl$WindowInputEventReceiver 处理
|
|||
|
|
AppView->>AppView: 7. 事件分发 (dispatchTouchEvent)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 5.3 命中测试与`InputChannel`
|
|||
|
|
|
|||
|
|
1. **建立通道**:在窗口添加时(`WMS.addWindow`),如果窗口需要接收输入,WMS会为它创建一对`InputChannel`(基于Socket)。服务端的一端注册到IMS的`InputDispatcher`,客户端的一端通过Binder返回给`ViewRootImpl`,并封装成`WindowInputEventReceiver`。
|
|||
|
|
```java
|
|||
|
|
// WMS.addWindow 中创建InputChannel的片段
|
|||
|
|
if (outInputChannel != null && (attrs.inputFeatures & ...) == 0) {
|
|||
|
|
String name = win.makeInputChannelName();
|
|||
|
|
InputChannel[] inputChannels = InputChannel.openInputChannelPair(name); // 创建Socket对
|
|||
|
|
win.setInputChannel(inputChannels[0]);
|
|||
|
|
inputChannels[1].transferTo(outInputChannel); // 将客户端Channel返回
|
|||
|
|
mInputManager.registerInputChannel(win.mInputChannel, win.mInputWindowHandle); // 注册服务端Channel
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
*代码示例:InputChannel的创建与注册 *
|
|||
|
|
|
|||
|
|
2. **命中测试**:当IMS的`InputDispatcher`有事件到来时,它会调用WMS(通过`InputMonitor`)获取当前所有窗口的信息(Z-order、位置、焦点状态)。`InputDispatcher`执行**命中测试**,找到最合适的窗口,然后通过之前建立的Socket连接(`InputChannel`)直接将事件发送给目标应用进程。
|
|||
|
|
|
|||
|
|
3. **应用内处理**:应用进程的`WindowInputEventReceiver`收到事件后,将其传递给`ViewRootImpl`,进而开始我们在应用层熟悉的`View`树事件分发流程。
|
|||
|
|
|
|||
|
|
## 6. 与关键系统服务的协同
|
|||
|
|
|
|||
|
|
### 6.1 WMS & AMS:状态同步
|
|||
|
|
|
|||
|
|
AMS和WMS是一对紧密协作的“好基友”。以Activity启动为例:
|
|||
|
|
1. **AMS准备**:AMS在启动一个Activity时,首先会调用WMS的`addAppToken`,告诉WMS:“我要启动一个Activity了,这是它的令牌(`AppWindowToken`)”。WMS将其加入到`mAppTokens`列表中。
|
|||
|
|
2. **WMS同步**:当Activity在应用进程创建完毕,并通过`ViewRootImpl`请求添加窗口时,WMS会通过`AppWindowToken`找到这个窗口属于哪个Activity,并将其与已有的`Token`关联起来。
|
|||
|
|
3. **状态回调**:当窗口显示或隐藏时,WMS也会回调AMS的方法,通知它窗口的`finishedStarting`、`finishedPausing`等状态,以便AMS继续执行生命周期的下一步。
|
|||
|
|
|
|||
|
|
### 6.2 UML简化类图:核心协作关系
|
|||
|
|
|
|||
|
|
下图展示了WMS、AMS及其核心内部类与周边服务的关系,有助于建立宏观印象。
|
|||
|
|
|
|||
|
|
```mermaid
|
|||
|
|
classDiagram
|
|||
|
|
class WindowManagerService {
|
|||
|
|
- InputManagerService mInputManager
|
|||
|
|
- ActivityManagerService mActivityManager
|
|||
|
|
- WindowAnimator mAnimator
|
|||
|
|
- HashMap~IBinder, WindowState~ mWindowMap
|
|||
|
|
- ArrayList~AppWindowToken~ mAppTokens
|
|||
|
|
+ addWindow()
|
|||
|
|
+ relayoutWindow()
|
|||
|
|
+ performSurfacePlacement()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
class ActivityManagerService {
|
|||
|
|
+ startActivity()
|
|||
|
|
+ resumeTopActivity()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
class InputManagerService {
|
|||
|
|
- InputDispatcher mDispatcher
|
|||
|
|
+ registerInputChannel()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
class SurfaceFlinger {
|
|||
|
|
+ createSurface()
|
|||
|
|
+ setTransaction()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
class WindowState {
|
|||
|
|
- IWindow mClient
|
|||
|
|
- WindowToken mToken
|
|||
|
|
- SurfaceControl mSurfaceControl
|
|||
|
|
- InputChannel mInputChannel
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
class AppWindowToken {
|
|||
|
|
- IApplicationToken mAppToken
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
class DisplayContent {
|
|||
|
|
- int mDisplayId
|
|||
|
|
- WindowList mWindows
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
WindowManagerService --> ActivityManagerService : 持有引用
|
|||
|
|
WindowManagerService --> InputManagerService : 持有引用
|
|||
|
|
WindowManagerService --> SurfaceFlinger : Binder通信
|
|||
|
|
WindowManagerService *-- WindowAnimator
|
|||
|
|
WindowManagerService *-- WindowState : 管理 (mWindowMap)
|
|||
|
|
WindowManagerService *-- AppWindowToken : 管理 (mAppTokens)
|
|||
|
|
WindowManagerService *-- DisplayContent : 管理
|
|||
|
|
WindowState --> WindowToken
|
|||
|
|
AppWindowToken --|> WindowToken
|
|||
|
|
DisplayContent --> WindowState : 包含
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 7. 高级主题与案例分析(可选)
|
|||
|
|
|
|||
|
|
### 7.1 从`dumpsys window`看窗口状态
|
|||
|
|
|
|||
|
|
`adb shell dumpsys window`是我们调试窗口问题的利器。通过它,我们可以看到:
|
|||
|
|
- **`Display`信息**:当前有几个逻辑屏幕,分辨率如何。
|
|||
|
|
- **`Window Hierarchy`**:当前所有窗口的堆叠顺序,最上层的是焦点窗口。
|
|||
|
|
- **`WindowState`详情**:每个窗口的包名、token、`mSurfaceControl`是否有效、`mFrame`坐标、`mInputChannel`状态等。
|
|||
|
|
- **`Focus`**:当前焦点窗口是哪个。
|
|||
|
|
|
|||
|
|
**案例分析:** 当遇到窗口无法显示时,可以通过`dumpsys window`查看对应`WindowState`是否存在。如果存在,检查`mSurfaceControl`是否有效;如果不存在,说明窗口添加失败,可能是权限或token问题。
|
|||
|
|
|
|||
|
|
### 7.2 常见问题分析
|
|||
|
|
|
|||
|
|
- **问题1:悬浮窗无法显示或显示位置不对**
|
|||
|
|
- **可能原因**:未正确申请`SYSTEM_ALERT_WINDOW`权限;LayoutParams中的`type`设置错误;在Android 10+上,未对`Display`进行适配。
|
|||
|
|
- **WMS视角**:检查`dumpsys window`中该悬浮窗的`WindowState`是否存在,以及其`mFrame`和`mAttrs.gravity`是否符合预期。
|
|||
|
|
|
|||
|
|
- **问题2:触摸事件“被窃取”或无法传递到目标View**
|
|||
|
|
- **可能原因**:有其它窗口(如系统弹窗、输入法窗口)覆盖在目标窗口之上,但被设置为可触摸(`FLAG_NOT_TOUCH_MODAL`处理不当);焦点窗口不是你期望的那个。
|
|||
|
|
- **WMS视角**:通过`dumpsys window`查看`Window Hierarchy`,确认Z-order的顺序。点击事件会首先发给Z-order最大的窗口。检查焦点窗口`Focus`是否正确。
|
|||
|
|
|
|||
|
|
## 8. 总结与Q&A
|
|||
|
|
|
|||
|
|
### 总结
|
|||
|
|
WMS作为Android GUI系统的“大脑”,其核心价值在于:
|
|||
|
|
1. **集中管理**:通过`WindowState`、`WindowToken`和`DisplayContent`,统一维护所有窗口的状态和层级。
|
|||
|
|
2. **协调调度**:作为中心枢纽,协调应用进程的绘制请求(`relayoutWindow`)、输入系统的派发请求(命中测试)以及SurfaceFlinger的合成请求(`Transaction`)。
|
|||
|
|
3. **状态同步**:与AMS深度绑定,确保Activity的生命周期与窗口的显示状态同步。
|
|||
|
|
|
|||
|
|
深入理解WMS,不仅能帮助我们诊断疑难杂症,更能让我们对整个Android系统的设计哲学有更深刻的体会。
|