WMS窗口管理
# 11.WMS窗口管理
# 目录介绍
- 01.为什么需要窗口管理系统
- 1.1 多窗口的核心挑战
- 1.2 WMS的核心职责
- 1.3 WMS在系统中的位置
- 02.WMS的整体架构设计
- 2.1 核心类关系
- 2.2 WMS的线程模型
- 2.3 Session机制
- 03.WMS的启动与初始化
- 3.1 WMS的创建时机
- 3.2 WMS构造过程
- 3.3 Display初始化
- 04.Window的类型与层级体系
- 4.1 窗口类型分类
- 4.2 窗口层级排序
- 4.3 子窗口的层级计算
- 05.WindowState的核心数据结构
- 5.1 WindowState详解
- 5.2 WindowToken的作用
- 5.3 DisplayContent的结构
- 06.addWindow的完整流程
- 6.1 应用端发起addWindow
- 6.2 WMS.addWindow核心逻辑
- 6.3 addWindow的安全检查
- 07.removeWindow的流程
- 7.1 窗口移除的触发方式
- 7.2 removeWindow核心逻辑
- 08.Surface与窗口的关系
- 8.1 Surface的本质
- 8.2 Surface的创建时机
- 8.3 SurfaceControl事务
- 09.SurfaceFlinger的合成原理
- 9.1 SurfaceFlinger的角色
- 9.2 BufferQueue三缓冲机制
- 9.3 VSync与帧调度
- 10.窗口布局与尺寸计算
- 10.1 布局计算时机
- 10.2 WindowSurfacePlacer的工作
- 10.3 Insets系统
- 11.输入法窗口的特殊管理
- 11.1 输入法窗口的特殊性
- 11.2 输入法窗口的层级管理
- 11.3 输入法弹出的窗口调整
- 12.Dialog与PopupWindow的窗口原理
- 12.1 Dialog的窗口机制
- 12.2 为什么不能用Application Context显示Dialog
- 12.3 PopupWindow的窗口机制
- 13.窗口动画系统
- 13.1 动画类型
- 13.2 动画系统架构
- 13.3 窗口动画的执行
- 14.多屏幕与折叠屏窗口管理
- 14.1 多屏幕架构
- 14.2 分屏模式的实现
- 14.3 折叠屏的窗口适配
- 15.WindowContainer层级树详解
- 15.1 WindowContainer继承体系
- 15.2 Z-Order的计算方式
- 15.3 完整的窗口层级树示例
- 16.WMS的性能优化与调试
- 16.1 WMS相关的性能问题
- 16.2 WMS调试命令
- 16.3 使用Systrace分析WMS
- 17.总结与技术思考
- 17.1 核心要点
- 17.2 WMS与其他服务的关系
- 17.3 学习建议
- 18.Window与DecorView创建过程
- 18.1 Activity组件UI实现
- 18.2 PhoneWindow创建过程
- 18.3 DecorView创建过程
- 18.4 ViewRootImpl创建过程
- 18.5 Dialog中Window创建
- 18.6 四者关系总结
# 01.为什么需要窗口管理系统
# 1.1 多窗口的核心挑战
在Android系统中,屏幕上同时可能存在多个窗口:Activity窗口、状态栏窗口、导航栏窗口、输入法窗口、Toast窗口、Dialog窗口等。这些窗口需要按照特定的规则进行排列、显示和交互。
疑惑:为什么不能让每个应用自己管理自己的窗口?
答疑:如果每个应用自己管理窗口,会产生以下问题:
- 层级冲突:两个应用都想显示在最上面,谁说了算?
- 输入抢占:屏幕上有多个窗口,触摸事件应该发给谁?
- 资源浪费:每个窗口都直接操作硬件显示,效率极低
- 安全隐患:恶意应用可以覆盖系统UI,伪造界面
论证:所以需要一个集中式的窗口管理者——WindowManagerService(WMS),运行在SystemServer进程中,统一管理所有窗口的创建、布局、显示和销毁。
# 1.2 WMS的核心职责
WMS的职责:
├── 窗口管理:创建、销毁、布局、层级排序
├── 窗口动画:转场动画、窗口动画、系统动画
├── 输入管理:将输入事件分发到正确的窗口
├── Surface管理:管理窗口的绘制表面
├── 焦点管理:决定哪个窗口获得输入焦点
└── 屏幕管理:多屏幕、分屏、画中画
2
3
4
5
6
7
# 1.3 WMS在系统中的位置
WMS在系统架构中的位置:
应用层
┌──────────────────┐
│ WindowManager │ ← 应用通过WindowManager接口添加窗口
│ (ViewManager) │
└────────┬─────────┘
│ Binder IPC
┌────────▼─────────┐
│ WMS │ ← 窗口逻辑管理
│ (SystemServer) │
└────────┬─────────┘
│
┌────────▼─────────┐
│ SurfaceFlinger │ ← 图层合成与显示
│ (Native进程) │
└────────┬─────────┘
│
┌────────▼─────────┐
│ Hardware (HWC) │ ← 硬件合成器
│ (HAL层) │
└──────────────────┘
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 02.WMS的整体架构设计
# 2.1 核心类关系
WMS核心类关系:
┌──────────────────────────────────────────────┐
│ WindowManagerService │
│ ┌───────────────────────────────────────┐ │
│ │ RootWindowContainer │ │
│ │ ├── DisplayContent (屏幕) │ │
│ │ │ ├── DisplayArea.Root │ │
│ │ │ │ ├── TaskDisplayArea │ │
│ │ │ │ │ └── Task │ │
│ │ │ │ │ └── ActivityRecord │ │
│ │ │ │ │ └── WindowState │ │
│ │ │ │ ├── DisplayArea (StatusBar) │ │
│ │ │ │ │ └── WindowToken │ │
│ │ │ │ │ └── WindowState │ │
│ │ │ │ └── DisplayArea (NavBar) │ │
│ │ │ │ └── WindowToken │ │
│ │ │ └── ImeContainer │ │
│ │ │ └── WindowState (输入法) │ │
│ │ └── DisplayContent (第二屏幕) │ │
│ └───────────────────────────────────────┘ │
│ ┌────────────────┐ ┌───────────────────┐ │
│ │ WindowAnimator │ │ InputManagerSvc │ │
│ │ (动画管理) │ │ (输入事件分发) │ │
│ └────────────────┘ └───────────────────┘ │
│ ┌────────────────┐ ┌───────────────────┐ │
│ │ WindowSurfacePl │ │ AccessibilityCtrl │ │
│ │ acer (布局计算) │ │ (无障碍) │ │
│ └────────────────┘ └───────────────────┘ │
└──────────────────────────────────────────────┘
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 2.2 WMS的线程模型
WMS的操作必须在特定线程中执行,这是保证线程安全的关键:
// WindowManagerService.java
class WindowManagerService extends IWindowManager.Stub {
// WMS使用AMS的Handler线程(android.display线程)
final Handler mH;
// 全局锁,WMS所有操作都需要持有此锁
final WindowManagerGlobalLock mGlobalLock;
// 动画线程
final WindowAnimator mAnimator;
// SurfaceFlinger事务处理线程
final SurfaceAnimationRunner mSurfaceAnimationRunner;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
WMS使用单一锁mGlobalLock(在Android 10+中与ATMS共享同一个锁)来保护所有窗口状态的修改。这意味着所有窗口操作都是串行的,避免了复杂的并发问题。
# 2.3 Session机制
每个应用进程与WMS之间通过Session通信:
// Session.java - 应用进程与WMS的会话
class Session extends IWindowSession.Stub {
final WindowManagerService mService;
final int mUid; // 应用UID
final int mPid; // 应用PID
SurfaceSession mSurfaceSession; // 与SurfaceFlinger的连接
// 添加窗口
@Override
public int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, int userId,
InsetsVisibilities requestedVisibilities, InputChannel outInputChannel,
InsetsState outInsetsState, InsetsSourceControl[] outActiveControls) {
return mService.addWindow(this, window, attrs, viewVisibility, displayId,
userId, requestedVisibilities, outInputChannel, outInsetsState,
outActiveControls);
}
// 移除窗口
@Override
public void remove(IWindow window) {
mService.removeWindow(this, window);
}
// 更新窗口布局
@Override
public int relayout(IWindow window, WindowManager.LayoutParams attrs,
int requestedWidth, int requestedHeight, int viewVisibility, ...) {
return mService.relayoutWindow(this, window, attrs,
requestedWidth, requestedHeight, ...);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 03.WMS的启动与初始化
# 3.1 WMS的创建时机
WMS在SystemServer的startOtherServices阶段创建:
// SystemServer.java
private void startOtherServices() {
// WMS在AMS之后、其他服务之前创建
WindowManagerService wm = WindowManagerService.main(context,
inputManager, !mFirstBoot, mOnlyCore,
new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);
// 注册到ServiceManager
ServiceManager.addService(Context.WINDOW_SERVICE, wm, false,
DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);
// 与InputManager关联
inputManager.setWindowManagerCallbacks(wm.getInputManagerCallback());
// 初始化显示
wm.displayReady();
// 系统启动完成后
wm.systemReady();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 3.2 WMS构造过程
// WindowManagerService.java
private WindowManagerService(Context context, InputManagerService inputManager,
boolean showBootMsgs, boolean onlyCore, WindowManagerPolicy policy,
ActivityTaskManagerService atm) {
// 1.初始化核心组件
mContext = context;
mInputManager = inputManager;
mPolicy = policy; // PhoneWindowManager
mAtmService = atm;
// 2.初始化全局锁(与ATMS共享)
mGlobalLock = atm.getGlobalLock();
// 3.创建动画器
mAnimator = new WindowAnimator(this);
mSurfaceAnimationRunner = new SurfaceAnimationRunner(
mTransactionFactory, mPowerManagerInternal);
// 4.创建窗口容器根节点
mRoot = new RootWindowContainer(this);
// 5.初始化窗口布局器
mWindowPlacerLocked = new WindowSurfacePlacer(this);
// 6.创建DisplayManager连接
mDisplayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
mDisplayManager.registerDisplayListener(this, null);
// 7.初始化策略
mPolicy.init(context, this, mActivityManager);
// 8.初始化DragAndDrop控制器
mDragDropController = new DragDropController(this, mH.getLooper());
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 3.3 Display初始化
// WindowManagerService.java
public void displayReady() {
// 获取所有显示设备
final int[] displayIds = mDisplayManager.getDisplayIds();
for (int displayId : displayIds) {
// 为每个显示设备创建DisplayContent
final Display display = mDisplayManager.getDisplay(displayId);
createDisplayContentLocked(display);
}
synchronized (mGlobalLock) {
// 配置默认显示设备
final DisplayContent displayContent = getDefaultDisplayContentLocked();
// 读取显示设置
readForcedDisplayPropertiesLocked(displayContent);
// 初始化系统UI区域
mPolicy.setInitialDisplaySize(
displayContent.getDisplay(),
displayContent.mBaseDisplayWidth,
displayContent.mBaseDisplayHeight,
displayContent.mBaseDisplayDensity);
}
// 标记显示已就绪
mDisplayReady = true;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 04.Window的类型与层级体系
# 4.1 窗口类型分类
Android将窗口分为三大类,每类有不同的层级范围:
// WindowManager.LayoutParams 中的窗口类型定义
public static class LayoutParams {
// === 应用窗口 (1~99) ===
public static final int FIRST_APPLICATION_WINDOW = 1;
public static final int TYPE_BASE_APPLICATION = 1; // 应用基础窗口
public static final int TYPE_APPLICATION = 2; // 普通Activity窗口
public static final int TYPE_APPLICATION_STARTING = 3; // 启动窗口(Splash)
public static final int TYPE_DRAWN_APPLICATION = 4; // 绘制完成的应用窗口
public static final int LAST_APPLICATION_WINDOW = 99;
// === 子窗口 (1000~1999) ===
public static final int FIRST_SUB_WINDOW = 1000;
public static final int TYPE_APPLICATION_PANEL = 1000; // 面板窗口
public static final int TYPE_APPLICATION_MEDIA = 1001; // 媒体窗口
public static final int TYPE_APPLICATION_SUB_PANEL = 1002;
public static final int TYPE_APPLICATION_ATTACHED_DIALOG = 1003; // 附属Dialog
public static final int TYPE_APPLICATION_MEDIA_OVERLAY = 1004;
public static final int LAST_SUB_WINDOW = 1999;
// === 系统窗口 (2000~2999) ===
public static final int FIRST_SYSTEM_WINDOW = 2000;
public static final int TYPE_STATUS_BAR = 2000; // 状态栏
public static final int TYPE_SEARCH_BAR = 2001; // 搜索栏
public static final int TYPE_PHONE = 2002; // 来电窗口
public static final int TYPE_SYSTEM_ALERT = 2003; // 系统弹窗
public static final int TYPE_TOAST = 2005; // Toast
public static final int TYPE_SYSTEM_OVERLAY = 2006; // 系统覆盖层
public static final int TYPE_INPUT_METHOD = 2011; // 输入法
public static final int TYPE_WALLPAPER = 2013; // 壁纸
public static final int TYPE_STATUS_BAR_PANEL = 2014; // 状态栏面板
public static final int TYPE_APPLICATION_OVERLAY = 2038; // 应用悬浮窗(Android 8.0+)
public static final int TYPE_NAVIGATION_BAR = 2019; // 导航栏
public static final int LAST_SYSTEM_WINDOW = 2999;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# 4.2 窗口层级排序
WMS使用Z-Order来确定窗口的显示顺序。层级越高的窗口显示在越上面:
屏幕上窗口的Z-Order层级(从高到低):
Layer 36: TYPE_POINTER (鼠标指针)
Layer 35: TYPE_NAVIGATION_BAR_PANEL
Layer 34: TYPE_SECURE_SYSTEM_OVERLAY
Layer 33: TYPE_BOOT_PROGRESS (开机动画)
Layer 32: TYPE_DISPLAY_OVERLAY
...
Layer 25: TYPE_STATUS_BAR (状态栏)
Layer 24: TYPE_STATUS_BAR_PANEL
...
Layer 20: TYPE_NAVIGATION_BAR (导航栏)
...
Layer 17: TYPE_VOLUME_OVERLAY (音量条)
Layer 16: TYPE_SYSTEM_OVERLAY
Layer 15: TYPE_APPLICATION_OVERLAY (悬浮窗)
Layer 14: TYPE_TOAST
...
Layer 12: TYPE_INPUT_METHOD (输入法)
Layer 11: TYPE_INPUT_METHOD_DIALOG
...
Layer 5: TYPE_SYSTEM_ALERT
Layer 4: TYPE_PHONE
...
Layer 2: TYPE_APPLICATION (Activity窗口)
Layer 1: TYPE_BASE_APPLICATION
Layer 0: TYPE_WALLPAPER (壁纸)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
WMS中层级排序的实现:
// WindowManagerPolicy.java (PhoneWindowManager)
// 将窗口类型映射到层级值
default int getWindowLayerFromTypeLw(int type) {
if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
return APPLICATION_LAYER; // 2
}
switch (type) {
case TYPE_WALLPAPER:
return 1;
case TYPE_PHONE:
return 3;
case TYPE_SEARCH_BAR:
case TYPE_VOICE_INTERACTION_STARTING:
return 4;
case TYPE_SYSTEM_ALERT:
return 5;
case TYPE_TOAST:
return 8;
case TYPE_INPUT_METHOD:
return 12;
case TYPE_APPLICATION_OVERLAY:
return 15;
case TYPE_STATUS_BAR:
return 25;
case TYPE_NAVIGATION_BAR:
return 20;
// ...
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 4.3 子窗口的层级计算
子窗口的层级基于父窗口计算:
子窗口层级规则:
父窗口层级 = Application Layer (2)
子窗口层级 = 父窗口层级的相对偏移
TYPE_APPLICATION_PANEL (-2) → 在父窗口下方
TYPE_APPLICATION_MEDIA (-1) → 在父窗口下方
TYPE_APPLICATION_SUB_PANEL (1) → 在父窗口上方
TYPE_APPLICATION_ATTACHED_DIALOG (1) → 在父窗口上方
示例:SurfaceView的窗口
父窗口(Activity): Z = 某个值
SurfaceView窗口: Z = 父窗口Z - 1 (TYPE_APPLICATION_MEDIA)
这就是为什么SurfaceView显示在Activity内容之下,通过"挖洞"方式显示
2
3
4
5
6
7
8
9
10
11
12
13
# 05.WindowState的核心数据结构
# 5.1 WindowState详解
WindowState是WMS中描述每个窗口的核心数据结构,类似于AMS中的ActivityRecord:
class WindowState extends WindowContainer<WindowState> {
// === 身份标识 ===
final IWindow mClient; // 应用端的IWindow.Stub
final WindowToken mToken; // 所属的WindowToken
final Session mSession; // 所属的会话
final WindowManager.LayoutParams mAttrs; // 窗口属性
final int mBaseLayer; // 基础层级
final int mSubLayer; // 子层级
// === 窗口尺寸 ===
final Rect mRequestedWidth; // 请求的宽度
final Rect mRequestedHeight; // 请求的高度
final Rect mFrame; // 最终的窗口位置和大小
final Rect mDisplayFrame; // 显示区域
final Rect mContentFrame; // 内容区域
final Rect mVisibleFrame; // 可见区域
// === 窗口状态 ===
boolean mHasSurface; // 是否有Surface
boolean mRemoved; // 是否已移除
boolean mViewVisibility; // 视图可见性
boolean mPolicyVisibility; // 策略可见性
int mAnimatingExit; // 是否正在退出动画
// === Surface相关 ===
WindowSurfaceController mWinAnimator; // 动画控制器
SurfaceControl mSurfaceControl; // Surface控制句柄
// === 输入相关 ===
final InputWindowHandle mInputWindowHandle; // 输入窗口句柄
boolean mRemoveOnExit; // 退出时是否移除
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
# 5.2 WindowToken的作用
WindowToken是窗口的分组令牌,同一个WindowToken下的窗口属于同一个逻辑组:
// WindowToken.java
class WindowToken extends WindowContainer<WindowState> {
final IBinder token; // 令牌标识
final int windowType; // 窗口类型
// Activity的窗口对应的是ActivityRecord(ActivityRecord继承自WindowToken)
// 系统窗口对应的是WindowToken本身
}
2
3
4
5
6
7
8
WindowToken的组织关系:
Activity窗口:
ActivityRecord(extends WindowToken)
├── WindowState (主窗口)
├── WindowState (子窗口:Dialog)
└── WindowState (子窗口:PopupWindow)
输入法窗口:
WindowToken(TYPE_INPUT_METHOD)
└── WindowState (输入法窗口)
Toast窗口:
WindowToken(TYPE_TOAST)
└── WindowState (Toast窗口)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 5.3 DisplayContent的结构
每个物理屏幕对应一个DisplayContent,它是窗口层级树的根:
class DisplayContent extends RootDisplayArea {
final int mDisplayId; // 显示设备ID
private final Display mDisplay; // Display对象
// 显示尺寸信息
int mBaseDisplayWidth;
int mBaseDisplayHeight;
int mBaseDisplayDensity;
// 旋转状态
int mRotation;
int mLastOrientation;
// 焦点窗口
WindowState mCurrentFocus;
WindowState mLastFocus;
// 输入法目标窗口
WindowState mInputMethodTarget;
// 壁纸目标窗口
WindowState mWallpaperTarget;
// 显示区域布局
DisplayArea.Root mRootDisplayArea;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 06.addWindow的完整流程
# 6.1 应用端发起addWindow
当应用调用WindowManager.addView()时,最终会触发WMS的addWindow:
addWindow的完整调用链:
应用进程 SystemServer进程
WindowManager.addView(view, params)
│
└── WindowManagerImpl.addView()
└── WindowManagerGlobal.addView()
├── 创建ViewRootImpl
└── ViewRootImpl.setView()
├── 1.requestLayout() → 触发首次布局
└── 2.mWindowSession.addToDisplay()
│
└── ──Binder──► Session.addToDisplayAsUser()
│
└── WMS.addWindow()
├── 检查权限
├── 创建WindowState
├── 添加到窗口树
├── 创建InputChannel
└── 创建SurfaceSession
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 6.2 WMS.addWindow核心逻辑
// WindowManagerService.java
public int addWindow(Session session, IWindow client,
LayoutParams attrs, int viewVisibility, int displayId,
int requestUserId, InsetsVisibilities requestedVisibilities,
InputChannel outInputChannel, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls) {
// 1.权限检查
int res = mPolicy.checkAddPermission(attrs.type, isRoundedCornerOverlay,
attrs.packageName, appOp);
if (res != WindowManagerGlobal.ADD_OKAY) {
return res; // 无权限,拒绝添加
}
synchronized (mGlobalLock) {
// 2.获取DisplayContent
final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token);
if (displayContent == null) {
return WindowManagerGlobal.ADD_INVALID_DISPLAY;
}
// 3.检查窗口是否已存在
if (mWindowMap.containsKey(client.asBinder())) {
return WindowManagerGlobal.ADD_DUPLICATE_ADD;
}
// 4.查找WindowToken
WindowToken token = displayContent.getWindowToken(attrs.token);
// 5.对于应用窗口,token必须已存在(由AMS创建)
if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
ActivityRecord activity = token != null ? token.asActivityRecord() : null;
if (activity == null) {
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
}
// 6.对于系统窗口,可能需要创建token
if (token == null) {
if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
}
token = new WindowToken.Builder(this, client.asBinder(), type)
.setDisplayContent(displayContent)
.build();
}
// 7.创建WindowState
final WindowState win = new WindowState(this, session, client, token,
parentWindow, appOp, attrs, viewVisibility, session.mUid,
session.mCanAddInternalSystemWindow);
// 8.检查窗口是否可以添加
res = displayPolicy.validateAddingWindowLw(attrs, callingPid, callingUid);
if (res != ADD_OKAY) {
return res;
}
// 9.创建输入通道(用于接收触摸事件)
final boolean openInputChannels = (outInputChannel != null
&& (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);
if (openInputChannels) {
win.openInputChannel(outInputChannel);
}
// 10.将WindowState添加到窗口树
win.mToken.addWindow(win);
// 11.注册到全局Map
mWindowMap.put(client.asBinder(), win);
// 12.设置输入窗口
win.mInputWindowHandle.setFocusable(focusable);
mInputManagerService.registerInputChannel(win.mInputChannel);
// 13.如果窗口可见,准备Surface
if (win.isVisible()) {
displayContent.assignWindowLayers(false);
}
// 14.更新焦点窗口
if (focusChanged) {
mInputManagerService.setInputWindows(
displayContent.getInputWindowHandleList(), displayContent);
}
}
return res;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# 6.3 addWindow的安全检查
WMS在添加窗口时会进行严格的安全检查:
// PhoneWindowManager.java
public int checkAddPermission(int type, boolean isRoundedCornerOverlay,
String packageName, int[] outAppOp) {
// 应用窗口和子窗口不需要特殊权限
if (type < FIRST_SYSTEM_WINDOW || type > LAST_SYSTEM_WINDOW) {
return ADD_OKAY;
}
// 系统窗口需要检查权限
switch (type) {
case TYPE_TOAST:
// Toast不需要权限,但有数量限制
outAppOp[0] = OP_TOAST_WINDOW;
return ADD_OKAY;
case TYPE_APPLICATION_OVERLAY:
// 悬浮窗需要SYSTEM_ALERT_WINDOW权限
if (!Settings.canDrawOverlays(mContext)) {
return ADD_PERMISSION_DENIED;
}
outAppOp[0] = OP_SYSTEM_ALERT_WINDOW;
return ADD_OKAY;
case TYPE_INPUT_METHOD:
case TYPE_STATUS_BAR:
case TYPE_NAVIGATION_BAR:
// 需要INTERNAL_SYSTEM_WINDOW权限(仅系统应用)
if (mContext.checkCallingOrSelfPermission(INTERNAL_SYSTEM_WINDOW)
!= PERMISSION_GRANTED) {
return ADD_PERMISSION_DENIED;
}
return ADD_OKAY;
default:
// 其他系统窗口需要SYSTEM_ALERT_WINDOW权限
return mContext.checkCallingOrSelfPermission(SYSTEM_ALERT_WINDOW)
== PERMISSION_GRANTED ? ADD_OKAY : ADD_PERMISSION_DENIED;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# 07.removeWindow的流程
# 7.1 窗口移除的触发方式
窗口移除可以由多方触发:
窗口移除的触发场景:
├── 应用主动移除
│ └── WindowManager.removeView() → ViewRootImpl.die() → WMS.removeWindow()
│
├── AMS触发移除(Activity销毁)
│ └── AMS.destroyActivity() → ActivityRecord.removeWindow()
│
├── WMS主动移除
│ ├── 进程死亡 → WMS.handleAppDiedLocked()
│ └── 会话断开 → Session.onTransact(BINDER_DIED)
│
└── 系统策略移除
└── 屏幕关闭、用户切换等
2
3
4
5
6
7
8
9
10
11
12
13
# 7.2 removeWindow核心逻辑
// WindowManagerService.java
void removeWindow(Session session, IWindow client) {
synchronized (mGlobalLock) {
WindowState win = windowForClientLocked(session, client, false);
if (win != null) {
win.removeIfPossible();
}
}
}
// WindowState.java
void removeIfPossible() {
// 检查是否可以立即移除
if (!removeIfPossible(false)) {
// 如果窗口还在做动画,延迟移除
return;
}
}
boolean removeIfPossible(boolean force) {
// 1.检查是否还有退出动画
if (mAnimatingExit && !force) {
mRemoveOnExit = true; // 动画结束后移除
return false;
}
// 2.检查是否正在进行Surface操作
if (isAnimating(TRANSITION | PARENTS)) {
if (!force) {
mRemoveOnExit = true;
return false;
}
}
// 3.执行实际移除
removeImmediately();
return true;
}
void removeImmediately() {
// 1.标记已移除
mRemoved = true;
// 2.销毁Surface
if (mHasSurface) {
destroySurfaceLocked();
}
// 3.关闭输入通道
if (mInputChannel != null) {
mInputChannel.dispose();
mInputChannel = null;
}
// 4.从WindowToken中移除
mToken.removeChild(this);
// 5.从全局Map中移除
mService.mWindowMap.remove(mClient.asBinder());
// 6.更新焦点
if (mService.mCurrentFocus == this) {
mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true);
}
// 7.通知InputManager窗口已移除
mService.mInputManagerService.removeInputChannel(mInputWindowHandle.token);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
# 08.Surface与窗口的关系
# 8.1 Surface的本质
Surface是窗口绘制内容的载体,本质上是一块共享内存区域(GraphicBuffer):
Surface在系统中的位置:
应用进程 SystemServer SurfaceFlinger
┌──────────┐ ┌────────────┐ ┌──────────────┐
│ Canvas │ │ SurfaceCtrl│ │ Layer │
│ │ │ │ │ │ │ │ │
│ ▼ │ │ ▼ │ │ ▼ │
│ Surface ──┼──共享内存──┼──Surface───┼──Binder──►│ BufferQueue │
│ │ │ │ │ │ │
│ 绘制内容 │ │ 管理属性 │ │ ▼ │
│ (draw) │ │ (位置/大小) │ │ GraphicBuffer │
│ │ │ │ │ (实际显存) │
└──────────┘ └────────────┘ └──────────────┘
2
3
4
5
6
7
8
9
10
11
12
13
# 8.2 Surface的创建时机
Surface在窗口首次需要显示时创建:
// WindowStateAnimator.java
WindowSurfaceController createSurfaceLocked() {
if (mSurfaceController != null) {
return mSurfaceController;
}
// 1.确定Surface的尺寸
int w = mWin.mCompatFrame.width();
int h = mWin.mCompatFrame.height();
// 2.创建SurfaceControl
// SurfaceControl是应用进程控制Surface属性(位置、大小、层级)的句柄
mSurfaceController = new WindowSurfaceController(mWin.mSession.mSurfaceSession,
mWin.mAttrs.getTitle().toString(), w, h, format, flags, this,
windowType, ownerUid);
// 3.设置Surface的初始属性
mSurfaceController.setPositionInTransaction(mWin.mFrame.left, mWin.mFrame.top);
mSurfaceController.setLayer(mWin.mLayer);
mWin.mHasSurface = true;
return mSurfaceController;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 8.3 SurfaceControl事务
WMS通过SurfaceControl.Transaction来批量修改Surface属性:
// WMS中使用Transaction修改窗口属性
void applySurfaceChangesTransaction() {
SurfaceControl.Transaction t = mTransaction;
// 批量设置多个窗口的属性
for (WindowState win : mVisibleWindows) {
// 设置位置
t.setPosition(win.mSurfaceControl, win.mFrame.left, win.mFrame.top);
// 设置大小
t.setBufferSize(win.mSurfaceControl, win.mFrame.width(), win.mFrame.height());
// 设置层级
t.setLayer(win.mSurfaceControl, win.mLayer);
// 设置透明度
t.setAlpha(win.mSurfaceControl, win.mAlpha);
// 设置变换矩阵(旋转、缩放)
t.setMatrix(win.mSurfaceControl,
win.mDtDx, win.mDtDy, win.mDsDx, win.mDsDy);
}
// 一次性提交所有修改(原子操作)
t.apply();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
关键点:Transaction是原子操作,所有窗口属性的修改在同一帧生效,避免了画面撕裂。
# 09.SurfaceFlinger的合成原理
# 9.1 SurfaceFlinger的角色
SurfaceFlinger是Android显示系统的核心守护进程,负责将所有窗口的Surface合成为最终的屏幕画面:
SurfaceFlinger的工作流程:
每一帧的处理过程:
1. 收集所有Layer的Buffer
├── 应用窗口Layer
├── 状态栏Layer
├── 导航栏Layer
└── 输入法Layer
2. 计算可见区域
├── 确定每个Layer的可见部分
├── 被遮挡的部分不需要合成
└── 透明区域需要Alpha混合
3. 选择合成方式
├── GPU合成(OpenGL ES)
│ └── 适用于复杂动画、Alpha混合
└── HWC合成(Hardware Composer)
└── 适用于简单叠加,效率更高
4. 输出到Display
└── 通过HWC HAL将合成结果送到屏幕
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 9.2 BufferQueue三缓冲机制
应用绘制的内容通过BufferQueue传递给SurfaceFlinger:
BufferQueue三缓冲机制:
应用进程 SurfaceFlinger
(Buffer生产者) (Buffer消费者)
│ │
├── dequeueBuffer() ──►┐ │
│ 获取空闲Buffer │ │
│ │ │
├── 绘制内容到Buffer │ BufferQueue │
│ (Canvas/OpenGL) │ (3个Buffer) │
│ │ │
├── queueBuffer() ────►┤ │
│ 提交已绘制Buffer │ │
│ │ │
│ ├──► acquireBuffer()
│ │ 获取已绘制Buffer
│ │
│ ├──► 合成到屏幕
│ │
│ ├──► releaseBuffer()
│ │ 释放已合成Buffer
│ │
三缓冲:Buffer A (正在显示), Buffer B (已绘制等待合成), Buffer C (正在绘制)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 9.3 VSync与帧调度
SurfaceFlinger通过VSync信号来同步显示流水线:
VSync信号的两阶段偏移:
VSync VSync VSync
│ │ │
├──VSYNC-app ├──VSYNC-app ├──
│ │ │ │ │
│ └──应用绘制 │ └──应用绘制 │
│ Frame N+1 │ Frame N+2 │
│ │ │
├──VSYNC-sf ├──VSYNC-sf ├──
│ │ │ │ │
│ └──SF合成 │ └──SF合成 │
│ Frame N │ Frame N+1 │
│ │ │
└──Display └──Display └──
Frame N-1 Frame N Frame N+1
VSYNC-app: 触发应用开始绘制下一帧
VSYNC-sf: 触发SurfaceFlinger合成已完成的帧
偏移设计: 避免应用绘制和SF合成竞争同一个Buffer
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 10.窗口布局与尺寸计算
# 10.1 布局计算时机
WMS在以下情况会触发窗口布局计算:
触发布局计算的事件:
├── 窗口添加/移除
├── 窗口尺寸变化(relayout)
├── 屏幕旋转
├── 系统UI变化(状态栏显示/隐藏)
├── 输入法显示/隐藏
├── 分屏模式切换
└── 配置变化
2
3
4
5
6
7
8
# 10.2 WindowSurfacePlacer的工作
// WindowSurfacePlacer.java - 窗口布局的核心类
class WindowSurfacePlacer {
// 执行布局计算
void performSurfacePlacement() {
if (mInLayout) return; // 防止重入
mInLayout = true;
try {
performSurfacePlacementLoop();
} finally {
mInLayout = false;
}
}
void performSurfacePlacementLoop() {
int loopCount = 6; // 最多循环6次,防止死循环
do {
mTraversalScheduled = false;
// 核心布局过程
performSurfacePlacementNoTrace();
} while (mTraversalScheduled && loopCount > 0);
}
void performSurfacePlacementNoTrace() {
// 1.准备阶段:收集所有需要布局的窗口
mService.mRoot.prepareSurfaces();
// 2.布局阶段:计算每个窗口的位置和大小
mService.mRoot.performLayout(false, false);
// 3.应用阶段:将计算结果应用到Surface
mService.mRoot.applySurfaceChangesTransaction();
// 4.通知阶段:通知应用窗口尺寸变化
mService.mRoot.handleNotObscuredLocked(
defaultDisplay.mWinSizeChanged,
defaultDisplay.mTmpApplySurfaceChangesTransactionState);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# 10.3 Insets系统
Android 11+引入了全新的Insets系统替代原来的fitSystemWindows机制:
// InsetsState.java - 记录所有系统UI区域
class InsetsState {
// 不同类型的Insets源
private final SparseArray<InsetsSource> mSources = new SparseArray<>();
// Insets类型
static final int ITYPE_STATUS_BAR = 0; // 状态栏
static final int ITYPE_NAVIGATION_BAR = 1; // 导航栏
static final int ITYPE_IME = 2; // 输入法
static final int ITYPE_CAPTION_BAR = 3; // 标题栏
// 计算窗口可用区域
Insets calculateInsets(Rect frame, boolean ignoreVisibility) {
Insets result = Insets.NONE;
for (int i = 0; i < mSources.size(); i++) {
InsetsSource source = mSources.valueAt(i);
if (source.isVisible() || ignoreVisibility) {
result = Insets.max(result, source.calculateInsets(frame, false));
}
}
return result;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Insets如何影响窗口布局:
┌───────────────────────────────┐ ← 屏幕顶部
│ 状态栏 Inset │ (ITYPE_STATUS_BAR)
├───────────────────────────────┤
│ │
│ │
│ 应用可用区域 │ ← 扣除Insets后的区域
│ (Content Area) │
│ │
│ │
├───────────────────────────────┤
│ 导航栏 Inset │ (ITYPE_NAVIGATION_BAR)
└───────────────────────────────┘ ← 屏幕底部
当输入法弹出时:
├───────────────────────────────┤
│ 应用可用区域(缩小) │
├───────────────────────────────┤
│ │
│ 输入法 Inset │ (ITYPE_IME)
│ │
├───────────────────────────────┤
│ 导航栏 Inset │
└───────────────────────────────┘
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 11.输入法窗口的特殊管理
# 11.1 输入法窗口的特殊性
输入法窗口在WMS中有特殊的管理逻辑:
输入法窗口的特殊性:
1. 不属于任何应用,但显示在应用上方
2. 需要跟随目标窗口调整位置
3. 需要特殊的动画效果
4. 层级介于应用窗口和系统窗口之间
2
3
4
5
# 11.2 输入法窗口的层级管理
// DisplayContent.java
// 输入法窗口有专门的容器
class DisplayContent extends RootDisplayArea {
// 输入法容器
final ImeContainer mImeWindowsContainer;
// 输入法目标窗口
WindowState mInputMethodTarget;
// 重新定位输入法窗口
void computeImeTarget(boolean updateImeTarget) {
// 查找需要输入法的窗口(获得焦点且接受输入的窗口)
WindowState target = null;
for (WindowState ws : mWindows) {
if (ws.canBeImeTarget()) {
target = ws;
break;
}
}
if (target != null && target != mInputMethodTarget) {
mInputMethodTarget = target;
// 将输入法窗口重新定位到目标窗口上方
assignImeWindowLayer();
}
}
// 调整输入法层级,确保显示在目标窗口上方
void assignImeWindowLayer() {
// 输入法层级 = 目标窗口层级 + 1
// 但始终在系统窗口(状态栏、导航栏)下方
mImeWindowsContainer.assignLayer(mTransaction,
mInputMethodTarget.mSubLayer + 1);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 11.3 输入法弹出的窗口调整
当输入法弹出时,WMS需要调整其他窗口的布局:
// 输入法弹出时的窗口调整流程
void onImeShown(WindowState imeWindow) {
// 1.计算输入法占用的区域
Rect imeFrame = imeWindow.getFrame();
// 2.更新InsetsState
InsetsSource imeSource = new InsetsSource(ITYPE_IME);
imeSource.setFrame(imeFrame);
imeSource.setVisible(true);
// 3.通知所有受影响的窗口
for (WindowState ws : mVisibleWindows) {
if (ws.isAffectedByIme()) {
// 发送InsetsState变化通知
ws.notifyInsetsChanged();
// 应用端会根据新的InsetsState重新布局
}
}
// 4.如果设置了adjustResize
// 应用窗口会缩小,内容区域不与输入法重叠
// 5.如果设置了adjustPan
// 应用窗口不缩小,但整体向上平移
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 12.Dialog与PopupWindow的窗口原理
# 12.1 Dialog的窗口机制
Dialog虽然看起来像一个独立界面,但它本质上是一个依附于Activity的窗口:
// Dialog.java
class Dialog {
private final WindowManager mWindowManager;
Dialog(Context context) {
// Dialog使用Activity的WindowManager
mWindowManager = (WindowManager) context.getSystemService(
Context.WINDOW_SERVICE);
// 创建PhoneWindow
mWindow = new PhoneWindow(mContext);
mWindow.setWindowManager(mWindowManager, null, null);
// 设置窗口属性
mWindow.setGravity(Gravity.CENTER);
}
public void show() {
// 1.执行onCreate
if (!mCreated) {
dispatchOnCreate(null);
}
// 2.执行onStart
onStart();
// 3.获取DecorView
mDecor = mWindow.getDecorView();
// 4.设置窗口属性
WindowManager.LayoutParams l = mWindow.getAttributes();
l.type = WindowManager.LayoutParams.TYPE_APPLICATION; // 应用窗口类型
l.token = mContext.getWindow().getAttributes().token; // 使用Activity的token
// 5.通过WindowManager添加窗口
mWindowManager.addView(mDecor, l);
mShowing = true;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
关键点:Dialog使用Activity的token作为窗口令牌,所以它在WMS中属于Activity的WindowToken分组。
# 12.2 为什么不能用Application Context显示Dialog
使用Application Context显示Dialog的问题:
Application Context
└── 没有Activity的token
└── WindowManager.addView()
└── WMS.addWindow()
└── 查找WindowToken
└── 找不到对应的ActivityRecord
└── 返回BAD_APP_TOKEN错误
└── 抛出BadTokenException
解决方案:
1. 使用Activity Context
2. 使用TYPE_APPLICATION_OVERLAY类型(需要悬浮窗权限)
2
3
4
5
6
7
8
9
10
11
12
13
14
# 12.3 PopupWindow的窗口机制
PopupWindow使用子窗口类型:
// PopupWindow.java
class PopupWindow {
void invokePopup(WindowManager.LayoutParams p) {
// PopupWindow的窗口类型
p.type = WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL; // 子窗口
// 设置父窗口的token
p.token = mAnchor.get().getWindowToken();
// 添加窗口
mWindowManager.addView(mDecorView, p);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
Dialog vs PopupWindow vs Toast窗口对比:
Dialog:
类型:TYPE_APPLICATION(应用窗口)
Token:Activity的token
特点:独立的PhoneWindow,有完整的生命周期
PopupWindow:
类型:TYPE_APPLICATION_SUB_PANEL(子窗口)
Token:锚点View的window token
特点:依附于父窗口,跟随父窗口移动/销毁
Toast:
类型:TYPE_TOAST(系统窗口)
Token:null(由WMS自动创建token)
特点:不需要Activity,但有显示时长限制
悬浮窗:
类型:TYPE_APPLICATION_OVERLAY(系统窗口)
Token:null
特点:需要SYSTEM_ALERT_WINDOW权限,可以显示在所有应用之上
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 13.窗口动画系统
# 13.1 动画类型
WMS管理的窗口动画主要有以下几类:
WMS窗口动画类型:
├── 窗口进入/退出动画
│ └── 窗口添加和移除时的动画
├── Activity转场动画
│ └── Activity启动和结束时的窗口转换动画
├── 壁纸过渡动画
│ └── 壁纸切换时的过渡效果
├── 旋转动画
│ └── 屏幕方向改变时的旋转过渡
├── 窗口缩放动画
│ └── 多窗口模式下窗口大小变化
└── 圆角裁剪动画
└── 圆角屏幕的边缘裁剪动画
2
3
4
5
6
7
8
9
10
11
12
13
# 13.2 动画系统架构
Android 12+使用Shell Transitions来管理窗口动画:
// Transition.java (Shell Transitions)
class Transition {
// 参与转场的窗口
final ArraySet<WindowContainer> mParticipants = new ArraySet<>();
// 转场类型
final int mType;
// 动画开始
void start() {
// 1.收集所有参与者的起始状态
for (WindowContainer wc : mParticipants) {
mStartStates.put(wc, captureState(wc));
}
// 2.执行窗口变化(显示/隐藏/移动等)
applyChanges();
// 3.收集结束状态
for (WindowContainer wc : mParticipants) {
mEndStates.put(wc, captureState(wc));
}
// 4.创建动画
// 使用起始状态和结束状态之间的差异来计算动画
TransitionInfo info = calculateTransitionInfo();
// 5.将动画交给Shell执行
mShellTransitions.startAnimation(info, this::onFinished);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 13.3 窗口动画的执行
// WindowAnimator.java
class WindowAnimator {
// 每帧调用一次
void animate(long frameTimeNs) {
synchronized (mService.mGlobalLock) {
// 遍历所有显示设备
for (int i = mDisplayContentsAnimators.size() - 1; i >= 0; i--) {
DisplayContentsAnimator displayAnimator = mDisplayContentsAnimators.valueAt(i);
// 遍历所有窗口动画
displayAnimator.mScreenRotationAnimation.stepAnimationLocked(currentTime);
// 更新所有正在做动画的窗口
dc.forAllWindows(w -> {
WindowStateAnimator winAnimator = w.mWinAnimator;
if (winAnimator.isAnimationSet()) {
// 计算动画当前帧的变换
winAnimator.stepAnimationLocked(currentTime);
// 应用变换到Surface
SurfaceControl.Transaction t = winAnimator.mTmpTransaction;
t.setAlpha(w.mSurfaceControl, winAnimator.mAlpha);
t.setMatrix(w.mSurfaceControl,
winAnimator.mDtDx, winAnimator.mDtDy,
winAnimator.mDsDx, winAnimator.mDsDy);
t.setPosition(w.mSurfaceControl,
winAnimator.mPositionX, winAnimator.mPositionY);
}
}, true);
}
// 提交所有Surface变更
mTransaction.apply();
// 如果还有未完成的动画,请求下一帧
if (hasPendingAnimations()) {
scheduleAnimation();
}
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
# 14.多屏幕与折叠屏窗口管理
# 14.1 多屏幕架构
WMS天然支持多屏幕,每个屏幕对应一个DisplayContent:
多屏幕的窗口管理结构:
RootWindowContainer
├── DisplayContent (displayId=0, 主屏幕)
│ ├── TaskDisplayArea
│ │ ├── Task (全屏App)
│ │ └── Task (分屏左侧)
│ ├── DisplayArea (StatusBar)
│ └── DisplayArea (NavBar)
│
├── DisplayContent (displayId=1, 外接屏幕)
│ ├── TaskDisplayArea
│ │ └── Task (投屏App)
│ └── DisplayArea (StatusBar)
│
└── DisplayContent (displayId=2, 虚拟屏幕)
└── TaskDisplayArea
└── Task (VirtualDisplay App)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 14.2 分屏模式的实现
分屏模式在WMS中通过多个Root Task实现:
// 分屏模式的Task结构
// TaskDisplayArea中同时存在多个可见的Root Task
class TaskDisplayArea {
void setLaunchAdjacentFlagRootTask(Task adjacentFlagRootTask) {
// 设置相邻的分屏Task
mLaunchAdjacentFlagRootTask = adjacentFlagRootTask;
}
}
// 分屏时的布局计算
void onSplitScreenModeChanged() {
// 将屏幕区域分为两半
Rect primaryBounds = new Rect(0, 0, screenWidth / 2, screenHeight);
Rect secondaryBounds = new Rect(screenWidth / 2, 0, screenWidth, screenHeight);
// 设置两个Root Task的边界
primaryTask.setBounds(primaryBounds);
secondaryTask.setBounds(secondaryBounds);
// 创建分割线(Divider)
mDividerWindow.setPosition(screenWidth / 2 - dividerWidth / 2, 0);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 14.3 折叠屏的窗口适配
折叠屏设备在WMS中有特殊的处理逻辑:
// 折叠屏展开/折叠的处理
void onFoldStateChanged(boolean isFolded) {
if (isFolded) {
// 折叠:屏幕变小
// 1.更新DisplayContent的尺寸
updateDisplaySize(foldedWidth, foldedHeight);
// 2.通知所有窗口配置变化
mRoot.forAllActivities(activity -> {
activity.onConfigurationChanged(newConfig);
});
// 3.可能需要重新布局
mWindowPlacerLocked.performSurfacePlacement();
} else {
// 展开:屏幕变大
updateDisplaySize(unfoldedWidth, unfoldedHeight);
// Activity可能需要重新创建(如果不支持configChanges)
// 或者只是调整布局
}
}
// 铰链区域的特殊处理
void computeHingeRegion(DisplayContent dc) {
// 折叠屏铰链区域可能有遮挡
Rect hingeRegion = dc.getDisplayInfo().getHingeBounds();
// 在布局计算时避开铰链区域
for (WindowState ws : dc.mWindows) {
if (ws.getBounds().intersect(hingeRegion)) {
// 调整窗口避开铰链区域
ws.adjustBoundsForHinge(hingeRegion);
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 15.WindowContainer层级树详解
# 15.1 WindowContainer继承体系
WindowContainer是WMS中窗口层级树的基类,理解它的继承关系是理解整个WMS的关键:
WindowContainer继承体系:
WindowContainer<E extends WindowContainer>
├── RootWindowContainer (根节点,整个系统只有一个)
│
├── DisplayContent (代表一个屏幕)
│ └── extends RootDisplayArea
│
├── DisplayArea (显示区域)
│ ├── DisplayArea.Root
│ ├── TaskDisplayArea
│ ├── DisplayArea.Tokens (非Task窗口的容器)
│ └── ImeContainer
│
├── Task (任务,原ActivityStack和TaskRecord)
│ └── 可以包含子Task
│
├── ActivityRecord (Activity记录)
│ └── extends WindowToken
│
├── WindowToken (窗口令牌分组)
│ └── 包含多个WindowState
│
└── WindowState (窗口状态)
└── 可以包含子窗口WindowState
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 15.2 Z-Order的计算方式
WindowContainer树中,Z-Order由容器的位置决定:
// WindowContainer.java
class WindowContainer<E extends WindowContainer> {
// 子容器列表,位置越后 = Z-Order越高
protected final WindowList<E> mChildren = new WindowList<>();
// 添加子容器到指定位置
void addChild(E child, int index) {
mChildren.add(index, child);
child.mParent = this;
// 重新分配Layer
assignChildLayers(mSyncTransaction);
}
// 分配子容器的Layer
void assignChildLayers(SurfaceControl.Transaction t) {
int layer = 0;
for (int i = 0; i < mChildren.size(); i++) {
E child = mChildren.get(i);
child.assignLayer(t, layer);
layer++;
// 递归分配子容器的Layer
child.assignChildLayers(t);
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# 15.3 完整的窗口层级树示例
实际运行时的WindowContainer树:
RootWindowContainer
└── DisplayContent (id=0, "Built-in Screen")
└── DisplayArea.Root ("Root")
├── DisplayArea ("Leaf:0:0") [Layer 0]
│ └── WindowToken (TYPE_WALLPAPER)
│ └── WindowState ("com.android.wallpaper") [壁纸]
│
├── TaskDisplayArea ("DefaultTaskDisplayArea") [Layer 1~2]
│ ├── Task #1 (Root Task, fullscreen)
│ │ └── Task #5 (Leaf Task)
│ │ ├── ActivityRecord ("com.app.B/.ActivityB")
│ │ │ └── WindowState ("com.app.B/ActivityB")
│ │ └── ActivityRecord ("com.app.A/.ActivityA")
│ │ ├── WindowState ("com.app.A/ActivityA") [主窗口]
│ │ └── WindowState ("PopupWindow:xxx") [子窗口]
│ └── Task #2 (Root Task, home)
│ └── Task #6 (Leaf Task)
│ └── ActivityRecord ("com.launcher/.Main")
│ └── WindowState ("com.launcher/Main")
│
├── DisplayArea ("Leaf:12:12") [Layer 12]
│ └── ImeContainer
│ └── WindowToken (TYPE_INPUT_METHOD)
│ └── WindowState ("InputMethod") [输入法]
│
├── DisplayArea ("Leaf:15:15") [Layer 15]
│ └── WindowToken (TYPE_APPLICATION_OVERLAY)
│ └── WindowState ("悬浮窗") [应用悬浮窗]
│
├── DisplayArea ("Leaf:17:17") [Layer 17]
│ └── WindowToken (TYPE_VOLUME_OVERLAY)
│ └── WindowState ("VolumeDialog") [音量条]
│
├── DisplayArea ("Leaf:20:20") [Layer 20]
│ └── WindowToken (TYPE_NAVIGATION_BAR)
│ └── WindowState ("NavigationBar") [导航栏]
│
└── DisplayArea ("Leaf:25:25") [Layer 25]
└── WindowToken (TYPE_STATUS_BAR)
└── WindowState ("StatusBar") [状态栏]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# 16.WMS的性能优化与调试
# 16.1 WMS相关的性能问题
WMS层面常见的性能问题:
├── 窗口泄漏:Activity销毁但Dialog未dismiss
│ └── 现象:WindowLeaked异常
│ └── 原因:Dialog的WindowState没有被移除
│
├── 窗口布局频繁:requestLayout过于频繁
│ └── 现象:频繁触发WMS布局计算
│ └── 原因:View频繁变化导致多次relayout
│
├── Surface创建延迟:首帧显示慢
│ └── 现象:Activity启动白屏时间长
│ └── 原因:Surface创建和首次绘制耗时
│
├── 动画掉帧:转场动画不流畅
│ └── 现象:Activity切换卡顿
│ └── 原因:SurfaceFlinger合成超时或GPU负载过高
│
└── 输入延迟:触摸响应慢
└── 现象:点击响应不及时
└── 原因:WMS焦点切换延迟或Input分发队列堆积
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 16.2 WMS调试命令
# 查看所有窗口信息
adb shell dumpsys window windows
# 查看窗口层级树
adb shell dumpsys window containers
# 查看焦点窗口
adb shell dumpsys window displays | grep -E "mCurrentFocus|mFocusedApp"
# 查看输入法状态
adb shell dumpsys window input
# 查看窗口策略
adb shell dumpsys window policy
# 查看Surface信息
adb shell dumpsys SurfaceFlinger
# 查看SurfaceFlinger的Layer树
adb shell dumpsys SurfaceFlinger --list
# 查看帧率统计
adb shell dumpsys SurfaceFlinger --latency
# 开启窗口动画调试
adb shell settings put global window_animation_scale 5.0 # 放慢5倍
adb shell settings put global transition_animation_scale 5.0
adb shell settings put global animator_duration_scale 5.0
# 恢复正常
adb shell settings put global window_animation_scale 1.0
adb shell settings put global transition_animation_scale 1.0
adb shell settings put global animator_duration_scale 1.0
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 16.3 使用Systrace分析WMS
Systrace中WMS相关的关键Tag:
wm (Window Manager):
├── addWindow:窗口添加耗时
├── removeWindow:窗口移除耗时
├── relayoutWindow:窗口重布局耗时
├── performLayout:布局计算耗时
└── performSurfacePlacement:Surface更新耗时
SurfaceFlinger:
├── onMessageReceived:消息处理耗时
├── handleMessageInvalidate:处理Buffer更新
├── handleMessageRefresh:合成刷新
│ ├── preComposition:预合成
│ ├── rebuildLayerStacks:重建Layer栈
│ ├── setUpHWComposer:配置硬件合成
│ ├── doComposition:执行合成
│ └── postComposition:后合成处理
└── onVsync:VSync信号处理
分析方法:
1. 在Systrace中找到"wm"和"SurfaceFlinger"的Track
2. 查看每帧的处理耗时是否超过16.6ms
3. 找到耗时异常的帧,分析具体哪个操作慢
4. 常见问题:performLayout耗时(窗口过多或布局复杂)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 17.总结与技术思考
# 17.1 核心要点
WMS核心要点总结:
1. 窗口类型三分:应用窗口、子窗口、系统窗口
└── 类型决定层级,层级决定显示顺序
2. WindowContainer统一树:Android 10+的核心架构
└── 从RootWindowContainer到WindowState的完整层级
3. Surface是窗口的绘制载体
└── 通过BufferQueue与SurfaceFlinger交换数据
└── SurfaceControl.Transaction实现原子属性修改
4. VSync驱动的显示流水线
└── 应用绘制→WMS管理→SurfaceFlinger合成→Display显示
5. Insets系统管理系统UI区域
└── 替代了旧的fitSystemWindows机制
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 17.2 WMS与其他服务的关系
WMS的服务依赖关系:
┌──────────────┐
│ AMS │ 组件生命周期
│ (ATMS) │ Activity状态
└──────┬───────┘
│ 共享WindowContainer树
┌──────▼───────┐
│ WMS │ 窗口管理核心
└──┬──────┬────┘
│ │
┌───────▼──┐ ┌─▼──────────┐
│ IMS │ │SurfaceFlngr│
│(输入管理)│ │ (合成显示) │
└──────────┘ └────────────┘
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 17.3 学习建议
学习WMS建议按以下路径:
- 先理解窗口类型和层级——知道屏幕上有哪些窗口、为什么按这个顺序排列
- 再理解WindowContainer树——知道WMS内部如何组织窗口
- 然后理解Surface机制——知道窗口内容如何绘制和显示
- 最后理解动画和布局——知道窗口如何变换和适配
调试技巧:善用adb shell dumpsys window系列命令,可以实时查看WMS的内部状态,是理解WMS的最佳实践途径。
# 18.Window与DecorView创建过程
# 18.1 Activity组件UI实现
Activity组件的UI实现需要与WindowManagerService和SurfaceFlinger服务进行交互:
- Activity通过Session Binder对象请求WMS创建WindowState对象,描述窗口状态
- 应用程序通过Client Binder对象请求SurfaceFlinger创建Layer对象,描述窗口数据
Activity UI实现的完整架构:
Activity
├── PhoneWindow (窗口抽象)
│ ├── DecorView (顶层View)
│ │ ├── TitleBar/ActionBar
│ │ └── ContentView (android.R.id.content)
│ │ └── 开发者的布局
│ └── WindowManager.LayoutParams (窗口属性)
│
├── WindowManagerImpl → WindowManagerGlobal
│ └── ViewRootImpl (每个Window一个)
│ ├── mSurface → Surface (绘图表面)
│ ├── mView → DecorView
│ ├── mWindowSession → IWindowSession (WMS代理)
│ └── InputChannel (事件通道)
│
└── 与系统服务的交互:
App进程 system_server SurfaceFlinger
ViewRootImpl WMS
│ setView() │ │
│ → addToDisplay() ──→ │ addWindow() │
│ │ → 创建WindowState │
│ │ → 分配Layer层级 │
│ │ → createSurfaceControl()│
│ │ ──────────────→ │ 创建Layer
│ ← 返回Surface ──── │ │
│ 在Surface上绘制 │ │
│ → lockCanvas() │ │
│ → draw() │ │
│ → unlockAndPost() │ │ 合成显示
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# 18.2 PhoneWindow创建过程
PhoneWindow是在Activity.attach()方法中创建的:
final void attach(Context context, ActivityThread aThread, ...) {
mWindow = new PhoneWindow(this, window, activityConfigCallback);
}
2
3
PhoneWindow类有两个重要成员变量:
mDecor(DecorView类型):描述窗口视图mContentParent(ViewGroup类型):描述视图内容的父容器
在attach方法中还会完成以下设置:
- setCallback(this):将Activity实现的Callback接口设置到PhoneWindow,使IO输入事件(键盘、触摸屏)可以转发给Activity处理
- setSoftInputMode():设置软键盘输入区域的显示模式
- setWindowManager():设置应用程序窗口的本地窗口管理器
# 18.3 DecorView创建过程
DecorView在PhoneWindow.setContentView()中被创建:
DecorView创建流程:
Activity.setContentView()
→ PhoneWindow.setContentView()
→ installDecor() // mContentParent为null时创建
→ generateDecor() // 创建DecorView
→ generateLayout() // 根据主题加载布局到DecorView
→ mLayoutInflater.inflate(layoutResID, mContentParent)
// 将自定义布局添加到content容器中
2
3
4
5
6
7
8
9
Activity中布局层次:mDecor → mContentRoot → mContentParent → 自定义layoutView
# 18.4 ViewRootImpl创建过程
ViewRootImpl是在Activity被激活(handleResumeActivity)时创建的:
ViewRootImpl创建流程:
ActivityThread.handleResumeActivity()
→ performResumeActivity() // 触发Activity.onResume()
→ r.activity.makeVisible()
→ wm.addView(mDecor, ...)
→ WindowManagerImpl.addView()
→ WindowManagerGlobal.addView()
├── new ViewRootImpl() // 创建ViewRootImpl
├── mViews.add(view) // 保存DecorView
├── mRoots.add(root) // 保存ViewRootImpl
└── root.setView(view, ...)
└── requestLayout() // 第一次布局请求
2
3
4
5
6
7
8
9
10
11
12
13
ViewRootImpl的核心职责:
- 负责为应用程序窗口视图创建Surface
- 配合WMS管理系统的应用程序窗口
- 负责管理、布局和渲染应用程序窗口视图的UI
- 接收Activity组件的IO输入事件(键盘事件等)
# 18.5 Dialog中Window创建
Dialog的Window创建过程与Activity类似:
- 创建Window:通过PolicyManager的makeNewWindow方法完成
- 初始化DecorView:通过Window.setContentView()实现
- 显示:在Dialog.show()方法中,通过WindowManager将DecorView添加到Window中
- 关闭:通过WindowManager.removeViewImmediate(mDecor)移除DecorView
重要限制:Dialog必须使用Activity的Context,因为需要应用token。Application和Service的Context没有token,会导致窗口添加失败。如果确需使用,可将Dialog的Window类型设置为系统Window(TYPE_SYSTEM_ERROR等)。
# 18.6 四者关系总结
Activity、PhoneWindow、DecorView、ViewRootImpl 关系:
Activity
└── PhoneWindow (mWindow)
└── DecorView (mDecor)
├── LinearLayout
│ ├── ViewStub (ActionBar)
│ ├── FrameLayout (titleBar)
│ └── FrameLayout (id=content) ← setContentView的内容
└── 关联 ViewRootImpl
├── 管理View的绘制(measure/layout/draw)
├── 管理IO输入事件
└── 通过Surface与SurfaceFlinger通信
创建时机:
PhoneWindow → Activity.attach()
DecorView → Activity.onCreate() → setContentView()
ViewRootImpl → Activity.onResume() → makeVisible() → addView()
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18