(承接上文:Android 窗口管理 - 窗口添加过程分析 Client 端)
上一篇我们分析到,App(Client 端)通过 addToDisplayAsUser 方法,经由跨进程调用进入 WMS(Server 端)。以下是 IWindowSession.addToDisplayAsUser 接口各参数的作用解析:
-
IWindow window:App 端实现的窗口回调接口,WMS 会通过该接口向 App 通知窗口状态变化(如尺寸调整、焦点变更等)。
-
WindowManager.LayoutParams attrs:窗口属性配置类,包含窗口类型(如应用窗口、子窗口)、尺寸、位置、标志位(如全屏、悬浮)等核心参数,直接决定窗口的显示行为。
-
int viewVisibility:窗口初始可见性,对应 View.VISIBLE/INVISIBLE/GONE 三种状态,影响 WMS 是否对该窗口执行绘制与布局操作。
-
int layerStackId:窗口所属的图层栈 ID,用于对窗口进行层级分组管理(例如普通应用、状态栏、壁纸分属不同图层栈),进而影响窗口在 Z 轴上的排序。
-
int userId:窗口所属的用户 ID,在多用户场景下用于隔离不同用户的窗口资源,保证用户间的独立性。
-
InsetsVisibilities requestedVisibilities:App 对 Insets(如状态栏、导航栏等系统 UI 区域)的可见性偏好设置,供 WMS 计算最终的 Insets 布局方案。
-
out InputChannel outInputChannel:输出参数,由 WMS 创建的输入通道,用于窗口接收触摸、按键等输入事件,是窗口与 InputManager 实现通信的关键。
-
out InsetsState insetsState:输出参数,返回当前窗口对应的 Insets 状态(如各 Insets 区域的尺寸、可见性等),为 App 进行布局适配提供依据。
-
out InsetsSourceControl[] activeControls:输出参数,返回 App 可控制的 Insets 源(如导航栏)控制权,允许 App 动态调整特定 Insets 的显示状态。
这些参数共同完成了窗口从 App 端到 WMS 的注册流程,涵盖窗口属性定义、交互能力配置、层级划分、用户隔离等核心信息,是 Client 端与 WMS 进行交互的关键数据载体。
WMS Server 端关键数据结构
-
WindowContainer:WMS 窗口树中的容器基类,内部维护了父容器(parent)和子容器列表(child list),提供添加子容器、设置父容器、遍历子容器等容器相关的基础操作。
-
WindowState:窗口管理的核心类,负责管理单个窗口的状态与行为。它存储了窗口的尺寸、位置、层级、可见性等关键信息,协调窗口的排版、Z-order 排序、焦点切换,处理窗口的显示 / 隐藏、动画执行及与 SurfaceFlinger 的交互,是系统管控窗口生命周期与界面呈现的核心载体。
-
WindowToken:WindowState 的子类,核心成员为 token(令牌),该 token 在多处作为查询 WindowToken 的关键字。WallpaperWindowToken(壁纸窗口令牌)和 ActivityRecord(Activity 记录)是两种典型的 WindowToken 具体实现。
-
TaskFragment 与 Task:均为存放 Activity 的容器类,主要用于 Activity 所属任务(Task)的管理,负责维护任务内 Activity 的栈结构与跳转逻辑。
-
DisplayArea:表示显示器中的特定显示区域,其衍生类包括:
- DisplayArea.Dimmable:支持 “变暗”(dim)效果的显示区域;
- DisplayArea.Tokens:用于存储各类非 Activity 窗口的区域;
- TaskDisplayArea:专门存储 Task(任务)的显示区域;
- RootDisplayArea:代表显示区域的根节点,每个显示器对应一个独立的 RootDisplayArea。
-
DisplayContent:代表一个物理或虚拟显示器,封装了与该显示器相关的所有窗口管理逻辑与数据。
-
RootWindowContainer:代表整个系统窗口管理的根节点,其直接子类为 DisplayContent,是构建系统级窗口树的顶层容器。
WindowToken 与 WindowState 的关系
WMS 启动后,会依据系统规则初始化好基础的窗口树结构。当添加新窗口时,会执行以下关联逻辑:
- 为新窗口生成对应的 WindowToken 和 WindowState;
- 将 WindowState 作为子节点添加到对应的 WindowToken 中;
- 再将 WindowToken 挂载到系统的窗口树中,完成窗口在树结构中的注册。
WMS 窗口添加具体流程
IWindowSession.addToDisplayAsUser 接口的实际实现逻辑位于 WindowManagerService.addWindow 方法中,具体流程如下:
1. 权限检查
首先校验当前 App 是否具备添加对应类型窗口的权限,若权限不通过则直接返回失败结果:
int res = mPolicy.checkAddPermission(attrs.type, isRoundedCornerOverlay, attrs.packageName,
appOp);
if (res != ADD_OKAY) {
return res;
}
2. 获取 WindowToken
根据窗口类型(是否为子窗口、是否关联 Activity 等)获取或创建对应的 WindowToken:
ActivityRecord activity = null;
final boolean hasParent = parentWindow != null;
// 子窗口复用父窗口的 WindowToken,确保父子窗口遵循相同的管理策略
WindowToken token = displayContent.getWindowToken(
hasParent ? parentWindow.mAttrs.token : attrs.token);
- 对于 Activity 关联的窗口:其对应的 WindowToken 实际是 ActivityRecord(WindowToken 子类),该 ActivityRecord 在 Activity 启动过程中已创建(具体逻辑位于 ActivityStarter#executeRequest),此处通过 displayContent.getWindowToken 即可直接查询到,且 ActivityRecord 会通过所属 Task 提前挂载到窗口树中。
- 对于 非 Activity 窗口(如悬浮窗、对话框等):若未找到已存在的 WindowToken,则会新建 WindowToken,并通过 DisplayContent#addWindowToken 方法将其添加到窗口树中。
3. 生成 WindowState
创建 WindowState 实例,封装当前窗口的所有状态与配置信息:
final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], attrs, viewVisibility, session.mUid, userId,
session.mCanAddInternalSystemWindow);
4. 注册 WindowState 到全局映射
将 WindowState 挂载到 WMS 的全局窗口映射表 mWindowMap 中,便于后续通过 IWindow 回调接口快速查找对应的 WindowState:
win.attach();
mWindowMap.put(client.asBinder(), win);
win.initAppOpsState();
5. 关联 WindowState 与 WindowToken
将 WindowState 作为子节点添加到其对应的 WindowToken 中,建立两者的从属关系:
win.mToken.addWindow(win);
6. 触发层级重新排序
通知当前窗口的父容器重新计算子窗口的 Z 轴层级(Z-order),确保窗口在屏幕上的显示顺序正确(注:此处不直接执行布局,窗口的最终布局需通过后续 relayout 方法触发):
// 此处不执行布局操作,窗口需调用 relayout 方法才会显示,布局逻辑将在 relayout 中完成
win.getParent().assignChildLayers();
至此,新窗口已完成在 WMS 窗口树中的注册与挂载,后续将通过 relayout 等流程完成最终的绘制与显示。
通过 dump 命令查看窗口信息
可通过以下 ADB 命令查看系统中窗口的相关信息,用于调试与分析:
- 查看窗口树结构:adb shell dumpsys activity containers
输出系统中所有窗口容器(如 Task、DisplayArea 等)的层级关系与基本信息。
- 查看窗口详细状态(WindowState) :adb shell dumpsys window windows
输出所有窗口的 WindowState 详情,包括尺寸、位置、类型、可见性、所属 Token 等。
- 查看窗口令牌信息(WindowToken) :adb shell dumpsys window tokens
输出所有 WindowToken 的信息,包括 Token 类型等。