窗口容器树系列文章:
- Android WMS(一)—— 窗口和窗口容器树
- Android WMS(二)—— 窗口容器树的构建
1. WMS 服务进程启动
1.1 WMS 服务进程启动,创建 RootWindowContainer 实例
SystemServer 进程首先在 startBootstrapServices 中创建初始化了 ATMS、AMS,AMS 构造时传入 ATMS 对象,AMS 持有 ATMS:
private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) {
...
// 【ATMS启动】ActivityTaskManagerService 负责 Activity 和 Task 的管理
ActivityTaskManagerService atm = mSystemServiceManager.startService(
ActivityTaskManagerService.Lifecycle.class).getService();
// 【AMS启动】ActivityManagerService 是系统的总调度器
mActivityManagerService = ActivityManagerService.Lifecycle.startService(
mSystemServiceManager, atm);
...
}
SystemServer 进程的 startOtherServices 方法中启动了 WMS 服务:
private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
...
// 启动 WMS 服务进程,在启动 AMS 之后
mSystemServiceManager.startBootPhase(t, SystemService.PHASE_WAIT_FOR_SENSOR_SERVICE);
wm = WindowManagerService.main(context, inputManager, !mFirstBoot,
new PhoneWindowManager(), mActivityManagerService.mActivityTaskManager);
ServiceManager.addService(Context.WINDOW_SERVICE, wm, /* allowIsolated= */ false,
DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PROTO);
// 关注点,向 AMS 添加 WMS 实例
mActivityManagerService.setWindowManager(wm);
...
}
通过静态工厂方法 main() 创建 WMS 实例。传入了 ATMS 对象, WMS 持有 ATMS:
public static WindowManagerService main(final Context context, final InputManagerService im,
final boolean showBootMsgs, WindowManagerPolicy policy,
ActivityTaskManagerService atm) {
return main(context, im, showBootMsgs, policy, atm, new DisplayWindowSettingsProvider(),
SurfaceControl.Transaction::new, SurfaceControl.Builder::new);
}
@VisibleForTesting
public static WindowManagerService main(final Context context, final InputManagerService im,
final boolean showBootMsgs, WindowManagerPolicy policy, ActivityTaskManagerService atm,
DisplayWindowSettingsProvider displayWindowSettingsProvider,
Supplier<SurfaceControl.Transaction> transactionFactory,
Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory) {
final WindowManagerService[] wms = new WindowManagerService[1];
// 使用 DisplayThread.getHandler().runWithScissors 是为了将 WMS 的创建强制切换到专用的 DisplayThread 上同步执行,
// 因为 WMS 依赖 VSYNC、动画、SurfaceControl 等显示子系统能力,这些逻辑必须在单一显示线程中串行运行以保证线程上下文(Looper/Choreographer/ThreadLocal)一致;
// 同时 SystemServer 主线程作为系统调度中枢不能承载高频显示任务,否则会引发调度阻塞与锁竞争。
// 因此通过 runWithScissors 在 DisplayThread 执行并阻塞等待结果,既保证线程归属正确,又保证初始化对系统是同步且确定的。
DisplayThread.getHandler().runWithScissors(() ->
wms[0] = new WindowManagerService(context, im, showBootMsgs, policy, atm,
displayWindowSettingsProvider, transactionFactory,
surfaceControlFactory), 0);
return wms[0];
}
启动 WMS 服务进程,在 WMS 的构造方法中,创建 RootWindowContainer 实例,RootWindowContainer 是窗口管理树的根节点,并创建了 DisplayAreaPolicy.Provider 对象:
private WindowManagerService(Context context, InputManagerService inputManager,
boolean showBootMsgs, WindowManagerPolicy policy, ActivityTaskManagerService atm,
DisplayWindowSettingsProvider displayWindowSettingsProvider,
Supplier<SurfaceControl.Transaction> transactionFactory,
Function<SurfaceSession, SurfaceControl.Builder> surfaceControlFactory) {
mRoot = new RootWindowContainer(this);
// 实际类型是 DisplayAreaPolicy.DefaultProvider
mDisplayAreaPolicyProvider = DisplayAreaPolicy.Provider.fromResources(mContext.getResources());
}
DisplayAreaPolicy 定义了 DisplayArea 的层次结构和窗口分组策略。
通过 fromResources 方法从系统资源中读取配置,如果没有自定义配置则使用 DefaultProvider:
static Provider fromResources(Resources res) {
String name = res.getString(
com.android.internal.R.string.config_deviceSpecificDisplayAreaPolicyProvider);
if (TextUtils.isEmpty(name)) {
return new DisplayAreaPolicy.DefaultProvider();
}
try {
return (Provider) Class.forName(name).newInstance();
} catch (ReflectiveOperationException | ClassCastException e) {
throw new IllegalStateException("Couldn't instantiate class " + name
+ " for config_deviceSpecificDisplayAreaPolicyProvider:"
+ " make sure it has a public zero-argument constructor"
+ " and implements DisplayAreaPolicy.Provider", e);
}
}
这里会去读取 config_deviceSpecificDisplayAreaPolicyProvider 这个 String 资源,在源码中没有搜索到这个 String 资源的设置,当该资源为空时,会 new 一个 DisplayAreaPolicy.DefaultProvider 对象返回。所以 mDisplayAreaPolicyProvider 的实际类型是 DisplayAreaPolicy.DefaultProvider.
本阶段几个有意思的问题:
Q1:为什么不使用反射创建?
- WMS 的实例化需要大量特定参数。而 SystemServiceManager.startService 的反射机制只支持单一 Context 参数的构造函数。
- WMS 的核心组件必须在 DisplayThread 中初始化。
Q2:为什么使用工厂方法创建?
- 隐藏了复杂的实例化逻辑和依赖项(如后面调用的重载 main 方法中注入的 provider 和 factory)。
- 可以在创建对象前做一些准备工作。
Q3:为什么使用数组保存 WMS 实例?
Lambda 捕获的局部变量必须是 final 或 effectively final。直接声明 WindowManagerService wms 无法在 Lambda 内部赋值。将变量包装在 final 数组中:数组引用不可变,但元素可修改,因此可通过 wms[0] 将 DisplayThread 中创建的实例传递出来。
Q4:为什么通过 DisplayThread.getHandler().runWithScissors 创建 WMS 实例?
- 绑定专属线程: WMS 负责核心 UI 与窗口管理,将其绑定在专用的高优先级 DisplayThread 中初始化,既能避免与其他系统服务抢夺 CPU 资源,又能保证其内部复杂数据结构的线程安全。
- 跨线程与同步阻塞: runWithScissors 的作用是将实例化 WMS 的任务投递给 DisplayThread 执行,同时强制挂起并阻塞当前的 SystemServer 主线程(参数 0 代表无限期死等,直到执行完毕)。
- 保障启动时序强依赖: Android 核心服务启动一环扣一环,后续服务(如 AMS)强依赖已完全初始化完毕的 WMS。强制阻塞主线程是为了防止后续代码跑得太快导致拿到空指针,引发系统崩溃。
Q5:fromResources 方法从系统资源中读取配置的思想?
- 支持设备多形态,避免 WMS 核心代码被污染。解耦默认的 DefaultProvider 只针对最基础的单屏幕手机形态。在里面,所有的应用窗口都被扔进唯一的 DefaultTaskDisplayArea 中。但现在的 Android 需要运行在折叠屏、车机屏幕、智能电视甚至双屏设备(如 Surface Duo)上。
- 利用 Android 的资源覆盖机制 (RRO) 实现“运行时插拔”,无侵入式定制扩展。在 Provider 的 fromResources 方法中,系统会读取一个配置:config_deviceSpecificDisplayAreaPolicyProvider。这种设计意味着:厂商不需要修改 Android 的核心 Java 源码。他们只需要在自己设备的 res/values/config.xml 文件中,将这个字符串替换为自己实现的类的全限定名(例如 com.android.car.CarDisplayAreaPolicyProvider)。
1.2 WMS 服务进程启动,AMS、ATMS、WMS、RootWindowContainer 之间建立联系
接着调用了 AMS 的 setWindowManager 方法向 AMS 添加 WMS 实例:
public void setWindowManager(WindowManagerService wm) {
synchronized (this) {
mWindowManager = wm;
mWmInternal = LocalServices.getService(WindowManagerInternal.class);
// 向 ATMS 添加 WMS 实例
mActivityTaskManager.setWindowManager(wm);
}
}
向 ATMS 添加 WMS 实例:
// frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
public void setWindowManager(com.android.server.wm.WindowManagerService wm) {
synchronized (mGlobalLock) {
mWindowManager = wm;
// ATMS 持有了 RootWindowContainer 实例
mRootWindowContainer = wm.mRoot;
mWindowOrganizerController.mTransitionController.setWindowManager(wm);
mLifecycleManager.setWindowManager(wm);
mTempConfig.setToDefaults();
mTempConfig.setLocales(LocaleList.getDefault());
mConfigurationSeq = mTempConfig.seq = 1;
mRootWindowContainer.onConfigurationChanged(mTempConfig);
mLockTaskController.setWindowManager(wm);
mTaskSupervisor.setWindowManager(wm);
// 从 mRootWindowContainer.setWindowManager(wm) 开始,就开始了窗口容器树的构建过程
mRootWindowContainer.setWindowManager(wm);
mBackNavigationController.setWindowManager(wm);
}
}
将 WMS 持有的 RootWindowContainer 实例赋值给了 ATMS 的 mRootWindowContainer。接着就调用了 mRootWindowContainer 的 setWindowManager 方法,此时就开始了窗口容器树的构建过程。
2. RootWindowContainer 构建窗口容器树
2.1 RootWindowContainer setWindowManager 开始构建窗口容器树
一个 Android 系统中只存在一个 RootWindowContainer,它作为全局窗口树的根,统一管理所有 DisplayContent。
看一下 RootWindowContainer 的 setWindowManager 方法:
void setWindowManager(WindowManagerService wm) {
// 将 WMS 实例传递给 RootWindowContainer
mWindowManager = wm;
mDisplayManager = mService.mContext.getSystemService(DisplayManager.class);
mDisplayManager.registerDisplayListener(this, mService.mUiHandler);
mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);
// 获取屏幕个数
final Display[] displays = mDisplayManager.getDisplays();
// 遍历每一个 Display,为每一个 Display 构建一个 DisplayContent。
for (int displayNdx = 0; displayNdx < displays.length; ++displayNdx) {
final Display display = displays[displayNdx];
// 为每一个 Display 创建一个 DisplayContent 对象。在窗口容器树中,DisplayContent 就代表了一个屏幕。
final DisplayContent displayContent = new DisplayContent(display, this, mDeviceStateController);
addChild(displayContent, POSITION_BOTTOM);
// DEFAULT_DISPLAY 0 默认的主屏
if (displayContent.mDisplayId == DEFAULT_DISPLAY) {
mDefaultDisplay = displayContent;
}
}
final TaskDisplayArea defaultTaskDisplayArea = getDefaultTaskDisplayArea();
defaultTaskDisplayArea.getOrCreateRootHomeTask(ON_TOP);
positionChildAt(POSITION_TOP, defaultTaskDisplayArea.mDisplayContent,
false /* includingParents */);
}
通过 DisplayService 获取到了当前设备所有屏幕,接着遍历每一个 Display(屏幕),为每一个 Display 构建一个 DisplayContent。在窗口容器树中,DisplayContent 就代表了一个屏幕。
2.2 DisplayContent 初始化
看一下 DisplayContent 初始化:
DisplayContent(Display display, RootWindowContainer root,
@NonNull DeviceStateController deviceStateController) {
super(root.mWindowManager, "DisplayContent", FEATURE_ROOT);
...
// 创建事务
final Transaction pendingTransaction = getPendingTransaction();
// 开始构建层级树
configureSurfaces(pendingTransaction);
// 执行事务
pendingTransaction.apply();
...
}
看一下 DisplayContent 的 configureSurfaces 方法:
private void configureSurfaces(Transaction transaction) {
// 构建一个 SurfaceControl,表示 SurfaceFlinger 中的一个图层节点
final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(mSession)
.setOpaque(true)
.setContainerLayer()
.setCallsite("DisplayContent");
// 设置名字后构建 (Display 0 name="XXX")Display 0 name="Built-in Screen"
mSurfaceControl = b.setName(getName()).setContainerLayer().build();
if (mDisplayAreaPolicy == null) {
// Setup the policy and build the display area hierarchy.
// Build the hierarchy only after creating the surface so it is reparented correctly
// 关注点,构建树结构。返回一个 DisplayAreaPolicy.Provider 对象
// 构建 DisplayArea 树结构,为不同窗口类型(如状态栏、对话框)分配层级。
mDisplayAreaPolicy = mWmService.getDisplayAreaPolicyProvider().instantiate(
mWmService, this /* content */, this /* root */,
mImeWindowsContainer);
}
final List<DisplayArea<? extends WindowContainer>> areas =
mDisplayAreaPolicy.getDisplayAreas(FEATURE_WINDOWED_MAGNIFICATION);
final DisplayArea<?> area = areas.size() == 1 ? areas.get(0) : null;
if (area != null && area.getParent() == this) {
// The windowed magnification area should contain all non-overlay windows, so just use
// it as the windowing layer.
mWindowingLayer = area.mSurfaceControl;
transaction.reparent(mWindowingLayer, mSurfaceControl);
} else {
// Need an additional layer for screen level animation, so move the layer containing
// the windows to the new root.
mWindowingLayer = mSurfaceControl;
mSurfaceControl = b.setName("RootWrapper").build();
transaction.reparent(mWindowingLayer, mSurfaceControl)
.show(mWindowingLayer);
}
if (mOverlayLayer == null) {
mOverlayLayer = b.setName("Display Overlays").setParent(mSurfaceControl).build();
} else {
transaction.reparent(mOverlayLayer, mSurfaceControl);
}
if (mInputOverlayLayer == null) {
mInputOverlayLayer = b.setName("Input Overlays").setParent(mSurfaceControl).build();
} else {
transaction.reparent(mInputOverlayLayer, mSurfaceControl);
}
if (mA11yOverlayLayer == null) {
mA11yOverlayLayer =
b.setName("Accessibility Overlays").setParent(mSurfaceControl).build();
} else {
transaction.reparent(mA11yOverlayLayer, mSurfaceControl);
}
// 事务相关设置
transaction
.setLayer(mSurfaceControl, 0)
.setLayerStack(mSurfaceControl, mDisplayId)
.show(mSurfaceControl)
.setLayer(mOverlayLayer, Integer.MAX_VALUE)
.show(mOverlayLayer)
.setLayer(mInputOverlayLayer, Integer.MAX_VALUE - 1)
.show(mInputOverlayLayer)
.setLayer(mA11yOverlayLayer, Integer.MAX_VALUE - 2)
.show(mA11yOverlayLayer);
}
首先创建 DisplayContent 的根 SurfaceControl。SurfaceControl 是系统合成器 (SurfaceFlinger) 管理的屏幕上 Surface 的句柄。SurfaceControl 用于创建、配置和管理 Surface 的显示属性(如大小、位置、透明度、层级等)。
DisplayAreaPolicy 类负责建立和管理 DisplayArea 的层级结构,定义显示区域的划分策略(如系统区、应用区)。
2.3 构建 HierarchyBuilder,配置 Feature
这里直接调用 DefaultProvider 的 instantiate 进行构建 DisplayArea 树结构,为不同窗口类型(如状态栏、对话框)分配层级:
// 默认显式策略
@Override
public DisplayAreaPolicy instantiate(WindowManagerService wmService,
DisplayContent content, RootDisplayArea root,
DisplayArea.Tokens imeContainer) {
// 创建一个名为 "DefaultTaskDisplayArea" 的对象作为应用窗口的默认容器,应用窗口容器
final TaskDisplayArea defaultTaskDisplayArea = new TaskDisplayArea(content, wmService,
"DefaultTaskDisplayArea", FEATURE_DEFAULT_TASK_CONTAINER);
final List<com.android.server.wm.TaskDisplayArea> tdaList = new ArrayList<>();
tdaList.add(defaultTaskDisplayArea);
// Define the features that will be supported under the root of the whole logical
// display. The policy will build the DisplayArea hierarchy based on this.
// 传递 RootDisplayArea(DisplayContent)构建出一个层级树的数据结构,用于后续窗口容器树的构建。
final HierarchyBuilder rootHierarchy = new HierarchyBuilder(root);
// Set the essential containers (even if the display doesn't support IME).
// HierarchyBuilder 设置输入法容器和应用窗口容器
rootHierarchy.setImeContainer(imeContainer).setTaskDisplayAreas(tdaList);
if (content.isTrusted()) {
// Only trusted display can have system decorations.
// 配置层级的支持的 Feature
configureTrustedHierarchyBuilder(rootHierarchy, wmService, content);
}
// Instantiate the policy with the hierarchy defined above. This will create and attach
// all the necessary DisplayAreas to the root.
// 开始构建窗口容器树
return new com.android.server.wm.DisplayAreaPolicyBuilder().setRootHierarchy(rootHierarchy).build(wmService);
}
创建一个名为 DefaultTaskDisplayArea 的对象作为应用窗口的默认容器。TaskDisplayArea 代表了屏幕上一块专门用来存放 App 窗口的区域。使用列表而非单个对象,是为了支持多 TDA 场景(如分屏模式下的多个 TDA)。
构建出 DisplayContent 的层级树的数据结构 HierarchyBuilder,HierarchyBuilder 是层级树的"蓝图",用于后续窗口容器树的构建。
先设置 IME 容器,再设置 TDA 列表。
接着通过 configureTrustedHierarchyBuilder 配置层级的支持的 Feature:
/**
* Feature.mName Feature.mID Feature.mWindowLayers 数组为 true 的区间
* WindowedMagnification FEATURE_WINDOWED_MAGNIFICATION [0,31]
* HideDisplayCutout FEATURE_HIDE_DISPLAY_CUTOUT [0,14], 16, [18,23], [26,35]
* OneHanded FEATURE_ONE_HANDED [0,23], [26,32],[34,35]
* FullscreenMagnification FEATURE_FULLSCREEN_MAGNIFICATION [0,12], [15,23], [26,27], [29,31], [33,35]
* ImePlaceholder FEATURE_IME_PLACEHOLDER [13,14]
*/
private void configureTrustedHierarchyBuilder(HierarchyBuilder rootHierarchy,
WindowManagerService wmService, DisplayContent content) {
// WindowedMagnification should be on the top so that there is only one surface
// to be magnified.
// 通过 Feature.Builder 构造一个 Feature 对象
// 再调用 HierarchyBuilder 对象的 addFeature 方法,将构造好的 Feature 对象保存到 HierarchyBuilder 对象内部的
// ArrayList<DisplayAreaPolicyBuilder.Feature> mFeatures 成员中
// 构造好了名为 WindowedMagnification 的 Feature 对象
rootHierarchy.addFeature(new Feature.Builder(wmService.mPolicy, "WindowedMagnification",
FEATURE_WINDOWED_MAGNIFICATION)
.upTo(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY) // 0-32 true
.except(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY)// 32 false
.setNewDisplayAreaSupplier(DisplayArea.Dimmable::new)// new 一个 DisplayArea.Dimmable 保存在 Builder 的 mNewDisplayAreaSupplier 成员中
.build()); // 最后调用 build 方法,new 一个 Feature 返回,前面准备好的数据都作为参数传入构造方法,然后保存到 Feature 中
if (content.isDefaultDisplay) {
// Only default display can have cutout.
// See LocalDisplayAdapter.LocalDisplayDevice#getDisplayDeviceInfoLocked.
rootHierarchy.addFeature(new Feature.Builder(wmService.mPolicy, "HideDisplayCutout",
FEATURE_HIDE_DISPLAY_CUTOUT)
.all()
.except(TYPE_NAVIGATION_BAR, TYPE_NAVIGATION_BAR_PANEL, TYPE_STATUS_BAR,
TYPE_NOTIFICATION_SHADE)
.build())
.addFeature(new Feature.Builder(wmService.mPolicy, "OneHanded",
FEATURE_ONE_HANDED)
.all()
.except(TYPE_NAVIGATION_BAR, TYPE_NAVIGATION_BAR_PANEL,
TYPE_SECURE_SYSTEM_OVERLAY)
.build());
}
rootHierarchy
.addFeature(new Feature.Builder(wmService.mPolicy, "FullscreenMagnification",
FEATURE_FULLSCREEN_MAGNIFICATION)
.all()
.except(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, TYPE_INPUT_METHOD,
TYPE_INPUT_METHOD_DIALOG, TYPE_MAGNIFICATION_OVERLAY,
TYPE_NAVIGATION_BAR, TYPE_NAVIGATION_BAR_PANEL)
.build())
.addFeature(new Feature.Builder(wmService.mPolicy, "ImePlaceholder",
FEATURE_IME_PLACEHOLDER)
.and(TYPE_INPUT_METHOD, TYPE_INPUT_METHOD_DIALOG)
.build());
}
DisplayArea 节点会有自己的 Feature,来指定节点所包含的特殊功能。配置层级的支持的 Feature。一共有 5 个 Feature :
- WindowedMagnification
- HideDisplayCutout
- OneHanded
- FullscreenMagnification
- ImePlaceholder
setRootHierarchy() 将 HierarchyBuilder 保存到 DisplayAreaPolicyBuilder。
2.4 构建 PendingArea 树
继续调用 DisplayAreaPolicyBuilder 的 build 方法:
// 构建 PendingArea 树
// 根据 PendingArea 树构建最终的窗口容器树
Result build(WindowManagerService wmService) {
validate();
mRootHierarchyBuilder.build(mDisplayAreaGroupHierarchyBuilders);
List<RootDisplayArea> displayAreaGroupRoots = new ArrayList<>(
mDisplayAreaGroupHierarchyBuilders.size());
// mDisplayAreaGroupHierarchyBuilders 是一个空列表
for (int i = 0; i < mDisplayAreaGroupHierarchyBuilders.size(); i++) {
HierarchyBuilder hierarchyBuilder = mDisplayAreaGroupHierarchyBuilders.get(i);
hierarchyBuilder.build();
displayAreaGroupRoots.add(hierarchyBuilder.mRoot);
}
// Use the default function if it is not specified otherwise.
if (mSelectRootForWindowFunc == null) {
mSelectRootForWindowFunc = new DefaultSelectRootForWindowFunction(
mRootHierarchyBuilder.mRoot, displayAreaGroupRoots);
}
return new Result(wmService, mRootHierarchyBuilder.mRoot, displayAreaGroupRoots,
mSelectRootForWindowFunc, mSelectTaskDisplayAreaFunc);
}
继续看一下 HierarchyBuilder 的 build 方法,这里主要通过两个循环构建了 PendingArea 树,完成之后再使用 PendingArea 树构建出目标的窗口容器树。build 方法较长,分几个阶段来看。首先准备构建所需的数据结构:
private void build(@Nullable List<HierarchyBuilder> displayAreaGroupHierarchyBuilders) {
// 初始化局部变量
final WindowManagerPolicy policy = mRoot.mWmService.mPolicy;
// 定义最大层级数 37
final int maxWindowLayerCount = policy.getMaxWindowLayer() + 1;
// 存储每个窗口层级对应的 DisplayArea.Tokens 37 个
final DisplayArea.Tokens[] displayAreaForLayer =
new DisplayArea.Tokens[maxWindowLayerCount];
// 存储每个 Feature 对应的 DisplayArea 列表
final Map<Feature, List<DisplayArea<WindowContainer>>> featureAreas =
new ArrayMap<>(mFeatures.size());
for (int i = 0; i < mFeatures.size(); i++) {
featureAreas.put(mFeatures.get(i), new ArrayList<>());
}
// 初始化 PendingArea 数组,用于临时存储每个窗口层级对应的 PendingArea,一共 37 个
PendingArea[] areaForLayer = new PendingArea[maxWindowLayerCount];
final PendingArea root = new PendingArea(null, 0, null);
// areaForLayer 数组全部填充为 root
Arrays.fill(areaForLayer, root);
...
}
displayAreaForLayer 保存每一个 Window Layer 对应的 DisplayArea.Tokens 对象,避免后续创建 WindowToken 时,每次都重新遍历 DisplayArea 层级树去寻找对应的 DisplayArea.Tokens。
featureAreas 存储每个 Feature 对应的 DisplayArea 列表。
areaForLayer 记录的不是最终的 DisplayArea,也不是窗口。它记录的是:当前 layer 在 PendingArea 构建阶段,应该挂载到哪个“最近的父 PendingArea 节点”。
接着创建 PendingArea 树的根节点 root,不关联任何 Feature。初始将所有层级都指向根节点,表示初始时所有窗口都附加到根节点。
随后的 Feature 遍历会将这些连接“打断”并插入新的中间节点,从而形成树状结构。所有定义的 Feature 创建对应的 DisplayArea:
private void build(@Nullable List<HierarchyBuilder> displayAreaGroupHierarchyBuilders) {
...
final int size = mFeatures.size();
for (int i = 0; i < size; i++) {
final Feature feature = mFeatures.get(i);
PendingArea featureArea = null;
// 【层级遍历】遍历所有窗口层级(0-36),检查当前 Feature 是否适用于每个层级。
for (int layer = 0; layer < maxWindowLayerCount; layer++) {
if (feature.mWindowLayers[layer]) {
if (featureArea == null || featureArea.mParent != areaForLayer[layer]) {
// 参数:1.关联Feature; 2.最小层级; 3.父PendingArea
featureArea = new PendingArea(feature, layer, areaForLayer[layer]);
// 将新创建的 PendingArea 添加到父节点的子节点列表中
areaForLayer[layer].mChildren.add(featureArea);
}
// 更新当前 layer 的插入位置
// 下一个 Feature 遍历时,就会基于这个新的 featureArea 继续构建子节点。
areaForLayer[layer] = featureArea;
} else {
// 【Feature 不适用】当前 Feature 不适用于此层级
// 如果下一层级需要应用此 Feature,将需要创建新的 DisplayArea
// 重置 featureArea 为 null,确保下次创建时不会错误重用
featureArea = null;
}
}
}
...
}
mFeatures 按定义顺序遍历,越早定义的 Feature 越靠近根节点,后定义的 Feature 作为子节点插入。Feature 顺序不是排列问题,而是决定变换执行层级。
featureArea 变量起到了“缓存”作用,用于判断当前层级是否可以延续上一个层级的容器。重用条件:
- 现有 DisplayArea 是为当前 Feature 在前一层级创建的。
- 应用于前一层级的最后一个 Feature 与应用于当前层级的 Feature 相同。
以下情况必须重新创建:
- featureArea 为空(说明是该 Feature 的第一个适用层级)。
- 或者 featureArea 的父节点 != 当前层级原本指向的节点(说明父容器变了,不能单纯延续)。
新建之后需要将新创建的 PendingArea 添加到父节点的子节点列表中。
至此,所有的 Feature 节点(中间节点)都已创建完毕,此时窗口容器树如下:
接下来需要挂载真正的“叶子”容器,即 DisplayArea.Tokens,用于存放 WindowToken:
private void build(@Nullable List<HierarchyBuilder> displayAreaGroupHierarchyBuilders) {
...
PendingArea leafArea = null;
int leafType = LEAF_TYPE_TOKENS; // 默认当前叶子类型为 TOKENS
for (int layer = 0; layer < maxWindowLayerCount; layer++) {
// 【层级类型判断】根据窗口层级确定叶子节点类型
// LEAF_TYPE_TOKENS(0): 默认类型,用于大多数窗口层级
// LEAF_TYPE_TASK_CONTAINERS(1): 应用层级,用于承载 Task 和 Activity
// LEAF_TYPE_IME_CONTAINERS(2): 输入法层级,用于承载输入法窗口
int type = typeOfLayer(policy, layer);
// 叶子节点重用检查:若当前层级与上一层级类型相同且父节点一致(同一 Feature 组),
// 则复用已有 LeafArea,避免为每个层级都创建新的 DisplayArea 对象。
// 以下三种情况需要重新创建:
// 1. leafArea == null:首个层级,无历史节点可复用
// 2. leafArea.mParent != areaForLayer[layer]:父节点不同,分属不同 Feature 组
// 3. type != leafType:层级类型不同(如从系统窗口层切换到应用窗口层)
if (leafArea == null || leafArea.mParent != areaForLayer[layer]
|| type != leafType) {
// Create a new Tokens for this layer.
// 参数:1.叶子节点Feature为null; 2.最小层级; 3.父PendingArea
leafArea = new PendingArea(null /* feature */, layer, areaForLayer[layer]);
// 将新创建的 PendingArea 添加到父节点的子节点列表中
areaForLayer[layer].mChildren.add(leafArea);
leafType = type;// 更新
if (leafType == LEAF_TYPE_TASK_CONTAINERS) {// 如果叶子节点类型是 LEAF_TYPE_TASK_CONTAINERS,也就是 layer 等于 2,则做以下处理:
// We use the passed in TaskDisplayAreas for task container type of layer.
// Skip creating Tokens even if there is no TDA.
// 【深度解析:为什么 TaskDisplayArea 要新建额外的 PendingArea,导致 layer=2 出现多个叶子节点?】
// 1. Android 的设计中,Layer 2 (APPLICATION_LAYER) 是一个非常特殊的层级。
// 普通的 layer (比如 layer 15 的状态栏) 只需要一个单一的 `DisplayArea.Tokens` 容器来装窗口。
// 但是 Layer 2 承担了所有的 App 窗口,它必须支持【分屏模式】和【多窗口模式】。
// 2. 为了支持分屏,WMS 允许开发者在这个层级创建多个 `TaskDisplayArea`(比如左边一个 TDA,右边一个 TDA)。
// 3. 因此,Layer 2 的初始 leafArea (普通的 DisplayArea.Tokens) 被 `mSkipTokens = true` 废弃掉。
// 随后调用 `addTaskDisplayAreasToApplicationLayer`,根据 `mTaskDisplayAreas` 列表的长度(可能是1个主屏,也可能是多个分屏),
// 为每一个 TDA 创建专属的子 `PendingArea` 并挂载。
// 这就是为什么 Layer 2 看起来会新建额外的叶子节点——因为它可以是一对多的关系。
addTaskDisplayAreasToApplicationLayer(areaForLayer[layer]);
// 【功能】添加 DisplayAreaGroup 到应用层(通常为空列表)
// displayAreaGroupHierarchyBuilders是一个空的列表,这个方法就不用管了
addDisplayAreaGroupsToApplicationLayer(areaForLayer[layer],
displayAreaGroupHierarchyBuilders);
// 【优化策略】跳过 Tokens 创建,因为已有专门的 TaskDisplayArea
// 接着将叶子节点 leafArea 的 mSkipTokens 设置为 true,那么后续在根据PendingArea 树生成 DisplayArea 层级结构的时候,就不会为这个 PendingArea 对象生成一个 DisplayArea 对象了。
// 【系统架构】应用层(Layer 2) 是特殊的,它不直接使用 DisplayArea.Tokens,
// 而是使用功能更强大的 TaskDisplayArea。因此这里标记 skipTokens。
leafArea.mSkipTokens = true;
} else if (leafType == LEAF_TYPE_IME_CONTAINERS) {// 如果叶子节点类型是 LEAF_TYPE_IME_CONTAINERS,也就是 layer 等于 13 或者 14
// We use the passed in ImeContainer for ime container type of layer.
// Skip creating Tokens even if there is no ime container.
// 【深度解析:为什么 ImeContainer 不新建额外的叶子节点,而是直接复用当前的 leafArea?】
// 1. 唯一性:不管是在什么复杂的场景下(甚至分屏),一个 Display (屏幕) 上永远【只会有一个】输入法容器 (ImeContainer)。
// 2. 因为它是一对一的关系,所以不需要像 TDA 那样遍历列表去新建多个子 PendingArea。
// 3. 因此,代码直接将提前准备好的 `mImeContainer` 塞进了当前这个 `leafArea` 的 `mExisting` 属性中。
// 并在实例化阶段(instantiateChildren)发现 `mExisting` 有值时,直接返回这个预先创建好的输入法容器,
// 避免了再去 new 一次普通的 `DisplayArea.Tokens`。
leafArea.mExisting = mImeContainer;
leafArea.mSkipTokens = true;
}
}
// 【层级管理】设置叶子节点的最大层级
// 【底层原理】确保每个 DisplayArea 知道自己管理的窗口层级范围
leafArea.mMaxLayer = layer;
}
...
}
首先通过typeOfLayer获取叶子节点类型:
private static int typeOfLayer(WindowManagerPolicy policy, int layer) {
// 层级类型映射,将数字层级 (int layer) 映射到 语义类型 (Type)
// 方便系统对 Application、IME 和 System 窗口进行不同的策略处理
if (layer == APPLICATION_LAYER) {// layer 等于 2,叶子节点类型是 LEAF_TYPE_TASK_CONTAINERS,用于表示应用窗口容器
return LEAF_TYPE_TASK_CONTAINERS;
} else if (layer == policy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD)
|| layer == policy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD_DIALOG)) {// layer 等于 13 14,叶子节点类型是 LEAF_TYPE_IME_CONTAINERS,用于表示输入法窗口
return LEAF_TYPE_IME_CONTAINERS;
} else {// layer 不等于 2,13,14,叶子节点类型是 LEAF_TYPE_TOKENS,用于表示系统窗口
return LEAF_TYPE_TOKENS;
}
}
以下三种情况需要重新创建 leafArea:
- leafArea == null:首个层级,无历史节点可复用
- leafArea.mParent != areaForLayer[layer]:父节点不同,分属不同 Feature 组
- type != leafType:层级类型不同(如从系统窗口层切换到应用窗口层)
同样,创建之后将新创建的 PendingArea 添加到父节点的子节点列表中。
当叶子节点类型对应应用容器时,通过addTaskDisplayAreasToApplicationLayer 新建额外的 PendingArea:
private void addTaskDisplayAreasToApplicationLayer(PendingArea parentPendingArea) {
// 将所有 TaskDisplayArea (TDA) 挂载到应用层节点下
// 此时默认的 mTaskDisplayAreas 中只有一个元素,即名为 DefaultTaskDisplayArea 的 TaskDisplayArea 对象,这里是为该对象创建了一个对应的 PendingArea 对象
final int count = mTaskDisplayAreas.size();
// 并且将创建的 PendingArea 添加到 areaForLayer[2] 节点之下,然后将 PendingArea.mExisting 设置为 DefaultTaskDisplayArea
for (int i = 0; i < count; i++) {
// 【创建应用节点】APPLICATION_LAYER = 2
// 这里创建的 PendingArea 是为了容纳 TDA
PendingArea leafArea =
new PendingArea(null /* feature */, APPLICATION_LAYER, parentPendingArea);
// 【复用现有对象】关键点:这里直接使用了已经存在的 mTaskDisplayAreas 对象
// 而不是让系统再去 new 一个新的 DisplayArea。
// 后续根据 PendingArea 生成 DisplayArea.Tokens 的时候,如果 mExisting 不为空,那么直接用 mExisting,而不会再重新创建一个 DisplayArea.Tokens 对象。
leafArea.mExisting = mTaskDisplayAreas.get(i);
leafArea.mMaxLayer = APPLICATION_LAYER;
parentPendingArea.mChildren.add(leafArea);
}
}
将原有的叶子节点 leafArea 的 mSkipTokens 设置为 true,那么后续在根据 PendingArea 树生成 DisplayArea 层级结构的时候,就不会为这个 PendingArea 对象生成一个 DisplayArea 对象了。
当叶子节点类型对应 IME 容器时,将 leafArea.mExisting 直接设置为 mImeContainer。
此时,叶子节点挂载完毕,窗口容器树如下:
private void build(@Nullable List<HierarchyBuilder> displayAreaGroupHierarchyBuilders) {
...
// 计算非叶子节点的 mMaxLayer
// 通过递归计算确保每个 DisplayArea 知道自己管理的层级范围。
root.computeMaxLayer();
// We built a tree of PendingAreas above with all the necessary info to represent the
// hierarchy, now create and attach real DisplayAreas to the root.
// 【第四阶段:实例化 DisplayArea 树】基于 PendingArea 树生成真正的 DisplayArea 对象
// 【底层原理】将临时的 PendingArea 结构转换为实际的窗口容器层级
// 【面试高频考点】问:PendingArea 和 DisplayArea 的区别是什么?为什么需要两阶段构建?
// 答:PendingArea 是构建时的逻辑中间节点,DisplayArea 是运行时的物理节点。
// 两阶段构建解耦了层级计算逻辑和对象创建逻辑,使得复杂的层级重用和 Feature 插入算法更容易实现。
// 逻辑树推演好以后,就会调用下面的方法,基于 PendingArea 树生成窗口容器树 DisplayAreas
root.instantiateChildren(mRoot, displayAreaForLayer, 0, featureAreas);
// Notify the root that we have finished attaching all the DisplayAreas. Cache all the
// feature related collections there for fast access.
// 【完成构建】通知根节点完成所有 DisplayArea 的附加,缓存特性相关集合以便快速访问
// 【底层原理】构建完成后需要建立快速查找索引,提高运行时性能
// 系统需要频繁查询 Feature 对应的 DisplayArea(例如开启单手模式时),缓存这些映射至关重要。
mRoot.onHierarchyBuilt(mFeatures, displayAreaForLayer, featureAreas);
...
}
在此之前,非叶子节点的 mMaxLayer 都是 0,这里调用 computerMaxLayer 方法计算非叶子节点的 mMaxLayer:
int computeMaxLayer() {
for (int i = 0; i < mChildren.size(); i++) {
mMaxLayer = Math.max(mMaxLayer, mChildren.get(i).computeMaxLayer());
}
return mMaxLayer;
}
计算之后,窗口容器树如下:
接着通过 instantiateChildren,基于 PendingArea 树生成真正的 DisplayArea 树。
2.5 基于 PendingArea 树,生成窗口容器树
void instantiateChildren(DisplayArea<DisplayArea> parent, DisplayArea.Tokens[] areaForLayer,
int level, Map<Feature, List<DisplayArea<WindowContainer>>> areas) {
// 1. 子区域按照它们的最小层级进行升序排列
mChildren.sort(Comparator.comparingInt(pendingArea -> pendingArea.mMinLayer));
// 2. 遍历孩子将 PendingArea 转换成 DisplayArea
for (int i = 0; i < mChildren.size(); i++) {
final PendingArea child = mChildren.get(i);
// 将该节点转成对应的 DisplayArea
final DisplayArea area = child.createArea(parent, areaForLayer);
if (area == null) {
// TaskDisplayArea and ImeContainer can be set at different hierarchy, so it can
// be null.
continue;
}
// 将返回的 area 设置为孩子,第一次执行的时候 root 就是 DisplayContent
parent.addChild(area, WindowContainer.POSITION_TOP);
if (child.mFeature != null) {
// 让 Feature 对应的容器里添加创建的 DisplayArea
areas.get(child.mFeature).add(area);
}
// 开始迭代构建
child.instantiateChildren(area, areaForLayer, level + 1, areas);
}
}
遍历每一个孩子节点,调用其 PendingArea 的 createArea 方法,创建相应的 DisplayArea 对象:
private DisplayArea createArea(DisplayArea<DisplayArea> parent,
DisplayArea.Tokens[] areaForLayer) {
if (mExisting != null) {
// asTokens,方法定义在 DisplayArea 中默认返回 null,只有 DisplayArea.Tokens 返回本身。 而 ImeContainer 是继承 DisplayArea.Tokens 的,所以有返回值。
if (mExisting.asTokens() != null) {
// Store the WindowToken container for layers
// 只有输入法满足
fillAreaForLayers(mExisting.asTokens(), areaForLayer);
}
return mExisting;
}
// mSkipTokens 为 true,直接 return,mSkipTokens 是和 mExisting 是同一个逻辑下设置的。
if (mSkipTokens) {
return null;
}
// 定义 DisplayArea 的类型
DisplayArea.Type type;
if (mMinLayer > APPLICATION_LAYER) {
type = DisplayArea.Type.ABOVE_TASKS;
} else if (mMaxLayer < APPLICATION_LAYER) {
type = DisplayArea.Type.BELOW_TASKS;
} else {
type = DisplayArea.Type.ANY;
}
// DisplayArea 的类型
if (mFeature == null) {// leaf 的 mFeature null
// 构建返回的 Leaf
final DisplayArea.Tokens leaf = new DisplayArea.Tokens(parent.mWmService, type,
"Leaf:" + mMinLayer + ":" + mMaxLayer);
fillAreaForLayers(leaf, areaForLayer);// 给对应覆盖的层级都需要赋值
return leaf;
} else {// 创建 DisplayArea 对象。如果 mFeature不为 null,那么创建一个 DisplayArea 对象,注意这里构造函数 name 参数传入的是 mFeature.mName + ":" + mMinLayer + ":" + mMaxLayer,包含了 Feature 名,纵向层级范围信息。
return mFeature.mNewDisplayAreaSupplier.create(parent.mWmService, type,
mFeature.mName + ":" + mMinLayer + ":" + mMaxLayer, mFeature.mId);
}
}
当 mExisting 不为 null,前面分析的 TaskDisplayArea 和 ImeContainer 的情况,此时不需要再创建 DisplayArea 对象,直接用 mExisting。
根据窗口层级范围确定 DisplayArea 的类型:
- ABOVE_TASKS: 层级 > APPLICATION_LAYER(2),包含系统窗口(状态栏、导航栏、输入法等)这些窗口在 Z 轴上位于应用窗口之上。
- BELOW_TASKS: 层级 < APPLICATION_LAYER(2),包含壁纸等底层窗口这些窗口在 Z 轴上位于应用窗口之下。
- ANY: 跨越 APPLICATION_LAYER(2),可以包含任意类型的窗口Feature 节点通常属于此类型,因为它们覆盖多个层级范围。
最终生成的窗口容器树如下: