在Android系统中,一个显示页面有多个窗口组成,一个窗口相当于一个图层,不同的图层叠加后显示在屏幕这个画布上。不同的图层有不同高度,然后合成在一起成为最终的画面。
树形层次结构的规则:Configuration、Visible、WindowMode等都是从上往下设置更新
窗口容器
配置容器类图
ConfigurationContainer
ConfigurationContainer是WMS设计的配置容器类,一个配置容器类可以容纳其他的配置容器(像View一样),它是抽象类,只定义容纳的关系并没有实现子容器的保存形式。
它是一个泛型类只能保存对应泛型的子容器。每一个配置容器都可以在其父容器的基础上附加一些新的配置变化,并应用到它的子容器上。
- mRequestedOverrideConfiguration,Container需要更新自己的Configuration时,请求设置的Configuration;
- mResolvedOverrideConfiguration,可以通过resolveOverrideConfiguration()重新设置mResolvedOverrideConfiguration为自定义的Config,然后将自定义的Config和mRequestedOverrideConfiguration合并得到mFullConfiguration。
- mFullConfiguration,最终真实的Configuration
- mMergedOverrideConfiguration, Configuration是可继承的,自顶层Container一次传递给子Container。
public abstract class ConfigurationContainer<E extends ConfigurationContainer> {
private Configuration mRequestedOverrideConfiguration = new Configuration();
private Configuration mResolvedOverrideConfiguration = new Configuration();
private Configuration mFullConfiguration = new Configuration();
public Configuration getConfiguration() { return mFullConfiguration;}
public void onConfigurationChanged(Configuration newParentConfig) {...}
abstract protected int getChildCount();
abstract protected E getChildAt(int index);
abstract protected ConfigurationContainer getParent();
}
核心窗口容器
WindowContainer
WindowContainer是WMS里设计的窗口容器顶级类,它可以容纳其他的窗口容器,继承自配置容器类ConfigurationContainer,因此每一个窗口容器都可以向它管理的窗口附加一份新的配置主要为WindowConfiguration相关的配置。
窗口容器将它的子容器保存在一个列表中,并实现了添加、删除子容器,以及遍历不同类型子容器的方法。即所有的窗口容器类都是WindowContainer的子类,Window Container定义了这些节点通用的成员变量和成员方法。
其中有两个重要的成员变量
- mParent,类型也是Window Container,保存的是当前节点的父节点
- mChildren,保存的是当前节点的所有孩子节点,是有顺序的,最顶层的子容器位于队尾。
//
class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<E>
implements Comparable<WindowContainer>, Animatable, SurfaceFreezer.Freezable,
InsetsControlTarget {
private WindowContainer<WindowContainer> mParent = null;
protected final WindowList<E> mChildren = new WindowList<E>();
private int mOverrideOrientation = SCREEN_ORIENTATION_UNSET;
protected WindowContainer mLastOrientationSource;
protected DisplayContent mDisplayContent;
protected SurfaceControl mSurfaceControl;
WindowContainer(WindowManagerService wms) {
mWmService = wms;
mTransitionController = mWmService.mAtmService.getTransitionController();
mSyncTransaction = wms.mTransactionFactory.get();
mSurfaceAnimator = new SurfaceAnimator(this, this::onAnimationFinished, wms);
mSurfaceFreezer = new SurfaceFreezer(this, wms);
}
}
RootWindowContainer
RootWindowContainer是WMS管理所有窗口容器的根容器,系统中只存在一个。RootWindowContainer管理的是DisplayContent,每一个DisplayContent对应一个Display屏幕,包括物理屏幕和虚拟屏幕。
RootWindowContainer同时实现了DisplayListener的接口,可以监听屏幕的添加和删除操作。
RootWindowContainer由WMS服务创建,但是会等待ATMS服务启动完成后再初始化。
//com.android.server.wm.WindowManagerService#WindowManagerService
public class WindowManagerService {
RootWindowContaienr mRoot;
public WindowManagerService(...){
//在WMS构造函数中创建,
mRoot = new RootWindowContainer(this);
}
}
// com.android.server.wm.ActivityTaskManagerService#setWindowManager
public void setWindowManager(WindowManagerService wm) {
synchronized (mGlobalLock) {
mWindowManager = wm;
mRootWindowContainer = wm.mRoot;
mWindowOrganizerController.setWindowManager(wm);
mTempConfig.setToDefaults();
mTempConfig.setLocales(LocaleList.getDefault());
mConfigurationSeq = mTempConfig.seq = 1;
//先给RootWindowContainer设置一个默认的Configuration
mRootWindowContainer.onConfigurationChanged(mTempConfig);
mRootWindowContainer.setWindowManager(wm);
}
}
// RootWindowContainer 是一个DisplayContent容器,并且会监听Display的增删
class RootWindowContainer extends WindowContainer<DisplayContent>
implements DisplayManager.DisplayListener {
//在WMS初始化后遍历当前已经创建的Display,创建对应的DisplayContent,并addChild
void setWindowManager(WindowManagerService wm) {
final Display[] displays = mDisplayManager.getDisplays();
for (int displayNdx = 0; displayNdx < displays.length; ++displayNdx) {
final Display display = displays[displayNdx];
final DisplayContent displayContent = new DisplayContent(display, this);
addChild(displayContent, POSITION_BOTTOM);
if (displayContent.mDisplayId == DEFAULT_DISPLAY) {
mDefaultDisplay = displayContent;
}
}
}
}
DisplayContent
DisplayContent继承自RootDisplayArea,RootDisplayArea是屏幕最顶层的DisplayArea,它既可以是一个DisplayContent占据整个屏幕,也可以将一个屏幕分成几个区域,每一个区域用一个DisplayAreaGroup表示。
DisplayContent是一个占据整个屏幕的根窗口容器,它只能容纳几个DisplayArea类型的容器。
class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.DisplayContentInfo {
DisplayAreaPolicy mDisplayAreaPolicy; //是DisplayArea的策略管理器
DisplayContent(){
//配置DisplayContent内部的Surface的层级结构
configureSurfaces(pendingTransaction);
}
}
class RootDisplayArea extends DisplayArea.Dimmable {
List<DisplayAreaPolicyBuilder.Feature> mFeatures;
}
//可以设置应用效果的DisplayArea,它只能容纳DisplayArea
static class Dimmable extends DisplayArea<DisplayArea> {
}
DisplayContent内部的容器是通过DisplayAreaPolicy进行管理,在DisplayContent的构造函数中会配置Surface的层级结构,即创建DisplayArea的策略管理器,并初始化DisplayArea的层级。
private void configureSurfaces(Transaction transaction) {
final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(mSession)
.setOpaque(true)
.setContainerLayer()
.setCallsite("DisplayContent");
mSurfaceControl = b.setName(getName()).setContainerLayer().build();
if (mDisplayAreaPolicy == null) {
// 设置策略管理器,并构建 DisplayArea的层级结构
// Setup the policy and build the display area hierarchy.
// Build the hierarchy only after creating the surface so it is reparented correctly
mDisplayAreaPolicy = mWmService.getDisplayAreaPolicyProvider().instantiate(
mWmService, this /* content */, this /* root */,
mImeWindowsContainer);
}
.......
transaction
.setLayer(mSurfaceControl, 0)
.setLayerStack(mSurfaceControl, mDisplayId)
.show(mSurfaceControl)
.setLayer(mOverlayLayer, Integer.MAX_VALUE)
.show(mOverlayLayer);
}
DisplayArea
DisplayArea代表了屏幕上的一块区域,会根据窗口层级区域,存放不同类型的窗口。
DisplayArea中有一个成员变量mName,它由三部分组成name + ":" + mMinLayer + ":" + mMaxLayer,
name, 用于指定Display Area的功能(Feature)mMinLayer、mMaxLayer,指定当前DisplayArea的层级高度范围,WMS将Z轴上的空间划分了0~36层级,值越大代表层级越高。
应用的Activity窗口一般位于Task中,根据相对于Task层级的位置,DisplayArea主要有3大类:
- ABOVE_TASKS,只能存放层级高于Task层级的窗口,例如系统弹窗、水印、系统装饰等;
- BELOW_TASKS,只能存放层级低于Task层级的窗口,例如壁纸;
- ANY,可以存放任意层级的窗口。
从泛型类的定义中可以看到,它可以容纳其他任何类型的窗口容器。
public class DisplayArea<T extends WindowContainer> extends WindowContainer<T> {
private final String mName;
}
TaskDisplayArea
是系统中所有Task的父节点,用于管理Task。
WindowState
WindowState和客户端窗口一一对应,向WMS添加一个窗口时,系统会为其创建一个WindowState来表示窗口的所有属性。WindowState用于管理窗口的属性,在是Window层级结构最底层的容器,即位于树的最底层。
窗口画面显示相关的属性由WindowStateAnimator管理,WindowStateAnimator管理Window相关的Surface显示逻辑,例如mDrawState更新。
WindowToken
WindowToken保存了一组具有同一token的WindowState,将属于同一个Activity的Window组织在一起。Activity在更新窗口时必现向WMS提供WindowToken,并且窗口的类型和所持有的WindowToken的类型必须保持一致。
一个WindowToken对应多个WindowState,WindowToken是一个用于表示窗口层次结构中的窗口的标识符。每个Window具有一个与之关联的WindowToken,用于帮助WMS管理窗口的显示和交互。
一共有三种类型的WindowToken实体,即WindowToken,ActivityRecord,WallpaperWindowToken分别用于管理不同类型的WindowState.
暂时无法在飞书文档外展示此内容
1. WindowToken(基类)
通用系统窗口token,直接通过WindowToken.Builder创建。
创建场景:
- addWindow时隐式创建:应用添加窗口时如果没有已存在的token,WMS自动创建。适用于Toast、SystemAlert、ApplicationOverlay、StatusBar、NavigationBar等
- addWindowTokenWithOptions预注册:SystemUI等系统服务提前注册token(
persistOnEmpty=true),后续窗口直接挂载
挂载位置:按type映射到对应layer的DisplayArea.Tokens
2. ActivityRecord
应用Activity窗口token。
创建场景:由ATMS在启动Activity时创建,不通过addWindow创建
挂载位置:TaskDisplayArea → Task → ActivityRecord
承载窗口:TYPE_BASE_APPLICATION、TYPE_APPLICATION、TYPE_APPLICATION_STARTING(启动窗口)等应用窗口
addWindow中通过token.asActivityRecord()做类型校验,应用窗口必须找到对应的ActivityRecord token
3. WallpaperWindowToken
壁纸窗口token。
创建场景:通过addWindowTokenWithOptions创建,type为TYPE_WALLPAPER时专门走此分支
挂载位置:DisplayArea.Tokens [layer=1](最底层)
特有属性:壁纸偏移量(mWallpaperX/Y)、裁剪区域(mCropHints)等。有独立的可见性控制和动画逻辑
4. ImeWindowToken
输入法窗口token。
创建场景:通过addImeWindowToken创建,type为TYPE_INPUT_METHOD
挂载位置:ImeContainer(独立的输入法容器,layer=13-14)
特有属性:mTargetUserId(目标用户ID),支持多用户场景下的输入法隔离。重写setClientVisible()同时设置visibleRequested,影响子窗口可见性判断
创建路径总结
| Token类型 | 创建入口 | 窗口类型 | 挂载位置 |
|---|---|---|---|
| ActivityRecord | ATMS启动Activity | TYPE_APPLICATION系列 | TaskDisplayArea → Task |
| WallpaperWindowToken | addWindowTokenWithOptions | TYPE_WALLPAPER | DisplayArea.Tokens [layer=1] |
| ImeWindowToken | addImeWindowToken | TYPE_INPUT_METHOD | ImeContainer |
| WindowToken(基类) | addWindow隐式创建 / addWindowTokenWithOptions预注册 | 其他所有系统窗口 | 按type映射到对应layer的DisplayArea.Tokens |
窗口层级
DefaultProvider#instantiate
- DefaultTaskDisplayArea,配置DefaultTaskDisplayArea Feature
- configureTrustedHierarchyBuilder,配置其他Feature
- new DisplayAreaPolicyBuilder().setRootHierarchy(rootHierarchy).build(wmService); 层级构建算法。
窗口类型与层级
DisplayArea层级的构建主要依赖DisplayAreaPolicyBuilder.Feature,每一个Feature为应用在某一类窗口上的功能特性。getWindowLayerFromTypeLw方法将每种类型的窗口做了层级划分,一共划分了36个层级。
| 窗口Type | 窗口类型 | 层级 | 备注 |
|---|---|---|---|
| 2013 | TYPE_WALLPAPER | 1 | 壁纸窗口在最底层 |
| 1~99 | APPLICATION_WINDOW | 2 | 应用窗口 |
| TYPE_PRESENTATION | 3 | ||
| TYPE_PRIVATE_PRESENTATION | 3 | ||
| TYPE_DOCK_DIVIDER | 3 | ||
| TYPE_QS_DIALOG | 3 | ||
| TYPE_PHONE | 3 | ||
| TYPE_SEARCH_BAR | 4 | ||
| TYPE_INPUT_CONSUMER | 5 | ||
| TYPE_SYSTEM_DIALOG | 6 | ||
| 2005 | TYPE_TOAST | 7 | Toast窗口 |
| TYPE_PRIORITY_PHONE | 8 | ||
| TYPE_SYSTEM_ALERT | |||
| TYPE_APPLICATION_OVERLAY | 11 | ||
| 2011 | TYPE_INPUT_METHOD | 13 | 输入法窗口 |
| TYPE_INPUT_METHOD_DIALOG | 14 | ||
| 2000 | TYPE_STATUS_BAR | 15 | 状态栏窗口 |
| TYPE_STATUS_BAR_ADDITIONAL | 16 | ||
| 2040 | TYPE_NOTIFICATION_SHADE | 17 | 锁屏/通知窗口 |
| TYPE_STATUS_BAR_SUB_PANEL | 18 | ||
| TYPE_KEYGUARD_DIALOG | 19 | ||
| TYPE_VOICE_INTERACTION_STARTING | 20 | ||
| TYPE_VOICE_INTERACTION | 21 | ||
| TYPE_VOLUME_OVERLAY | 22 | ||
| TYPE_SYSTEM_OVERLAY | |||
| 2019 | TYPE_NAVIGATION_BAR | 24 | 导航栏窗口 |
| TYPE_NAVIGATION_BAR_PANEL | 25 | ||
| TYPE_SCREENSHOT | 26 | ||
| TYPE_SYSTEM_ERROR | |||
| TYPE_MAGNIFICATION_OVERLAY | 28 | ||
| TYPE_DISPLAY_OVERLAY | 29 | ||
| TYPE_DRAG | 30 | ||
| TYPE_ACCESSIBILITY_OVERLAY | 31 | ||
| TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY | 32 | ||
| TYPE_SECURE_SYSTEM_OVERLAY | 33 | ||
| TYPE_BOOT_PROGRESS | 34 | ||
| TYPE_POINTER | 35 |
type到layer的转换使用getWindowLayerFromTypeLw(),和运行时窗口添加时用的是同一个映射函数。这保证了构建时和运行时的layer编号一致性。
mAreaForLayer是RootDisplayArea中的核心数据结构,类型为DisplayArea.Tokens[],索引是layer编号,值是该layer对应的叶子节点容器。它在DisplayAreaPolicyBuilder.HierarchyBuilder.build()中通过三个阶段生成。
各Feature的作用
1. WindowedMagnification(窗口化放大)
- Feature ID:
FEATURE_TOP_LEVEL_ZOOM - 作用: 支持无障碍窗口化放大镜功能。用户开启辅助功能中的"放大"后,屏幕上出现一个可拖动的放大镜窗口,该窗口内显示放大后的屏幕内容。这个Feature创建的DisplayArea就是被放大镜捕获和渲染的内容范围
- 层级范围:
upTo(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY).except(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY)— 包含从最底层到layer=32之间的所有窗口,但排除放大覆盖层本身(否则会递归放大) - 特殊处理: 使用
DisplayArea.Dimmable创建,使放大镜窗口也能镜像dim层。位于层级树最顶层,是几乎所有窗口的共同祖先
2. HideDisplayCutout(隐藏刘海屏)
- Feature ID:
FEATURE_HIDE_DISPLAY_CUTOUT - 作用: 支持隐藏屏幕刘海/挖孔区域。开启后,通过
HideDisplayCutoutOrganizer对该DisplayArea做偏移和裁剪,将窗口内容区域缩小到不包含刘海的安全区域内 - 层级范围:
all().except(TYPE_NAVIGATION_BAR, TYPE_NAVIGATION_BAR_PANEL, TYPE_STATUS_BAR, TYPE_NOTIFICATION_SHADE)— 排除状态栏、通知面板、导航栏(这些窗口需要覆盖刘海区域) - 前提条件: 仅在
ro.support_hide_display_cutout为true且是默认屏幕时才创建
3. OneHanded(单手模式)
- Feature ID:
FEATURE_ONE_HANDED - 作用: 支持单手操作模式。激活后,通过
OneHandedController将该DisplayArea内的所有内容缩小并下移到屏幕下半部分,方便用户单手操作大屏手机 - 层级范围:
all().except(TYPE_NAVIGATION_BAR, TYPE_NAVIGATION_BAR_PANEL, TYPE_SECURE_SYSTEM_OVERLAY)— 排除导航栏和安全系统覆盖层(需保持原位) - 仅默认屏幕创建
4. AppZoomOut(应用缩放)
- Feature ID:
FEATURE_APP_ZOOM_OUT - 作用: 支持应用内容缩放/推回(pushback)效果。通过
AppZoomOutDisplayAreaOrganizer对DisplayArea做缩放变换,在特定场景(如返回桌面的手势过渡)将应用内容略微缩小,产生视觉上的深度感 - 层级范围:
upTo(TYPE_VOLUME_OVERLAY).except(TYPE_NAVIGATION_BAR, TYPE_NAVIGATION_BAR_PANEL, TYPE_STATUS_BAR, TYPE_STATUS_BAR_SUB_PANEL, TYPE_NOTIFICATION_SHADE, TYPE_KEYGUARD_DIALOG, TYPE_WALLPAPER)— 排除状态栏、通知面板、锁屏、壁纸等不应跟随缩放的窗口 - 仅默认屏幕创建
5. FullscreenMagnification(全屏放大)
- Feature ID:
FEATURE_FULLSCREEN_MAGNIFICATION - 作用: 支持无障碍全屏放大功能。与WindowedMagnification(窗口化放大镜)不同,这个是对整个屏幕内容做放大。通过对该DisplayArea应用缩放变换,实现三击屏幕后全屏放大的效果
- 层级范围:
all().except(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, TYPE_INPUT_METHOD, TYPE_INPUT_METHOD_DIALOG, TYPE_MAGNIFICATION_OVERLAY, TYPE_NAVIGATION_BAR, TYPE_NAVIGATION_BAR_PANEL)— 排除放大覆盖层自身、输入法、导航栏(输入法和导航栏不跟随放大,否则无法正常输入和导航) - 前提条件: 仅在
USE_DISPLAY_AREA_FOR_FULLSCREEN_MAGNIFICATION为true时创建
6. ImePlaceholder(输入法占位)
- Feature ID:
FEATURE_IME_PLACEHOLDER - 作用: 作为输入法容器(ImeContainer)的占位节点。ImeContainer需要在不同的RootDisplayArea之间迁移 — 当输入法目标(获得焦点的EditText)在不同的RootDisplayArea时,ImeContainer会被reparent到目标所在的RootDisplayArea的ImePlaceholder下,确保输入法显示在正确的位置和层级
- 层级范围:
and(TYPE_INPUT_METHOD, TYPE_INPUT_METHOD_DIALOG)— 仅包含输入法相关的两个layer RootDisplayArea.placeImeContainer()利用此Feature找到目标RootDisplayArea中的占位DA,然后将ImeContainer迁移过去
Feature总结对比
| Feature | 作用 | 控制方 | 排除的关键窗口 |
|---|---|---|---|
| WindowedMagnification | 无障碍窗口化放大镜的捕获范围 | AccessibilityService | 放大覆盖层本身 |
| HideDisplayCutout | 隐藏刘海区域,缩小内容区域 | HideDisplayCutoutOrganizer | 状态栏、通知面板、导航栏 |
| OneHanded | 单手模式,内容下移缩小 | OneHandedController | 导航栏、安全覆盖层 |
| AppZoomOut | 应用缩放/推回视觉效果 | AppZoomOutDisplayAreaOrganizer | 状态栏、通知面板、壁纸、锁屏 |
| FullscreenMagnification | 无障碍全屏放大 | AccessibilityService | 输入法、导航栏、放大覆盖层 |
| ImePlaceholder | 输入法容器的迁移占位 | RootDisplayArea.placeImeContainer() | — |
Feature与层级
每一个Feature里保存了一个长度为36的整形数组,对应36个层级,如果这个Feature可以应用到某个层级的窗口上,对应数组里的值为1,否则为0。在configureTrustedHierarchyBuilder方法中为构建DisplayArea层级是添加Feature的主要实现。主要添加了以下Feature
- WindowedMagnification,可以放大这个屏幕,类似于放大镜效果,可以应用到0-31层级的DisplayArea。
- HideDisplayCutout(仅主屏),受刘海或者挖孔区域影响的窗口,可以应用到0-16层级的DisplayArea,因此会创建HideDisplayCutout 0:16挂到WindowedMagnification0:31下。
- OneHanded,可以适应单手模式的窗口,
- FullscreenMagnification,可以全屏放大的窗口,
- ImePlaceholder,输入法窗口容器,只能应用到15:16层级。
Feature的数据结构
- mName,当前Feature的名字,例如WindowedMagnification、HideDisplayCutout、OneHanded
- mId,每个Feature对应的ID,
- mWindowLayers,一个长度36的boolean数组,true意味着可以容纳对应类型的窗口。
static class Feature {
private final String mName;
private final int mId;
private final boolean[] mWindowLayers;
private final NewDisplayAreaSupplier mNewDisplayAreaSupplier;
}
PendingArea的数据结构
PendingArea是层级结构树的节点,
- mParent和mChildren分别记录父节点和孩子节点
- mFeature对应的当前节点支持的Feature、或者说当前节点支持的窗口类型;
- mMinLayer和mMaxLayer分别代表当前节点支持的最大Layer层级和最小Layer层级。
static class PendingArea {
final int mMinLayer;
final ArrayList<PendingArea> mChildren = new ArrayList<>();
final Feature mFeature;
final PendingArea mParent;
int mMaxLayer;
}
窗口层级结构初始化
DefaultProvider#instantiate
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<TaskDisplayArea> tdaList = new ArrayList<>();
tdaList.add(defaultTaskDisplayArea);
//定义逻辑屏下根层级的Feature,策略会基于此构建层级树
final HierarchyBuilder rootHierarchy = new HierarchyBuilder(root);
// Set the essential containers (even if the display doesn't support IME).
rootHierarchy.setImeContainer(imeContainer).setTaskDisplayAreas(tdaList);
if (content.isTrusted()) {
//配置构建层级树
configureTrustedHierarchyBuilder(rootHierarchy, wmService, content);
}
//根据上述的配置策略实例化层级树,这里会创建所有要显示的区域并添加到根节点
return new DisplayAreaPolicyBuilder().setRootHierarchy(rootHierarchy).build(wmService) ;
}
configureTrustedHierarchyBuilder
- WindowedMagnification中upTo并且except
TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY,并且其对应的层级是32,则WindowedMagnification这个Feature容纳的是0-31层级的窗口。 - HideDisplayCutout主要是排除了
TYPE_NAVIGATION_BAR(24), TYPE_NAVIGATION_BAR_PANEL(25), TYPE_STATUS_BAR(15),TYPE_NOTIFICATION_SHADE(17),所以是0-14,16,18-23,26-35 - OneHanded主要是排除了
TYPE_NAVIGATION_BAR(24), TYPE_NAVIGATION_BAR_PANEL(25),TYPE_SECURE_SYSTEM_OVERLAY(33),所以是0-23,26-32,34-35 - FullscreenMagnification主要是排除了
TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY(32), TYPE_INPUT_METHOD(13),TYPE_INPUT_METHOD_DIALOG(14), TYPE_MAGNIFICATION_OVERLAY(28), TYPE_NAVIGATION_BAR(24), TYPE_NAVIGATION_BAR_PANEL(25),所以是0-13,15-23,26-27,29-31,33-35。 - ImePlaceholder主要是与上了
TYPE_INPUT_METHOD, TYPE_INPUT_METHOD_DIALOG,即只管理13,14的层级的容器。
private void configureTrustedHierarchyBuilder(HierarchyBuilder rootHierarchy,
WindowManagerService wmService, DisplayContent content) {
//WindowedMagnification 窗口放大功能在顶部
rootHierarchy.addFeature(new Feature.Builder(wmService.mPolicy, "WindowedMagnification",
FEATURE_WINDOWED_MAGNIFICATION)
.upTo(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY)//到32
.except(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY)//不包含32
// Make the DA dimmable so that the magnify window also mirrors the dim layer.
.setNewDisplayAreaSupplier(DisplayArea.Dimmable::new)
.build());
if (content.isDefaultDisplay) {
//只有默认Display有CUTOUT
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());
}
实例化孩子节点
这里主要是深度遍历构建树
void instantiateChildren(DisplayArea<DisplayArea> parent, DisplayArea.Tokens[] areaForLayer,
int level, Map<Feature, List<DisplayArea<WindowContainer>>> areas) {
//首先把孩子节点的mMinLayer进行排序
mChildren.sort(Comparator.comparingInt(pendingArea -> pendingArea.mMinLayer));
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;
}
//将创建好的DisplayArea添加到父节点的TOP
parent.addChild(area, WindowContainer.POSITION_TOP);
if (child.mFeature != null) {
areas.get(child.mFeature).add(area);
}
//深度遍历创建孩子节点
child.instantiateChildren(area, areaForLayer, level + 1, areas);
}
}
模拟构建过程
- WindowedMagnification中upTo并且except
TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY,并且其对应的层级是32,则WindowedMagnification这个Feature容纳的是0-31层级的窗口。 - HideDisplayCutout主要是排除了
TYPE_NAVIGATION_BAR(24), TYPE_NAVIGATION_BAR_PANEL(25), TYPE_STATUS_BAR(15),TYPE_NOTIFICATION_SHADE(17),所以是0-14,16,18-23,26-35 - OneHanded主要是排除了
TYPE_NAVIGATION_BAR(24), TYPE_NAVIGATION_BAR_PANEL(25),TYPE_SECURE_SYSTEM_OVERLAY(33),所以是0-23,26-32,34-35 - FullscreenMagnification主要是排除了
TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY(32), TYPE_INPUT_METHOD(13),TYPE_INPUT_METHOD_DIALOG(14), TYPE_MAGNIFICATION_OVERLAY(28), TYPE_NAVIGATION_BAR(24), TYPE_NAVIGATION_BAR_PANEL(25),所以是0-13,15-23,26-27,29-31,33-35。 - ImePlaceholder主要是与上了
TYPE_INPUT_METHOD, TYPE_INPUT_METHOD_DIALOG,即只管理13,14的层级的容器。