Android 窗口管理 - 窗口添加过程分析Server端

90 阅读6分钟

(承接上文: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 端关键数据结构

  1. WindowContainer:WMS 窗口树中的容器基类,内部维护了父容器(parent)和子容器列表(child list),提供添加子容器、设置父容器、遍历子容器等容器相关的基础操作。

  2. WindowState:窗口管理的核心类,负责管理单个窗口的状态与行为。它存储了窗口的尺寸、位置、层级、可见性等关键信息,协调窗口的排版、Z-order 排序、焦点切换,处理窗口的显示 / 隐藏、动画执行及与 SurfaceFlinger 的交互,是系统管控窗口生命周期与界面呈现的核心载体。

  3. WindowToken:WindowState 的子类,核心成员为 token(令牌),该 token 在多处作为查询 WindowToken 的关键字。WallpaperWindowToken(壁纸窗口令牌)和 ActivityRecord(Activity 记录)是两种典型的 WindowToken 具体实现。

  4. TaskFragment Task:均为存放 Activity 的容器类,主要用于 Activity 所属任务(Task)的管理,负责维护任务内 Activity 的栈结构与跳转逻辑。

  5. DisplayArea:表示显示器中的特定显示区域,其衍生类包括:

  • DisplayArea.Dimmable:支持 “变暗”(dim)效果的显示区域;
  • DisplayArea.Tokens:用于存储各类非 Activity 窗口的区域;
  • TaskDisplayArea:专门存储 Task(任务)的显示区域;
  • RootDisplayArea:代表显示区域的根节点,每个显示器对应一个独立的 RootDisplayArea。
  1. DisplayContent:代表一个物理或虚拟显示器,封装了与该显示器相关的所有窗口管理逻辑与数据。

  2. RootWindowContainer:代表整个系统窗口管理的根节点,其直接子类为 DisplayContent,是构建系统级窗口树的顶层容器。

WindowToken 与 WindowState 的关系

WMS 启动后,会依据系统规则初始化好基础的窗口树结构。当添加新窗口时,会执行以下关联逻辑:

  1. 为新窗口生成对应的 WindowToken 和 WindowState;
  2. 将 WindowState 作为子节点添加到对应的 WindowToken 中;
  3. 再将 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 命令查看系统中窗口的相关信息,用于调试与分析:

  1. 查看窗口树结构:adb shell dumpsys activity containers

输出系统中所有窗口容器(如 Task、DisplayArea 等)的层级关系与基本信息。

  1. 查看窗口详细状态(WindowState) :adb shell dumpsys window windows

输出所有窗口的 WindowState 详情,包括尺寸、位置、类型、可见性、所属 Token 等。

  1. 查看窗口令牌信息(WindowToken) :adb shell dumpsys window tokens

输出所有 WindowToken 的信息,包括 Token 类型等。