WindowManagerService (WMS) 通过一个基于树形结构的“窗口容器”体系来管理所有窗口的层级。其核心计算思路是:为每个窗口计算一个最终的 Z-order 值,值越大,在屏幕上显示的位置就越靠上。
核心架构:基于 WindowContainer 的树形结构
Android 12 中,所有窗口元素都继承自 WindowContainer,形成了一棵严格的树。Z-order 由子节点在父容器 mChildren 列表中的位置决定,越靠近队尾,层级越高。核心层级如下:
- RootWindowContainer:WMS 中的顶层容器,管理所有显示屏。
- DisplayContent:代表一个物理或逻辑显示屏。
- DisplayArea:用于管理特定功能区域(如刘海屏、放大镜)。
- Task:代表一个任务栈。
- ActivityRecord / WindowToken:代表一个 Activity 或一组窗口。
- WindowState:代表一个具体的窗口。
关键参数:mBaseLayer 与 mSubLayer
1. 基础层级 (mBaseLayer)
在 WindowState 构造时由 WindowManagerPolicy.getWindowLayerLw()根据窗口类型获取 mBaseLayer。
主要分为三大类:
- 应用窗口 (1-99) :如
TYPE_APPLICATION。 - 子窗口 (1000-1999) :依附于父窗口,如 PopupWindow。
- 系统窗口 (2000-2999) :如状态栏、Toast。
2. 子层级 (mSubLayer)
通过 WindowManagerPolicy.getSubWindowLayerFromTypeLw() 获取 mSubLayer。
仅用于子窗口,决定其在父窗口内部的相对顺序。值为正显示在父窗口之上,为负则在之下。
//frameworks/base/services/core/java/com/android/server/wm/WindowState.java
//WindowState构造函数
WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token,
WindowState parentWindow, int appOp, WindowManager.LayoutParams a, int viewVisibility,
int ownerId, int showUserId, boolean ownerCanAddInternalSystemWindow,
PowerManagerWrapper powerManagerWrapper) {
super(service);
...
//初始化 `InputWindowHandle`,用于接收触摸和按键事件
mInputWindowHandle = new InputWindowHandleWrapper(new InputWindowHandle(
mActivityRecord != null
? mActivityRecord.getInputApplicationHandle(false /* update */) : null,
getDisplayId()));
mInputWindowHandle.setOwnerPid(s.mPid);
...
//子窗口(如 PopupWindow、对话框等)
if (mAttrs.type >= FIRST_SUB_WINDOW && mAttrs.type <= LAST_SUB_WINDOW) {
//基于**父窗口**的层级计算而来。使用 `TYPE_LAYER_MULTIPLIER` (通常为 10000)
//放大,加上 `TYPE_LAYER_OFFSET` (通常为 1000),为子窗口在父窗口内部预留了
//足够的层级空间
mBaseLayer = mPolicy.getWindowLayerLw(parentWindow)
* TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
//用于决定子窗口在**父窗口内部**的相对顺序。正值在父窗口之上,负值在之下。
mSubLayer = mPolicy.getSubWindowLayerFromTypeLw(a.type);
mIsChildWindow = true;
...
} else {
//对于非子窗口(如 Activity 主窗口、系统窗口)
//获取基础层级,再乘以 `TYPE_LAYER_MULTIPLIER` 并加上偏移量。
mBaseLayer = mPolicy.getWindowLayerLw(this)
* TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
//`mSubLayer` 固定为 0
mSubLayer = 0;
mIsChildWindow = false;
mLayoutAttached = false;
mIsImWindow = mAttrs.type == TYPE_INPUT_METHOD
|| mAttrs.type == TYPE_INPUT_METHOD_DIALOG;
mIsWallpaper = mAttrs.type == TYPE_WALLPAPER;
}
...
//子窗口
if (mIsChildWindow) {
//将子窗口添加到父窗口的子列表中,并利用 `sWindowSubLayerComparator`
//这个比较器来维护子窗口在父窗口内的正确 Z-order 顺序
//WindowContainer.addChild(this, sWindowSubLayerComparator);
parentWindow.addChild(this, sWindowSubLayerComparator);
}
}
//frameworks/base/services/core/java/com/android/server/policy/WindowManagerPolicy.java
//根据窗口类型决定基础层级mBaseLayer
default int getWindowLayerFromTypeLw(int type, boolean canAddInternalSystemWindow,
boolean roundedCornerOverlay) {
/如果窗口既是圆角覆盖层,又有内部系统权限,直接返回可能的最大层级
//这确保了屏幕圆角等系统级 UI 始终显示在最顶层,不被任何应用遮挡。
if (roundedCornerOverlay && canAddInternalSystemWindow) {
return getMaxWindowLayer();
}
//应用窗口
if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
return APPLICATION_LAYER;
}
switch (type) {
//壁纸窗口,位于所有应用窗口之下
case TYPE_WALLPAPER:
return 1;
//演示屏窗口
case TYPE_PRESENTATION:
case TYPE_PRIVATE_PRESENTATION:
//分屏分隔线窗口
case TYPE_DOCK_DIVIDER:
//快速设置对话框窗口
case TYPE_QS_DIALOG:
case TYPE_PHONE:
return 3;
//系统级搜索窗口
case TYPE_SEARCH_BAR:
//语音交互启动窗口,过渡占位窗口
case TYPE_VOICE_INTERACTION_STARTING:
return 4;
//语音交互会话主窗口
case TYPE_VOICE_INTERACTION:
return 5;
//与系统特殊手势处理相关的系统窗口类型
case TYPE_INPUT_CONSUMER:
return 6;
//系统级对话框窗口
case TYPE_SYSTEM_DIALOG:
return 7;
case TYPE_TOAST:
return 8;
case TYPE_PRIORITY_PHONE:
return 9;
//悬浮窗
case TYPE_SYSTEM_ALERT:
return canAddInternalSystemWindow ? 13 : 10;
//高于普通应用窗口,低于系统级悬浮窗。
case TYPE_APPLICATION_OVERLAY:
return 12;
//输入法窗口
case TYPE_INPUT_METHOD:
return 15;
case TYPE_INPUT_METHOD_DIALOG:
return 16;
//状态栏
case TYPE_STATUS_BAR:
return 17;
case TYPE_STATUS_BAR_ADDITIONAL:
return 18;
case TYPE_NOTIFICATION_SHADE:
return 19;
//状态栏的子面板
case TYPE_STATUS_BAR_SUB_PANEL:
return 20;
case TYPE_KEYGUARD_DIALOG:
return 21;
case TYPE_VOLUME_OVERLAY:
return 22;
case TYPE_SYSTEM_OVERLAY:
return canAddInternalSystemWindow ? 23 : 11;
case TYPE_NAVIGATION_BAR:
return 24;
case TYPE_NAVIGATION_BAR_PANEL:
return 25;
case TYPE_SCREENSHOT:
return 26;
case TYPE_SYSTEM_ERROR:
return canAddInternalSystemWindow ? 27 : 10;
case TYPE_MAGNIFICATION_OVERLAY:
return 28;
case TYPE_DISPLAY_OVERLAY:
return 29;
case TYPE_DRAG:
return 30;
case TYPE_ACCESSIBILITY_OVERLAY:
return 31;
case TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY:
return 32;
case TYPE_SECURE_SYSTEM_OVERLAY:
return 33;
case TYPE_BOOT_PROGRESS:
return 34;
case TYPE_POINTER:
// the (mouse) pointer layer
return 35;
default:
return 3;
}
}
//根据窗口类型决定子层级
default int getSubWindowLayerFromTypeLw(int type) {
switch (type) {
case TYPE_APPLICATION_PANEL:
case TYPE_APPLICATION_ATTACHED_DIALOG:
return APPLICATION_PANEL_SUBLAYER;
case TYPE_APPLICATION_MEDIA:
return APPLICATION_MEDIA_SUBLAYER;
case TYPE_APPLICATION_MEDIA_OVERLAY:
return APPLICATION_MEDIA_OVERLAY_SUBLAYER;
case TYPE_APPLICATION_SUB_PANEL:
return APPLICATION_SUB_PANEL_SUBLAYER;
case TYPE_APPLICATION_ABOVE_SUB_PANEL:
return APPLICATION_ABOVE_SUB_PANEL_SUBLAYER;
}
return 0;
}
计算流程:插入与分配
添加窗口时,层级计算分为两步:
第一步:插入 (addChild)
当添加新窗口时,WMS 会调用父容器的 addChild 方法,根据窗口类型、应用的 Z-order 等规则,将其插入到 mChildren 列表中的正确位置。
//frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,
int displayId, int requestUserId, InsetsState requestedVisibility,
InputChannel outInputChannel, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls) {
...
//创建WindowState,这个对象维护了窗口的所有状态信息
/WindowState构造函数:
//计算窗口的主序 (`mBaseLayer`) 和子序 (`mSubLayer`) 的初始值
final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], attrs, viewVisibility, session.mUid, userId,
session.mCanAddInternalSystemWindow);
...
res = ADD_OKAY;
...
//将窗口与 DisplayContent 关联,并设置 SurfaceControl
win.attach();
//将窗口注册到 WMS 的全局映射表中
mWindowMap.put(client.asBinder(), win);
...
boolean imMayMove = true;
//将窗口加入所属 `WindowToken` 的窗口列表
//将窗口作为一个节点,添加到 `WindowContainer` 层级树中。
//`WindowContainer` 树中的父子关系和兄弟顺序,
//由之前计算的 `mBaseLayer` 和 `mSubLayer` 决定
win.mToken.addWindow(win);
//根据窗口类型,决定它应该为应用窗口提供什么样的"留白区域"(Insets)
displayPolicy.addWindowLw(win, attrs);
//输入法窗口处理
if (type == TYPE_INPUT_METHOD) {
//注册 Inset → 触发布局
displayContent.setInputMethodWindowLocked(win);
imMayMove = false;
} else if (type == TYPE_INPUT_METHOD_DIALOG) {
//调整布局
displayContent.computeImeTarget(true /* updateImeTarget */);
imMayMove = false;
} else {
...
}
...
if (imMayMove) {
//调整布局
displayContent.computeImeTarget(true /* updateImeTarget */);
}
//计算窗口的 Z-order 层级
//实际执行的方法,是 `win` 父容器对象的实际类型(如 `DisplayContent`)所重写的版本。
// - 如果 `win` 是子窗口,其父容器通常是 `WindowState`,就会执行 `WindowState` 的 `assignChildLayers`,负责处理子窗口的相对顺序。
//- 如果 `win` 是 Activity 主窗口,其父容器可能是 `Task` 或 `ActivityRecord`,则会 执行它们的实现,负责任务或 Activity 内部的层级管理。
//- 如果 `win` 是系统窗口(如状态栏),其父容器可能是 `DisplayContent` 或其子区域(如 `AboveAppWindowContainers`),最终会由 `DisplayContent` 的复杂逻辑来统筹
//WindowContainer.assignChildLayers()
win.getParent().assignChildLayers();
...
}
...
return res;
}
//将窗口加入所属 `WindowToken` 的窗口列表
void addWindow(final WindowState win) {
//如果窗口是子窗口,直接返回
//`DisplayContent` 负责管理顶级窗口的层级。子窗口的层级由其父窗口管理,
//因此不需要也不应该被直接添加到 `DisplayContent` 的 `mChildren` 列表中。
//这体现了 `WindowContainer` 树形结构的分层管理思想。
if (win.isChildWindow()) {
return;
}
//检查 `DisplayContent` 自身的 `SurfaceControl` 是否已创建。
//如果为空,则立即创建。
//`DisplayContent` 作为显示屏容器,也需要拥有自己的 `Surface`。这个
//`Surface` 是所有子窗口的“根画布”,必须先于子窗口存在。`createSurfaceControl`
//会向 `SurfaceFlinger` 申请一块显示内存。
if (mSurfaceControl == null) {
createSurfaceControl(true /* force */);
}
//检查 `mChildren` 列表是否已包含该窗口
if (!mChildren.contains(win)) {
//并使用 `mWindowComparator` 比较器将其插入到正确的位置
addChild(win, mWindowComparator);
//将 WMS 的 `mWindowsChanged` 标志置为 `true`,这会触
//发 `assignWindowLayers()` 等后续操作,重新计算所有窗口的 Z-order
mWmService.mWindowsChanged = true;
}
}
//frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java
//基于比较器的插入排序算法,确保 `mChildren` 列表始终保持正确的 Z-order 顺序。
protected void addChild(E child, Comparator<E> comparator) {
//检查子节点是否已有父容器。如果 `child` 已经有父容器且当前不是“重新设置父容器”状态
//(`!mReparenting`),则抛出异常,防止一个节点被错误地添加到多个父容器下。
//这维护了树形结构的单一父节点约束。
if (!child.mReparenting && child.getParent() != null) {
throw new IllegalArgumentException("addChild: container=" + child.getName()
+ " is already a child of container=" + child.getParent().getName()
+ " can't add to container=" + getName());
}
int positionToAdd = -1;
//如果提供了比较器 (`comparator`)
if (comparator != null) {
final int count = mChildren.size();
//遍历现有的 `mChildren` 列表
for (int i = 0; i < count; i++) {
//**新子节点的优先级应该排在当前节点之前**(即层级更低,应显示在下方)。
if (comparator.compare(child, mChildren.get(i)) < 0) {
positionToAdd = i;
break;
}
}
}
if (positionToAdd == -1) {
//如果没找到合适的插入点(意味着新节点优先级高于或等于所有现有节点),
//则直接添加到列表**末尾**。在 Z-order 体系中,列表末尾代表最高层级,会显示在最
//上面。否则,插入到找到的 `positionToAdd` 位置。
mChildren.add(child);
} else {
//找到第一个满足此条件的位置,记录在 `positionToAdd` 中。
//后续节点将依次后移,为新节点腾出空间。
mChildren.add(positionToAdd, child);
}
//在成功将子节点加入列表后,设置其父节点引用。
//这个顺序很重要,可以避免子类在 `setParent` 过程中因尚未加入列表而产生不一致的状态
child.setParent(this);
}
//WindowContainer计算窗口的 Z-order 层级
//frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java
void assignChildLayers() {
assignChildLayers(getSyncTransaction());
scheduleAnimation();
}
void assignChildLayers(Transaction t) {
int layer = 0;
for (int j = 0; j < mChildren.size(); ++j) {
final WindowContainer wc = mChildren.get(j);
//递归调用:先处理子节点的子节点
wc.assignChildLayers(t);
//只有 `needsZBoost()` 返回 `false` 的节点才会在第一轮被分配层级。
//`layer` 从 0 开始递增,因此第一轮分配到的层级值相对较低。
if (!wc.needsZBoost()) {
wc.assignLayer(t, layer++);
}
}
for (int j = 0; j < mChildren.size(); ++j) {
final WindowContainer wc = mChildren.get(j);
//第二轮:为需要 Z 提升的节点分配层级
//只有 `needsZBoost()` 返回 `true` 的节点才会在这一轮被分配层级。
//`layer` 继续递增,因此这些节点会获得**比第一轮所有节点都高的层级值**。
if (wc.needsZBoost()) {
wc.assignLayer(t, layer++);
}
}
}
void assignLayer(Transaction t, int layer) {
//如果 `TransitionController` 正在播放动画,则直接返回,不执行任何层级分配。
//动画期间层级正在被动画系统动态控制(例如窗口正在缩放或移动),此时强行修改层级
//可能会破坏动画效果或导致画面闪烁。
if (mWmService.mAtmService.getTransitionController().isPlaying()) return;
//**检查是否需要更新**
//新的绝对层级值与上次记录的层级值不同,上次设置的是**相对层级**(`assignRelativeLayer`),这意味着当前需要切换到绝对层级。
final boolean changed = layer != mLastLayer || mLastRelativeToLayer != null;
//只有当 `mSurfaceControl` 存在(窗口已有 Surface)且层级确实发生了变化时,
//才执行更新。
if (mSurfaceControl != null && changed) {
//将操作委托给了 `SurfaceAnimator.setLayer`,
//这个 `Transaction` 对象是一个包含了一系列 `Surface` 操作(如设置层级、位置、
//大小等)的批处理容器。当所有操作都通过 `Transaction` 准备好后,WMS 会调
//用 `Transaction.apply()` 方法,将这一批指令**一次性提交
//给 `SurfaceFlinger`**
setLayer(t, layer);
//记录当前分配的层级值,用于下次变更检测。
mLastLayer = layer;
//清除相对层级状态,表示当前使用的是绝对层级
mLastRelativeToLayer = null;
}
}
//`WindowState` 为其直接子窗口(如 PopupWindow、Dialog)分配 Z-order 层级。
//frameworks/base/services/core/java/com/android/server/wm/WindowState.java
// **`TYPE_APPLICATION_MEDIA`**(层级 -2)是**底层**,通常用于视频画面的渲染,
//作为内容的背景。
// **`TYPE_APPLICATION_MEDIA_OVERLAY`**(层级 -1)位于其上方,
//通常用于显示视频控制条(如播放/暂停按钮),覆盖在视频画面之上,但仍低于 Activity 主窗口。
public void assignChildLayers(Transaction t) {
//初始层级:`layer` 从 `PRESERVED_SURFACE_LAYER + 1` 开始
//(`PRESERVED_SURFACE_LAYER` 通常为 0)。注释说明,如果主窗口的 Surface 被保
//留了(如窗口关闭但 Surface 仍存在),子窗口必须显示在保留 Surface 之上。
int layer = PRESERVED_SURFACE_LAYER + 1;
for (int i = 0; i < mChildren.size(); i++) {
//遍历 `mChildren` 列表。**注意**:这里 `mChildren` 中的元素
//是 `WindowState` 类型,代表该窗口的**直接子窗口**。
final WindowState w = mChildren.get(i);
if (w.mAttrs.type == TYPE_APPLICATION_MEDIA) {
if (mWinAnimator.hasSurface()) {
//“将子窗口 `w` 的层级设置为相对于父窗口的 Surface 偏移 **-2**”。
//也就是说,无论父窗口的最终层级是多少,子窗口都会恰好显示在它下方 2 层。
w.assignRelativeLayer(t, mWinAnimator.mSurfaceController.mSurfaceControl, -2);
} else {
//如果父窗口还没有 Surface(例如在窗口初始化早期),则无法建立相对层级关
//系。此时调用 **`assignLayer(t, -2)`** ,直接为子窗口分配一个绝对层级
//值 **-2**。由于此时父窗口的 Surface 还未创建,一个相对较低的绝对层级值
//也能达到类似的“显示在父窗口之下”的效果。
w.assignLayer(t, -2);
}
} else if (w.mAttrs.type == TYPE_APPLICATION_MEDIA_OVERLAY) {
if (mWinAnimator.hasSurface()) {
//将子窗口 `w` 的层级设置为相对于父窗口的 Surface **偏移 -1**。
//子窗口会显示在父窗口下方 **1 层**。
w.assignRelativeLayer(t, mWinAnimator.mSurfaceController.mSurfaceControl, -1);
} else {
//直接为子窗口分配一个绝对层级值 **-1**。
w.assignLayer(t, -1);
}
} else {
w.assignLayer(t, layer);
}
//通过递归调用 `assignChildLayers(t)`,层级分配逻辑可以从树的根节点
//一直传递到叶子节点。
w.assignChildLayers(t);
//分配一个递增的 `layer` 值,从 1 开始逐渐增加
layer++;
}
}
第二步:分配 (assignWindowLayers)
窗口插入到 mWindows 列表后,WMS会调用 DisplayContent.assignWindowLayers()。它会从底部到顶部遍历列表,根据窗口的 mBaseLayer(基础层级)和 mSubLayer(子层级),为每个窗口计算最终的 mLastLayer 值。
//frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java
void assignWindowLayers(boolean setLayoutNeeded) {
assignChildLayers(getSyncTransaction());
if (setLayoutNeeded) {
setLayoutNeeded();
}
//触发一次动画帧调度
scheduleAnimation();
}
//`DisplayContent` 分配 Z-order 层级。
1. **父类(`WindowContainer`)** :定义了算法骨架 `assignChildLayers(Transaction t)`,包含了通用的层级分配逻辑(如遍历子节点、分配层级)。
2. **子类(`DisplayContent`)** :重写了该算法,在调用父类逻辑**之前**,先插入了自己特有的业务逻辑(IME 处理)。
void assignChildLayers(SurfaceControl.Transaction t) {
//1. 先处理 IME 的特殊层级关系
assignRelativeLayerForIme(t, false /* forceUpdate */);
//2. 调用父类 `WindowContainer` 的通用层级分配方法。父类方法会递归遍历所有子节点,
//按照比较器定义的顺序为每个子节点分配层级。这是一个通用的、与具体业务无关的层级分配逻辑。
super.assignChildLayers(t);
}
//WindowContainer计算窗口的 Z-order 层级
//frameworks/base/services/core/java/com/android/server/wm/WindowContainer.java
void assignChildLayers(Transaction t) {
int layer = 0;
for (int j = 0; j < mChildren.size(); ++j) {
final WindowContainer wc = mChildren.get(j);
//递归调用:先处理子节点的子节点
wc.assignChildLayers(t);
//只有 `needsZBoost()` 返回 `false` 的节点才会在第一轮被分配层级。
//`layer` 从 0 开始递增,因此第一轮分配到的层级值相对较低。
if (!wc.needsZBoost()) {
wc.assignLayer(t, layer++);
}
}
for (int j = 0; j < mChildren.size(); ++j) {
final WindowContainer wc = mChildren.get(j);
//第二轮:为需要 Z 提升的节点分配层级
//只有 `needsZBoost()` 返回 `true` 的节点才会在这一轮被分配层级。
//`layer` 继续递增,因此这些节点会获得**比第一轮所有节点都高的层级值**。
if (wc.needsZBoost()) {
wc.assignLayer(t, layer++);
}
}
}
void assignLayer(Transaction t, int layer) {
//如果 `TransitionController` 正在播放动画,则直接返回,不执行任何层级分配。
//动画期间层级正在被动画系统动态控制(例如窗口正在缩放或移动),此时强行修改层级
//可能会破坏动画效果或导致画面闪烁。
if (mWmService.mAtmService.getTransitionController().isPlaying()) return;
//**检查是否需要更新**
//新的绝对层级值与上次记录的层级值不同,上次设置的是**相对层级**(`assignRelativeLayer`),这意味着当前需要切换到绝对层级。
final boolean changed = layer != mLastLayer || mLastRelativeToLayer != null;
//只有当 `mSurfaceControl` 存在(窗口已有 Surface)且层级确实发生了变化时,
//才执行更新。
if (mSurfaceControl != null && changed) {
//将操作委托给了 `SurfaceAnimator.setLayer`,
//这个 `Transaction` 对象是一个包含了一系列 `Surface` 操作(如设置层级、位置、
//大小等)的批处理容器。当所有操作都通过 `Transaction` 准备好后,WMS 会调
//用 `Transaction.apply()` 方法,将这一批指令**一次性提交
//给 `SurfaceFlinger`**
setLayer(t, layer);
//记录当前分配的层级值,用于下次变更检测。
mLastLayer = layer;
//清除相对层级状态,表示当前使用的是绝对层级
mLastRelativeToLayer = null;
}
}
总的来说,通过引入 WindowContainer 树形结构,将窗口层级管理从“平铺排序”演进为“树形管理”,逻辑更清晰,也更好地支持了多窗口和复杂功能。