一、WMS 核心数据结构:它们到底怎么组织的?

说实话,我刚接触 WMS 源码那会儿,最头疼的就是这些数据结构之间的关系。WindowList、WindowState、DisplayContent、Task、Stack……光名字就够绕的。但你别怕,这些东西说白了就是一套「窗口管理」的骨架。搞懂了它们,你就掌握了 WMS 的命脉。

我个人习惯把 WMS 的数据结构想象成一个「多屏多任务」的树形结构。嗯,咱们一层层拆开看。

1.1 顶层容器:DisplayContent

每个物理屏幕(或者虚拟屏幕)对应一个 DisplayContent。你可以把它理解成「一块画布」。手机主屏是一个 DisplayContent,如果外接了显示器或者投屏,那就会多出来一个。

核心要点:DisplayContent 是 WMS 中所有窗口的根容器。它管理着当前屏幕上的所有显示内容。

我记得有一次做双屏折叠设备的适配,发现副屏的窗口死活不显示。查了半天,原来是 DisplayContent 的创建时机不对。嗯,这里要注意:每个 DisplayContent 都有自己的 mStacks 列表,用来存放它上面的所有 TaskStack。

1.2 中间层:Stack 与 Task

Stack 是什么?说白了就是「应用栈」的容器。每个 Stack 里可以放多个 Task。比如你打开了微信,又打开了浏览器,它们可能属于不同的 Stack(全屏栈、分屏栈、自由窗口栈)。

Task 呢?它代表一个「应用的任务记录」。你打开微信,这就是一个 Task。你从微信里点开一个链接跳到浏览器,那浏览器又是一个新的 Task(或者复用已有的 Task)。

数据结构 作用 包含关系
DisplayContent 管理一个屏幕的所有内容 包含多个 Stack
TaskStack 管理一组 Task 的显示层级 包含多个 Task
Task 管理一个应用的 Activity 栈 包含多个 WindowState

避坑指南:我曾经在调试分屏功能时,发现两个应用无法同时显示。后来发现是 Stack 的 mStackId 冲突了。每个 Stack 必须有唯一的 ID,否则 WMS 会把它当成同一个栈来处理。

1.3 最底层:WindowState 与 WindowList

WindowState 是 WMS 里最核心的数据结构。每个窗口(无论是 Activity 的窗口、Dialog、还是系统窗口)都会对应一个 WindowState。它记录了窗口的位置、大小、层级、可见性等等。

WindowList 呢?它就是一个 ArrayList<WindowState>。每个 Task 内部会维护一个 WindowList,用来存放它所有的窗口。窗口的 Z-order(谁在上面谁在下面)就是通过这个列表的顺序来控制的。

// 伪代码示意:WindowState 的核心字段
class WindowState {
    int mWindowId;          // 窗口唯一 ID
    Rect mFrame;            // 窗口位置和大小
    int mBaseLayer;         // 基础层级
    int mSubLayer;          // 子层级
    boolean mHasSurface;    // 是否有 Surface
    Task mTask;             // 所属 Task
    DisplayContent mDisplayContent; // 所属屏幕
}

你想想看,一个窗口从创建到显示,经历了多少层?从 DisplayContent 到 Stack,再到 Task,最后到 WindowList。每一层都在做一件事:分层管理

1.4 组织关系全景图

下面这张图是我自己画的,帮你把整个关系串起来。别嫌丑,能看懂就行。

DisplayContent(屏幕容器) TaskStack(应用栈容器) mStackId = 1(全屏栈) | mStackId = 2(分屏栈) | mStackId = 3(自由窗口栈) Task(应用任务) taskId = 1001(微信) | taskId = 1002(浏览器) WindowList(窗口列表) [WindowState#1, WindowState#2, WindowState#3, ...]

从上往下看,就是一条完整的「窗口归属链」。每个 WindowState 都知道自己属于哪个 Task,哪个 Stack,哪个 DisplayContent。反过来,DisplayContent 也能通过层层遍历找到它上面的所有窗口。

1.5 为什么这么设计?

你可能会问:搞这么复杂干嘛?直接一个全局列表不就行了?

嗯,原因很简单:多窗口和多屏幕。如果没有分层,你怎么区分哪个窗口在哪个屏幕上?怎么处理分屏时两个应用互不干扰?怎么实现自由窗口的拖拽和缩放?

我记得在 Android 7.0 之前,WMS 的数据结构还没这么完善。那时候做分屏,各种 hack,代码写得跟屎一样。后来 Android 引入了 TaskStack 和 DisplayContent 的分层设计,整个架构才清晰起来。

注意:不要试图直接修改 WindowList 的顺序来控制窗口层级。WMS 内部有严格的 Z-order 计算逻辑,乱改会导致窗口显示异常甚至崩溃。

1.6 实战中的组织关系

假设你现在打开了一个应用,比如微信。WMS 会做以下事情:

  1. 找到当前焦点所在的 DisplayContent(通常是主屏)。
  2. 在 DisplayContent 的 mStacks 中找到合适的 Stack(比如全屏栈)。
  3. 在 Stack 中创建或复用 Task(微信的 Task)。
  4. 在 Task 的 WindowList 中添加微信的 WindowState。
  5. WMS 根据 WindowList 的顺序和层级,决定哪些窗口可见、哪些被遮挡。

整个过程发生在毫秒级别。你感觉不到,但 WMS 已经忙得不可开交了。

我个人觉得,理解这套组织关系的关键在于:不要把它当成静态的数据结构,而要当成动态的「窗口树」。每次窗口变化(打开、关闭、移动、缩放),WMS 都会重新遍历这棵树,更新显示状态。

好了,这一章就到这里。下一章我们会深入 WindowState 的内部细节,看看它到底是怎么管理窗口状态的。


公众号:蓝海资料掘金营,微信deep3321