SwallowJoe的博客

Be a real go-getter,
NEVER SETTLE!

0%

WMS(2)-WMS中RootDisplayArea的创建

以下分析基于Android S.

简述

在我们开始分析WMS是怎么管理不同应用的Window的时,有点一头雾水,不知如何下手。那么我们还是从单个Activity的Window被添加至WMS中来入手。

从上文中我们知道PhoneWindow是通过IWindowSession被添加的,在ViewRoontImpl中调用了addToDisplayAsUser:

1
2
3
4
res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), userId,
mInsetsController.getRequestedVisibility(), inputChannel, mTempInsets,
mTempControls);

这里的mWindow是ViewRootImpl.W类的对象,用于WMS和该Window通信。

一. Activity的Window被添加至WMS

mWindowSession是ViewRootImpl被创建时通过IWindowManager在WMS中创建并返回的。这个我们回顾下:

2-1

1.1 Session.addToDisplayAsUser

1
2
3
4
5
6
7
public int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs,
int viewVisibility, int displayId, int userId, InsetsState requestedVisibility,
InputChannel outInputChannel, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls) {
return mService.addWindow(this, window, attrs, viewVisibility, displayId, userId,
requestedVisibility, outInputChannel, outInsetsState, outActiveControls);
}

这里的mService自然就是WMS本身了。

1.2 WMS.addWindow

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,
int displayId, int requestUserId, InsetsState requestedVisibility,
InputChannel outInputChannel, InsetsState outInsetsState,
InsetsSourceControl[] outActiveControls) {
......
// 窗口类型保存在LayoutParams中
final int type = attrs.type;
synchronized (mGlobalLock) {
......
// 1.2.1 获取该token对应的DisplayContent
final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token);
......
ActivityRecord activity = null;
final boolean hasParent = parentWindow != null;
// 当该Activity没有parent时,此时获取的token还是null
WindowToken token = displayContent.getWindowToken(
hasParent ? parentWindow.mAttrs.token : attrs.token);
......
if (token == null) {
if (hasParent) {
......
} else {
// 1.2.2 一般走这里创建WindowToken
final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
token = new WindowToken(this, binder, type, false, displayContent,
session.mCanAddInternalSystemWindow, isRoundedCornerOverlay);
}
}
...
// 初始化WindowState
final WindowState win = new WindowState(this, session, client, token, parentWindow,
appOp[0], attrs, viewVisibility, session.mUid, userId,
session.mCanAddInternalSystemWindow);
......
// 创建SurfaceSession, 用来和SurfaceFlinger通信
win.attach();
// 保存WindowState
mWindowMap.put(client.asBinder(), win);
......
win.mToken.addWindow(win);
......
......
}

1.2.1 WMS.getDisplayContentOrCreate

1
2
3
4
5
6
7
8
9
10
11
private DisplayContent getDisplayContentOrCreate(int displayId, IBinder token) {
if (token != null) {
// 遍历所有的DisplayContent找到保存该token的WindowToken
final WindowToken wToken = mRoot.getWindowToken(token);
if (wToken != null) {
return wToken.getDisplayContent();
}
}

return mRoot.getDisplayContentOrCreate(displayId);
}

这里首先遍历所有的DisplayContent尝试找到保存该token的WindowToken,自然是没有的,我们是第一次添加。

mRoot是WMS服务启动时创建的RootWindowContainer对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
DisplayContent getDisplayContentOrCreate(int displayId) {
// 是否已被创建,假设已创建
DisplayContent displayContent = getDisplayContent(displayId);
if (displayContent != null) {
return displayContent;
}
if (mDisplayManager == null) {
// The system isn't fully initialized yet.
return null;
}
final Display display = mDisplayManager.getDisplay(displayId);
if (display == null) {
// The display is not registered in DisplayManager.
return null;
}
// 1.2.1.1 创建DisplayContent
displayContent = new DisplayContent(display, this);
addChild(displayContent, POSITION_BOTTOM);
return displayContent;
}

DisplayContent getDisplayContent(int displayId) {
for (int i = getChildCount() - 1; i >= 0; --i) {
final DisplayContent displayContent = getChildAt(i);
if (displayContent.mDisplayId == displayId) {
return displayContent;
}
}
return null;
}

这里为了便于后续分析,先看看相关的类图:

2-2

从类的成员变量上推测:

  1. WindowState: 代表Activity在WMS中的Window
  2. DisplayContent: 管理显示窗口以及显示区域
  3. DisplayPolicy: 管理显示窗口的可视化,决定哪些窗口可以有焦点,可以被”看见”(mFocusedWindow)

注意到这些类图里有很多都使用了WindowContainer,这个类中最重要的是一个WindowList类的成员,该类就是一个继承了ArrayList的类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class WindowList<E> extends ArrayList<E> {

void addFirst(E e) {
add(0, e);
}

E peekLast() {
return size() > 0 ? get(size() - 1) : null;
}

E peekFirst() {
return size() > 0 ? get(0) : null;
}
}

简单包装了ArrayList, 实现了三个方法:

  1. addFirst: 将元素添加至index为0的位置,原先元素的index依次+1
  2. peekLast: 获取集合最后一个元素
  3. peekFirst: 获取集合第一个元素

1.2.1.1 DisplayContent 的创建

1
2
3
4
5
6
7
8
9
10
11
12
13
14
DisplayContent(Display display, RootWindowContainer root) {
super(root.mWindowManager, "DisplayContent", FEATURE_ROOT);
......
mRootWindowContainer = root;
......
// 创建DisplayPolicy
mDisplayPolicy = new DisplayPolicy(mWmService, this);
......

// 初始化DisplayAreaPolicy
mDisplayAreaPolicy = mWmService.getDisplayAreaPolicyProvider().instantiate(
mWmService, this /* content */, this /* root */, mImeWindowsContainer);
......
}

DisplayContent的初始化中做了很多工作,包括创建SurfaceControl等等,但这不是我们目前关注的。

DisplayContent是继承了RootDisplayArea, 其对应的Feature是FEATURE_ROOT,表示是该逻辑显示设备上的根显示区域

DisplayPolicy暂时不是我们关注的,主要了解下其是在这里初始化即可, 重点是初始化DisplayAreaPolicy。

1.2.1.2 DisplayAreaPolicy的初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
// getDisplayAreaPolicyProvider拿到的就是WMS初始化时创建的mDisplayAreaPolicyProvider
// 初始化时的代码:
// mDisplayAreaPolicyProvider = DisplayAreaPolicy.Provider.fromResources(
// mContext.getResources());
mDisplayAreaPolicy = mWmService.getDisplayAreaPolicyProvider().instantiate(
mWmService, this /* content */, this /* root */, mImeWindowsContainer);

// DisplayAreaPolicy.java
static Provider fromResources(Resources res) {
// config_deviceSpecificDisplayAreaPolicyProvider用于特定屏幕设备的DisplayAreaPolicy初始化
// 一般都是个空值,除非屏幕设备特殊
String name = res.getString(
com.android.internal.R.string.config_deviceSpecificDisplayAreaPolicyProvider);
if (TextUtils.isEmpty(name)) {
// 使用默认的DisplayAreaPolicy初始化构造器
return new DisplayAreaPolicy.DefaultProvider();
}
...
}

// static final class DefaultProvider implements DisplayAreaPolicy.Provider
public DisplayAreaPolicy instantiate(WindowManagerService wmService,
DisplayContent content, RootDisplayArea root,
DisplayArea.Tokens imeContainer) {
// FEATURE_DEFAULT_TASK_CONTAINER 默认任务容器所在的显示区域
final TaskDisplayArea defaultTaskDisplayArea = new TaskDisplayArea(content, wmService,
"DefaultTaskDisplayArea", FEATURE_DEFAULT_TASK_CONTAINER);
final List<TaskDisplayArea> tdaList = new ArrayList<>();
tdaList.add(defaultTaskDisplayArea);

// 层次结构生成, 将在此基础上构建DisplayArea层次结构。
final HierarchyBuilder rootHierarchy = new HierarchyBuilder(root);
// 设置基本容器(即使显示器不支持输入法)以及设置mTaskDisplayAreas集合
rootHierarchy.setImeContainer(imeContainer).setTaskDisplayAreas(tdaList);
// 这个就是确认Display是否包含FLAG_TRUSTED, 一般是包含的
if (content.isTrusted()) {
// 1.2.1.3 设置rootHierarchy中的feature
configureTrustedHierarchyBuilder(rootHierarchy, wmService, content);
}

// 1.2.1.4 实例化DisplayAreaPolicy
return new DisplayAreaPolicyBuilder().setRootHierarchy(rootHierarchy).build(wmService);
}

创建了名为”DefaultTaskDisplayArea”的显示区域-TaskDisplayArea,并将其放入层次结构生成器中,然后设置该层次结构生成器的相应参数,最后通过DisplayAreaPolicyBuilder实例化DisplayAreaPolicy。

注意传入的content和root都是DisplayContent本身。

1.2.1.3 DisplayAreaPolicy.DefaultProvider.configureTrustedHierarchyBuilder

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private void configureTrustedHierarchyBuilder(HierarchyBuilder rootHierarchy,
WindowManagerService wmService, DisplayContent content) {
// 窗口放大应该在顶部,因为只有一个表面被放大
rootHierarchy.addFeature(new Feature.Builder(wmService.mPolicy, "WindowedMagnification", FEATURE_WINDOWED_MAGNIFICATION)
......
if (content.isDefaultDisplay) {
// 只有默认显示可以有剪切
// See LocalDisplayAdapter.LocalDisplayDevice#getDisplayDeviceInfoLocked.
rootHierarchy.addFeature(new Feature.Builder(wmService.mPolicy, "HideDisplayCutout", FEATURE_HIDE_DISPLAY_CUTOUT)
......
.addFeature(new Feature.Builder(wmService.mPolicy, "OneHandedBackgroundPanel", FEATURE_ONE_HANDED_BACKGROUND_PANEL)
......
.addFeature(new Feature.Builder(wmService.mPolicy, "OneHanded", FEATURE_ONE_HANDED)
}
rootHierarchy
.addFeature(new Feature.Builder(wmService.mPolicy, "FullscreenMagnification", FEATURE_FULLSCREEN_MAGNIFICATION)
......
.addFeature(new Feature.Builder(wmService.mPolicy, "ImePlaceholder", FEATURE_IME_PLACEHOLDER)
......
}

因为这里的层次结构对应DisplayContent所处的DisplayId是默认的显示设备即DEFAULT_DISPLA,默认是添加6个FEATURE:

  1. FEATURE_WINDOWED_MAGNIFICATION: 可以放大的显示区域(比如无障碍里的放大镜)
  2. FEATURE_HIDE_DISPLAY_CUTOUT: 用于隐藏显示裁剪功能的显示区域
  3. FEATURE_ONE_HANDED_BACKGROUND_PANEL:显示区域为单手背景层
  4. FEATURE_ONE_HANDED: 单手功能的显示区域
  5. FEATURE_FULLSCREEN_MAGNIFICATION: 可以放大的显示区域,但这个是整个显示放大
  6. FEATURE_IME_PLACEHOLDER: 可以放置IME(输入法窗口)容器的显示区域

注意这里的顺序比较重要。

1.2.1.4 DisplayAreaPolicyBuilder.build

可以看到DisplayAreaPolicy的构建其实是采用build设计模式,最后由DisplayAreaPolicyBuilder生成DisplayAreaPolicy:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// DisplayAreaPolicyBuilder.java
Result build(WindowManagerService wmService) {
validate();

// 在将窗口添加到组层次结构之前,将DisplayArea组根附加到屏幕层次结构
// 这里的mRootHierarchyBuilder就是上面DefaultProvider.instantiate初始化的rootHierarchy
mRootHierarchyBuilder.build(mDisplayAreaGroupHierarchyBuilders);
List<RootDisplayArea> displayAreaGroupRoots = new ArrayList<>(
mDisplayAreaGroupHierarchyBuilders.size());
for (int i = 0; i < mDisplayAreaGroupHierarchyBuilders.size(); i++) {
HierarchyBuilder hierarchyBuilder = mDisplayAreaGroupHierarchyBuilders.get(i);
hierarchyBuilder.build();
displayAreaGroupRoots.add(hierarchyBuilder.mRoot);
}
// 使用默认的DefaultSelectRootForWindowFunction
if (mSelectRootForWindowFunc == null) {
// RootHierarchyBuilder.mRoot就是创建的DisplayContent
mSelectRootForWindowFunc = new DefaultSelectRootForWindowFunction(
mRootHierarchyBuilder.mRoot, displayAreaGroupRoots);
}
return new Result(wmService, mRootHierarchyBuilder.mRoot, displayAreaGroupRoots,
mSelectRootForWindowFunc);
}

// DefaultSelectRootForWindowFunction的构造
DefaultSelectRootForWindowFunction(RootDisplayArea displayRoot,
List<RootDisplayArea> displayAreaGroupRoots) {
// 所以mDisplayRoot其实就是拥有该DisplayAreaPolicy的DisplayContent
mDisplayRoot = displayRoot;
mDisplayAreaGroupRoots = Collections.unmodifiableList(displayAreaGroupRoots);
}

这里返回的是DisplayAreaPolicyBuilder.Result类对象。并且其TaskDisplayArea是FEATURE_DEFAULT_TASK_CONTAINER,表示默认任务容器所在的显示区域。

1.2.2 WindowToken的创建

1
2
3
final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
token = new WindowToken(this, binder, type, false, displayContent,
session.mCanAddInternalSystemWindow, isRoundedCornerOverlay);

这里的attrs.token我们之前也分析了,其实就是Activity里的mToken.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
WindowToken(WindowManagerService service, IBinder _token, int type, boolean persistOnEmpty,
DisplayContent dc, boolean ownerCanManageAppTokens, boolean roundedCornerOverlay,
boolean fromClientToken, @Nullable Bundle options) {
super(service);
// WindowToken中保存了来自Activity的mToken
token = _token;
windowType = type;
mOptions = options;
mPersistOnEmpty = persistOnEmpty;
mOwnerCanManageAppTokens = ownerCanManageAppTokens;
mRoundedCornerOverlay = roundedCornerOverlay;
mFromClientToken = fromClientToken;
// DisplayContent 不是null的
if (dc != null) {
// 将该WindowToken添加到DisplayContent中
dc.addWindowToken(token, this);
}
}

将该WindowToken添加到DisplayContent中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void addWindowToken(IBinder binder, WindowToken token) {
......
// 将该token保存至mTokenMap中,注意这里的key是binder,即对应Activity中的mToken(LocalActivityRecord)
mTokenMap.put(binder, token);
// 判断该WindowToken是否是ActivityRecord,这里并不是
if (token.asActivityRecord() == null) {
// 将该WindowToken的DisplayContent赋值为当前DisplayContent
token.mDisplayContent = this;
// 该token对应Activity的Window类型是TYPE_BASE_APPLICATION
switch (token.windowType) {
case TYPE_INPUT_METHOD:
case TYPE_INPUT_METHOD_DIALOG:
mImeWindowsContainer.addChild(token);
break;
default:
// 将该token保存至mDisplayAreaPolicy中
mDisplayAreaPolicy.addWindow(token);
break;
}
}
}

这里的mDisplayAreaPolicy是该DisplayContent初始化时创建的,所以 mDisplayAreaPolicy.addWindow(token) 其实是调用了 DisplayAreaPolicyBuilder.Result.addWindow:

1
2
3
4
5
6
7
8
9
10
public void addWindow(WindowToken token) {
DisplayArea.Tokens area = findAreaForToken(token);
area.addChild(token);
}

DisplayArea.Tokens findAreaForToken(WindowToken token) {
// mSelectRootForWindowFunc就是DefaultSelectRootForWindowFunction
return mSelectRootForWindowFunc.apply(token.windowType, token.mOptions)
.findAreaForToken(token);
}

mSelectRootForWindowFunc一般都是DefaultSelectRootForWindowFunction:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public RootDisplayArea apply(Integer windowType, Bundle options) {
if (mDisplayAreaGroupRoots.isEmpty()) {
return mDisplayRoot;
}

// 默认mDisplayAreaGroupRoots中只有一个RootDisplayArea,也就是mDisplayRoot,(CTS测试会有多个)
if (options != null && options.containsKey(KEY_ROOT_DISPLAY_AREA_ID)) {
final int rootId = options.getInt(KEY_ROOT_DISPLAY_AREA_ID);
if (mDisplayRoot.mFeatureId == rootId) {
return mDisplayRoot;
}
for (int i = mDisplayAreaGroupRoots.size() - 1; i >= 0; i--) {
if (mDisplayAreaGroupRoots.get(i).mFeatureId == rootId) {
return mDisplayAreaGroupRoots.get(i);
}
}
}
return mDisplayRoot;
}

mSelectRootForWindowFunc.apply(token.windowType, token.mOptions).findAreaForToken(token)其实是根据Window类型和Window所在的显示屏幕来选择应该将此WindowToken放入哪个RootDisplayArea中存储。

一般来说,mDisplayAreaGroupRoots中只有一个元素,即mDisplayRoot,也就是DisplayContent(猜猜什么情况下会有多个DisplayContent?)。所以这个方法其实等效于:mDisplayRoot.findAreaForToken,这里的mDisplayRoot就是DisplayContent:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Nullable
DisplayArea.Tokens findAreaForToken(WindowToken token) {
return findAreaForToken(token.windowType, token.mOwnerCanManageAppTokens,
token.mRoundedCornerOverlay);
}

@Nullable
DisplayArea.Tokens findAreaForToken(int windowType, boolean ownerCanManageAppTokens,
boolean roundedCornerOverlay) {
// 获取window类型对应的layer,getWindowLayerFromTypeLw看[Window类型转Layer](#222-windowmanagerpolicygetwindowlayerfromtypelw)。
// 一般应用的window类型是 TYPE_BASE_APPLICATION (值为1,handleResumeActivity中赋值的)
// 所以windowLayerFromType就是APPLICATION_LAYER!
int windowLayerFromType = mWmService.mPolicy.getWindowLayerFromTypeLw(windowType,
ownerCanManageAppTokens, roundedCornerOverlay);
// 所以这里就会抛出异常???
if (windowLayerFromType == APPLICATION_LAYER) {
throw new IllegalArgumentException(
"There shouldn't be WindowToken on APPLICATION_LAYER");
}
return mAreaForLayer[windowLayerFromType];
}

因为Activity对应的windowType是TYPE_BASE_APPLICATION,所以拿到的windowLayerFromType就是APPLICATION_LAYER,那么这里必然会抛出异常!这个疑问我们后续分析,先看返回值,这里最终是返回了RootDisplayArea中mAreaForLayer数组对应存储的对象,接下来我们看看这个数组是如何被创建。

二. RootDisplayArea的构造

2.1 HierarchyBuilder.build

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
private void build(@Nullable List<HierarchyBuilder> displayAreaGroupHierarchyBuilders) {
// ------ 第一部分 --------
// 这个mPolicy就是WMS服务初始化时候创建的PhoneWindowManager
final WindowManagerPolicy policy = mRoot.mWmService.mPolicy;
// getMaxWindowLayer值目前是固定的36,也就是窗口Layer最大不能超过36,稍后我们就知道为什么是36了!
final int maxWindowLayerCount = policy.getMaxWindowLayer() + 1;
final DisplayArea.Tokens[] displayAreaForLayer =
new DisplayArea.Tokens[maxWindowLayerCount];
// 由前面的分析可知,这里的feature数目是6个
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是一个树形结构, 以下使用PA替代
PendingArea[] areaForLayer = new PendingArea[maxWindowLayerCount];
// 创建根PA,并用其填充上面创建的36个PA数组,也就是每一个PA对应一个WindowLayer
final PendingArea root = new PendingArea(null, 0, null);
// 注意这里areaForLayer数组中每一个元素都是root,而不是copy了一个PA
Arrays.fill(areaForLayer, root);

// ------ 第二部分 --------
// 创建显示区域以覆盖所有的Feature
final int size = mFeatures.size();
for (int i = 0; i < size; i++) {
// 按照定义的顺序遍历feature,以便数组前面的Feature位于层次结构的顶部。
final Feature feature = mFeatures.get(i);
PendingArea featureArea = null;
for (int layer = 0; layer < maxWindowLayerCount; layer++) {
// Feature的mWindowLayers是一个boolean类型的数组,但是该值又是什么意思呢?
// 这里我们先打断一下,看看Feature是怎么构造的--[2.2.3](DisplayAreaPolicyBuilder.Feature的构造)
if (feature.mWindowLayers[layer]) {
// 此功能将应用于此窗口层。为它找到一个显示区:
// 可以重用现有的一个,如果它是为上一层的此功能创建的,并且应用到上一层的最后一个功能与应用到当前层的功能相同(因此它们可以共享相同的父显示区域)
if (featureArea == null || featureArea.mParent != areaForLayer[layer]) {
// 没有合适的显示区域:
// 在该层的上一个区域(作为父对象)下创建一个新区域。
featureArea = new PendingArea(feature, layer, areaForLayer[layer]);
areaForLayer[layer].mChildren.add(featureArea);
}
areaForLayer[layer] = featureArea;
} else {
// 此功能不会应用于此窗口层。如果需要应用到下一层,需要为此创建一个新的显示区域。
featureArea = null;
}
}
}

// ------ 第三部分 --------
// 为每个层创建Tokens作为叶子
PendingArea leafArea = null;
int leafType = LEAF_TYPE_TOKENS;
for (int layer = 0; layer < maxWindowLayerCount; layer++) {
// 2.2.1 获取当前layer类型
int type = typeOfLayer(policy, layer);
// 检查我们是否可以重用上一层的相同Tokens。如果上一个图层与当前图层的类型相同,并且没有仅应用于其中一个图层的功能,则会发生这种情况。
if (leafArea == null || leafArea.mParent != areaForLayer[layer]
|| type != leafType) {
// 为当前layer创建新的Tokens
leafArea = new PendingArea(null /* feature */, layer, areaForLayer[layer]);
areaForLayer[layer].mChildren.add(leafArea);
leafType = type;
if (leafType == LEAF_TYPE_TASK_CONTAINERS) {
// 我们使用传入的TaskDisplayAreas作为层的任务容器类型。
// 即使没有TaskDisplayArea,也不要创建Tokens。
addTaskDisplayAreasToApplicationLayer(areaForLayer[layer]);
addDisplayAreaGroupsToApplicationLayer(areaForLayer[layer],
displayAreaGroupHierarchyBuilders);
leafArea.mSkipTokens = true;
} else if (leafType == LEAF_TYPE_IME_CONTAINERS) {
// 我们使用传入的ImeContainer作为层的ime容器类型。
// 即使没有ime容器,也不要创建Tokens。
leafArea.mExisting = mImeContainer;
leafArea.mSkipTokens = true;
}
}
leafArea.mMaxLayer = layer;
}

// ------ 第四部分 --------
// [2.5.1]计算最大layer
root.computeMaxLayer();

// [2.5.2]构建了一个PendingAreas树,其中包含表示层次结构的所有必要信息,现在创建并将真实的显示区域附加到根目录
root.instantiateChildren(mRoot, displayAreaForLayer, 0, featureAreas);

// [2.5.3]完成DisplayArea的创建,缓存结果便于快速访问
mRoot.onHierarchyBuilt(mFeatures, displayAreaForLayer, featureAreas);
}

此方法使用以下属性构造图层层次结构:

  1. 每个Feature映射到一组显示区域(PendingArea)
  2. 添加窗口后,对于该窗口类型所属的每个要素,它都是该要素的相应显示区域之一的后代。
  3. 保持Z顺序,即如果Z范围(区域)表示显示区域内的窗口层集:
    对于每对DisplayArea同级(a,b),其中a低于b,它保持 max(z-range(a)) <= min(z-range(b))

下面的算法迭代地创建这样一个层次结构:
-最初,所有窗口都附加到根目录。
-对于每个feature,我们通过在层上循环创建一组显示区域
-如果该feature确实适用于当前层,我们需要为它找到一个显示区域以满足(2)
-如果当前feature也适用于前一层(满足(3)),并且应用于前一层的最后一个特征与应用于当前层的最后一个特征相同(满足(2)),我们可以重用前一层的区域
-否则,我们将在应用于当前层的最后一个功能下面创建一个新的显示区域

云里雾里的,看不懂啊, 将这段代码分成四个部分,逐一解析。

2.2 build的第一部分 - layer, feature

2.2.1 layer类型 – HierarchyBuilder.typeOfLayer

此方法获取layer对应的类型:

1
2
3
4
5
6
7
8
9
10
11
private static int typeOfLayer(WindowManagerPolicy policy, int layer) {
if (layer == APPLICATION_LAYER) {
return LEAF_TYPE_TASK_CONTAINERS;
// 2.2.2 获取输入法对应的layer类型
} else if (layer == policy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD)
|| layer == policy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD_DIALOG)) {
return LEAF_TYPE_IME_CONTAINERS;
} else {
return LEAF_TYPE_TOKENS;
}
}

首先这里的layer类型有三种:

  1. LEAF_TYPE_TOKENS: tokens的layer
  2. LEAF_TYPE_TASK_CONTAINERS: 作为Task的layer
  3. LEAF_TYPE_IME_CONTAINERS: 作为输入法的layer

其次当layer=2时,其type为LEAF_TYPE_TASK_CONTAINERS;当layer=15或16时,其type为LEAF_TYPE_IME_CONTAINERS;其余layer都是LEAF_TYPE_TOKENS类型。

2.2.2 WindowManagerPolicy.getWindowLayerFromTypeLw

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
default int getWindowLayerFromTypeLw(int type, boolean canAddInternalSystemWindow,
boolean roundedCornerOverlay) {
// 始终将圆角层放在最上面, 所以返回的layer类型就是对应最大的layer index
// 所谓圆角层是指手机屏幕的四个角,现在大多时候圆角,会用一层黑色覆盖避免硬件显示的毛刺
// 这样也容易调整角的弧度,使得出厂机器表现一致
if (roundedCornerOverlay && canAddInternalSystemWindow) {
return getMaxWindowLayer();
}
// 窗口类型是应用窗口时,返回的layer就是APPLICATION_LAYER(2)
if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
return APPLICATION_LAYER;
}

switch (type) {
case TYPE_WALLPAPER:
// 墙纸在底部,不过可能会被WindowManager移动
return 1;
......
case TYPE_POINTER:
// (鼠标)指针层
return 35;
default:
Slog.e("WindowManager", "Unknown window type: " + type);
return 3;
}
}

这里就可以解答刚刚的疑问了,为什么layer数量是36了,在考虑上面Window类型和Layer类型的对应,可以想到,layer类型的也是其Z轴的位置!

Window类型 作用 Layer(Z轴)
TYPE_WALLPAPER 2013 壁纸窗口 1
[FIRST_APPLICATION_WINDOW,
LAST_APPLICATION_WINDOW]
[1, 99] 应用窗口 2
TYPE_PRESENTATION
TYPE_PRIVATE_PRESENTATION
TYPE_DOCK_DIVIDER
TYPE_QS_DIALOG
TYPE_PHONE
2037
2030
2034
2035
2002
*在外部显示器上显示的窗口
*在专用屏幕上方显示的窗口
*显示调整docked堆栈大小的窗口
*用于快速设置的窗口
*phone窗口,比如来电窗口
3
TYPE_SEARCH_BAR
TYPE_VOICE_INTERACTION_STARTING
2001
2033
*搜索栏窗口,位于屏幕顶部
*语音交互层的启动窗口
4
TYPE_VOICE_INTERACTION 2031 语音交互层窗口 5
TYPE_INPUT_CONSUMER 2022 systemUI栏隐藏时使用输入事件的窗口 6
TYPE_SYSTEM_DIALOG 2008 从状态栏滑出的面板窗口 7
TYPE_TOAST 2005 临时通知窗口,即toast窗口 8
TYPE_PRIORITY_PHONE 2007 优先电话UI界面 9
TYPE_SYSTEM_ALERT 2003 系统窗口,比如低电Dialog或者ANR之类 10或者13
TYPE_APPLICATION_OVERLAY 2038 应用覆盖窗口,在所有窗口的上方
但在关键系统窗口(如状态栏)的下方
需要SYSTEM_ALERT_WINDOW权限
12
TYPE_INPUT_METHOD 2011 内部输入法窗口,显示在普通用户界面上方。
可以调整应用窗口的大小或平移,以在显示
此窗口时保持输入焦点可见
15
TYPE_INPUT_METHOD_DIALOG 2012 显示在当前输入法窗口上方的内部输入法
对话框窗口
16
TYPE_STATUS_BAR 2000 状态栏 17
TYPE_STATUS_BAR_ADDITIONAL 2041 用于在屏幕的非常规部分(即屏幕的左侧或
底部)显示状态栏窗口
18
TYPE_NOTIFICATION_SHADE 2040 状态栏下拉的通知栏窗口和keyguard 19
TYPE_STATUS_BAR_SUB_PANEL 2017 从状态栏滑出的面板窗口 20
TYPE_KEYGUARD_DIALOG 2009 keyguard创建的对话框窗口 21
TYPE_VOLUME_OVERLAY 2020 音量调整窗口 22
TYPE_SYSTEM_OVERLAY 2006 系统覆盖窗口,需要显示在所有其他窗口之上。
这些窗口不能获取输入焦点,否则会干扰keyguard
23或者11
TYPE_NAVIGATION_BAR 2019 导航栏窗口,即虚拟按键的窗口 24
TYPE_NAVIGATION_BAR_PANEL 2024 导航栏面板窗口 25
TYPE_SCREENSHOT 2036 截图窗口,用于屏幕截图动画、区域选择和UI 26
TYPE_SYSTEM_ERROR 2010 内部系统错误窗口 27或者10
TYPE_MAGNIFICATION_OVERLAY 2027 放大覆盖窗口。启用辅助功能放大时,用于
突出显示的放大部分
28
TYPE_DISPLAY_OVERLAY 2026 显示覆盖窗口。用于模拟辅助显示设备 29
TYPE_DRAG 2016 拖放窗口 30
TYPE_ACCESSIBILITY_OVERLAY 2032 无障碍辅助窗口 31
TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY 2039 无障碍辅助放大窗口 32
TYPE_SECURE_SYSTEM_OVERLAY 2015 安全系统覆盖窗口。
这些窗口不能获取输入焦点,否则会干扰keyguard
33
TYPE_BOOT_PROGRESS 2021 开机动画之后的启动对话框窗口 34
TYPE_POINTER 2018 鼠标指针窗口。如轨迹追踪球 35
- roundedCornerOverlay=true
canAddInternalSystemWindow=true
圆角层,始终在最上面 36

在分析DisplayArea的构造时,首先需要了解下这些类的作用:Feature、Feature.Builder、HierarchyBuilder:

2.2.3 DisplayAreaPolicyBuilder.Feature的构造

1
2
3
4
5
6
7
static class Feature {
private final String mName;
private final int mId;
private final boolean[] mWindowLayers;
private final NewDisplayAreaSupplier mNewDisplayAreaSupplier;
......
}

注意mWindowLayers是一个boolean数组,这里的feature有如下几种:

Feature Name ID Function
FEATURE_ROOT 0 表示该逻辑显示设备上的根显示区域
FEATURE_DEFAULT_TASK_CONTAINER 1 默认任务容器所在的显示区域
FEATURE_WINDOW_TOKENS 2 无Activity的显示区域
FEATURE_ONE_HANDED 3 单手功能的显示区域
FEATURE_WINDOWED_MAGNIFICATION 4 可以放大的显示区域。包含在{@link WindowManager.LayoutParams#TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY}下面的所有窗口
FEATURE_FULLSCREEN_MAGNIFICATION 5 可以放大的显示区域,但这个是整个显示放大
FEATURE_HIDE_DISPLAY_CUTOUT 6 用于隐藏显示裁剪功能的显示区域
FEATURE_IME_PLACEHOLDER 7 可以放置IME容器的显示区域。当IME目标改变时,如果IME容器可能被重表示为该层次结构,那么应该在每个根层次结构上启用。
FEATURE_ONE_HANDED_BACKGROUND_PANEL 8 显示区域为单手背景层,防止用户在打开暗主题时,无法清楚地识别屏幕已进入单手模式。
FEATURE_SYSTEM_LAST 10000 系统特征显示区的最后一个id,作为边界
FEATURE_VENDOR_FIRST 10001 供应商特定的显示区域定义可以从该值开始
FEATURE_VENDOR_LAST 20001 供应商特定的显示区域定义用该值作为边界
FEATURE_RUNTIME_TASK_CONTAINER_FIRST 20002 可以在运行时创建的任务显示区域以此值开始

2.2.4 DisplayAreaPolicyBuilder.Feature.Builder

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
static class Builder {
private final WindowManagerPolicy mPolicy;
private final String mName;
private final int mId;
private final boolean[] mLayers;
private NewDisplayAreaSupplier mNewDisplayAreaSupplier = DisplayArea::new;
private boolean mExcludeRoundedCorner = true;

Builder(WindowManagerPolicy policy, String name, int id) {
mPolicy = policy;
mName = name;
mId = id;
// mPolicy.getMaxWindowLayer = 36
mLayers = new boolean[mPolicy.getMaxWindowLayer() + 1];
}

Feature build() {
if (mExcludeRoundedCorner) {
// 将mLayers[36]置为false
mLayers[mPolicy.getMaxWindowLayer()] = false;
}
// builder中的mLayers就是Feature中的mWindowLayers
return new Feature(mName, mId, mLayers.clone(), mNewDisplayAreaSupplier);
}
......
}

注意看这里的mLayers,也是一个boolean数组,是一一对应36个layer的。

接下来看看 DisplayAreaPolicyBuilder.Feature.Builder 中关键方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// 将mLayers中所有元素都置为true
Builder all() {
Arrays.fill(mLayers, true);
return this;
}

// 传入一个int数组,其元素值代表Window类型,标记这些window窗口对应的layer为true
Builder and(int... types) {
for (int i = 0; i < types.length; i++) {
int type = types[i];
// 将该Window类型对应的layer置位true
set(type, true);
}
return this;
}

// 传入一个int数组,其元素值代表Window类型, 标记这些window窗口对应的layer为false
Builder except(int... types) {
for (int i = 0; i < types.length; i++) {
int type = types[i];
set(type, false);
}
return this;
}

// 从layer所处z轴从低往高直到typeInclusive类型窗口对应的layer,全部标记为true
// 注意是包含typeInclusive类型的
Builder upTo(int typeInclusive) {
final int max = layerFromType(typeInclusive, false);
for (int i = 0; i < max; i++) {
mLayers[i] = true;
}
set(typeInclusive, true);
return this;
}

// 将window类型为type对应的layer在mLayers中标记为value
private void set(int type, boolean value) {
mLayers[layerFromType(type, true)] = value;
// 当window类型是应用覆盖窗口时,其在所有窗口的上方, 但在关键系统窗口(如状态栏)的下方
if (type == TYPE_APPLICATION_OVERLAY) {
// 这是多余的一句了。。。
mLayers[layerFromType(type, true)] = value;
// 将TYPE_SYSTEM_ALERT、TYPE_SYSTEM_OVERLAY、TYPE_SYSTEM_ERROR的Layer对应的数组元素也置为true
mLayers[layerFromType(TYPE_SYSTEM_ALERT, false)] = value;
mLayers[layerFromType(TYPE_SYSTEM_OVERLAY, false)] = value;
mLayers[layerFromType(TYPE_SYSTEM_ERROR, false)] = value;
}
}

private int layerFromType(int type, boolean internalWindows) {
// 所以这个type是window的类型
return mPolicy.getWindowLayerFromTypeLw(type, internalWindows);
}

接下来我们通过分析FEATURE_WINDOWED_MAGNIFICATION和FEATURE_IME_PLACEHOLDER的构造,来理解,这个Feature的作用

2.2.4.1 FEATURE_WINDOWED_MAGNIFICATION

1
2
3
4
5
6
7
rootHierarchy.addFeature(new Feature.Builder(wmService.mPolicy, "WindowedMagnification",
FEATURE_WINDOWED_MAGNIFICATION)
.upTo(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY)
.except(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY)
// Make the DA dimmable so that the magnify window also mirrors the dim layer.
.setNewDisplayAreaSupplier(DisplayArea.Dimmable::new)
.build());

FEATURE_WINDOWED_MAGNIFICATION的feature构造,注释上标记的是代表可以放大的显示区域(比如无障碍里的放大镜),名称是”WindowedMagnification”:

  1. 首先调用 Builder.upTo(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY), TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY的layer对应Z轴值为32,也就是mLayer中0~31的值都是true
  2. 调用except(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY),将其对应的layer值32置为false

2.2.4.2 FEATURE_IME_PLACEHOLDER

1
2
3
4
rootHierarchy.addFeature(new Feature.Builder(wmService.mPolicy, "ImePlaceholder",
FEATURE_IME_PLACEHOLDER)
.and(TYPE_INPUT_METHOD, TYPE_INPUT_METHOD_DIALOG)
.build());

FEATURE_IME_PLACEHOLDER的feature构造,注释上标记的是代表可以放置IME(输入法窗口)容器的显示区域,只有一个最简单的and(TYPE_INPUT_METHOD, TYPE_INPUT_METHOD_DIALOG), 也就是仅仅将该feature中mWindowLayers里对应TYPE_INPUT_METHOD(15)和TYPE_INPUT_METHOD_DIALOG(16)的layer置位true, 其他都是false.

好了,大致明白Feature的构造过程,以及mWindowLayers中的值来源,现在在回过头继续看 HierarchyBuilder.build 过程。

2.3 build第二部分–PendingArea的构造

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
final int size = mFeatures.size();
for (int i = 0; i < size; i++) {
// 按照定义的顺序遍历feature,以便数组前面的Feature位于层次结构的顶部。
final Feature feature = mFeatures.get(i);
PendingArea featureArea = null;
for (int layer = 0; layer < maxWindowLayerCount; layer++) {
// Feature的mWindowLayers是一个boolean类型的数组,但是该值又是什么意思呢?
// 这里我们先打断一下,看看Feature是怎么构造的--[2.2.3](DisplayAreaPolicyBuilder.Feature的构造)
if (feature.mWindowLayers[layer]) {
// 此功能将应用于此窗口层。为它找到一个显示区:
// 可以重用现有的一个,如果它是为上一层的此功能创建的,并且应用到上一层的最后一个功能与应用到当前层的功能相同(因此它们可以共享相同的父显示区域)
if (featureArea == null || featureArea.mParent != areaForLayer[layer]) {
// 没有合适的显示区域:
// 在该层的上一个区域(作为父对象)下创建一个新区域。
featureArea = new PendingArea(feature, layer, areaForLayer[layer]);
areaForLayer[layer].mChildren.add(featureArea);
}
areaForLayer[layer] = featureArea;
} else {
// 此功能不会应用于此窗口层。如果需要应用到下一层,需要为此创建一个新的显示区域。
featureArea = null;
}
}
}

以默认的DisplayContent为例,有6个FEATURE:

Feature名 操作 mWindowLayers中为true的元素 mWindowLayers中为false的元素
FEATURE_WINDOWED_MAGNIFICATION .upTo(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY)
.except(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY)
[0,31] [32,36]
FEATURE_HIDE_DISPLAY_CUTOUT .all()
.except(TYPE_NAVIGATION_BAR,
&nbsp;&nbsp;&nbsp;&nbsp;TYPE_NAVIGATION_BAR_PANEL,
&nbsp;&nbsp;&nbsp;&nbsp;TYPE_STATUS_BAR,TYPE_NOTIFICATION_SHADE)
all 17,19,24,25
FEATURE_ONE_HANDED_BACKGROUND_PANEL .upTo(TYPE_WALLPAPER) 0 [1,36]
FEATURE_ONE_HANDED .all()
.except(TYPE_NAVIGATION_BAR,
&nbsp;&nbsp;&nbsp;&nbsp;TYPE_NAVIGATION_BAR_PANEL)
all 24,25
FEATURE_FULLSCREEN_MAGNIFICATION .all()
.except(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY,
&nbsp;&nbsp;&nbsp;&nbsp;TYPE_INPUT_METHOD,
&nbsp;&nbsp;&nbsp;&nbsp;TYPE_INPUT_METHOD_DIALOG
&nbsp;&nbsp;&nbsp;&nbsp;TYPE_MAGNIFICATION_OVERLAY,
&nbsp;&nbsp;&nbsp;&nbsp;TYPE_NAVIGATION_BAR,
&nbsp;&nbsp;&nbsp;&nbsp;TYPE_NAVIGATION_BAR_PANEL)
all 15,16,24,25,32
FEATURE_IME_PLACEHOLDER .and(TYPE_INPUT_METHOD,
&nbsp;&nbsp;&nbsp;&nbsp;TYPE_INPUT_METHOD_DIALOG)
15,16 all
  1. 第一个feature-FEATURE_WINDOWED_MAGNIFICATION里的mWindowLayers是[0,31]为true,其他位为false, 当此feature遍历完毕之后,areaForLayer数组中[0,31]的元素都是同一个新创建的PA,记为PA_for_WF_0(其mParent为root), 其他元素都是指向root,而且现在root.mChildren中的唯一元素就是PA_for_WF_0. areaForLayer中的结果如下:
    1. [0,31] = PA_for_WF_0(其mParent为root)
    2. [32,36]= root
  2. FEATURE_HIDE_DISPLAY_CUTOUT里除了17,19,24,25号其余都是true:
    1. 当第二个for循环开始时,featureArea为null, 会新建一个PA, 记为PA_for_HDC_0, 并将其保存到root.mChildren中
    2. 依次将areaForLayer[0,16]置为PA_for_HDC_0
    3. 当layer为17时,因为FEATURE_HIDE_DISPLAY_CUTOUT.mWindowLayers[17]为false, 所以会将featureArea重置为null, 继续
    4. 当layer为18时,而FEATURE_HIDE_DISPLAY_CUTOUT.mWindowLayers[18]为true, 所以又创建一个PA, 记为PA_for_HDC_18,并将areaForLayer[18]置为PA_for_HDC_18
    5. 重复制止遍历完毕
      1. 现在areaForLayer中的结果如下:
        1. [0,16] = PA_for_HDC_0 (此PA的mParent为PA_for_WF_0)
        2. [17] = PA_for_WF_0
        3. [18] = PA_for_HDC_18 (此PA的mParent为PA_for_WF_0)
        4. [19] = PA_for_WF_0
        5. [20,23]= PA_for_HDC_20 (此PA的mParent为PA_for_WF_0)
        6. [24,25]= PA_for_WF_0
        7. [26,31]= PA_for_HDC_26 (此PA的mParent为PA_for_WF_0)
        8. [32,35]= PA_for_HDC_32 (此PA的mParent为root)
        9. [36] = root
      2. root.mChildren结果中保存PA_for_WF_0、PA_for_HDC_0、PA_for_HDC_18、PA_for_HDC_20及PA_for_HDC_26,PA_for_HDC_32

这么分析不够直观,我们画个图来看看:

2-3

然后其他的feature过程省略,直接看结果:

2-4

于此同时,各个PA中的mParent指向如下:

2-5

注意每个PA中的mChildren集合包含所有以该PA为mParent的PA. 其子PA按从上到下,从左到右的顺序依次被添加到该PA中。

2.4 build第三部分–创建Tokens

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// ------ 第三部分 --------
// 为每个层创建Tokens作为叶子
PendingArea leafArea = null;
int leafType = LEAF_TYPE_TOKENS;
for (int layer = 0; layer < maxWindowLayerCount; layer++) {
// 2.2.1 获取当前layer类型
int type = typeOfLayer(policy, layer);
// 检查我们是否可以重用上一层的相同Tokens。如果上一个图层与当前图层的类型相同,并且没有仅应用于其中一个图层的功能,则会发生这种情况。
if (leafArea == null || leafArea.mParent != areaForLayer[layer]
|| type != leafType) {
// 为当前layer创建新的Tokens
leafArea = new PendingArea(null /* feature */, layer, areaForLayer[layer]);
areaForLayer[layer].mChildren.add(leafArea);
leafType = type;
if (leafType == LEAF_TYPE_TASK_CONTAINERS) {
// 使用传入的TaskDisplayAreas作为任务容器类型。
// [2.4.1] 即 DisplayAreaPolicy的初始化 的时候创建的 DefaultTaskDisplayArea
addTaskDisplayAreasToApplicationLayer(areaForLayer[layer]);
addDisplayAreaGroupsToApplicationLayer(areaForLayer[layer],
displayAreaGroupHierarchyBuilders);
leafArea.mSkipTokens = true;
} else if (leafType == LEAF_TYPE_IME_CONTAINERS) {
// 我们使用传入的ImeContainer作为层的ime容器类型。
// 即使没有ime容器,也不要创建Tokens。
leafArea.mExisting = mImeContainer;
leafArea.mSkipTokens = true;
}
}
leafArea.mMaxLayer = layer;
}

这段循环看起来和第二段中的循环类似,从Z轴最底层往上依次遍历37个layer。typeOfLayer这个我们已经分析过了: 其次当layer=2(也就是APPLICATION_LAYER)时,其type为LEAF_TYPE_TASK_CONTAINERS;当layer=15或16时,其type为LEAF_TYPE_IME_CONTAINERS;其余layer都是LEAF_TYPE_TOKENS类型:

  1. layer=0时,layerType=LEAF_TYPE_TOKENS, 创建一个PA,记为PA_TOKENS_0, 其mParent指向areaForLayer[0], 也即是 PA_for_FM_0, 并将该PA添加到PA_for_FM_0的mChildren中。
  2. layer=1时,layerType=LEAF_TYPE_TOKENS, 现在leafArea就是PA_TOKENS_0, 所以此次循环仅仅是将PA_TOKENS_0.mMaxLayer改成当前layer也就是1.
  3. layer=2时,layerType=LEAF_TYPE_TASK_CONTAINERS, 新建一个PA, 记为 PA_TASK_2, 其mParent指向areaForLayer[2],即 PA_for_FM_2
  4. layer=3时,layerType=LEAF_TYPE_TOKENS, 创建一个PA,记为PA_TOKENS_3, 其mParent指向areaForLayer[3], 还是 PA_for_FM_2

最终结果如下:

2-6

上图中,leafArea的命名方式是 PA_layerType_minLayer_max_Layer. 箭头指向的方向就是其parent方向。

2.4.1 HierarchyBuilder.addTaskDisplayAreasToApplicationLayer

1
2
3
4
5
6
7
8
9
10
11
12
13
private void addTaskDisplayAreasToApplicationLayer(PendingArea parentPendingArea) {
// mTaskDisplayAreas中仅包含一个名为"DefaultTaskDisplayArea"的TaskDisplayArea
final int count = mTaskDisplayAreas.size();
for (int i = 0; i < count; i++) {
PendingArea leafArea =
new PendingArea(null /* feature */, APPLICATION_LAYER, parentPendingArea);
// 将mTaskDisplayAreas中的DisplayArea放入该PA中
leafArea.mExisting = mTaskDisplayAreas.get(i);
leafArea.mMaxLayer = APPLICATION_LAYER;
// 添加一个新的PA, 记为 PA_Task_2_2_tda
parentPendingArea.mChildren.add(leafArea);
}
}

现在我们知道传入的parentPendingArea就是上面构造的areaForLayer[APPLICATION_LAYER], 即 PA_for_FM_2!

这里就是把mTaskDisplayAreas中所有的TaskDisplayArea保存到PA_for_FM_2中, 最后结果如下:

2-7

2-8

2.5 build第四部分–创建DisplayArea

1
2
3
4
5
6
7
8
9
// ------ 第四部分 --------
// [2.5.1]计算最大layer
root.computeMaxLayer();

// [2.5.2]构建了一个PendingAreas树,其中包含表示层次结构的所有必要信息,现在创建并将真实的显示区域附加到根目录
root.instantiateChildren(mRoot, displayAreaForLayer, 0, featureAreas);

// [2.5.3]完成DisplayArea的创建,缓存结果便于快速访问
mRoot.onHierarchyBuilt(mFeatures, displayAreaForLayer, featureAreas);

2.5.1 计算最大layer

1
2
3
4
5
6
int computeMaxLayer() {
for (int i = 0; i < mChildren.size(); i++) {
mMaxLayer = Math.max(mMaxLayer, mChildren.get(i).computeMaxLayer());
}
return mMaxLayer;
}

有上面的图可以知道,root的children中最大的mMaxLayer为PA_TOKENS_36_36, 值为36。

2.5.2 遍历PA树生成DisplayArea

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void instantiateChildren(DisplayArea<DisplayArea> parent, DisplayArea.Tokens[] areaForLayer,
int level, Map<Feature, List<DisplayArea<WindowContainer>>> areas) {
// 将root里面的mChildren按照mMinLayer的大小排序,最小的在前面
mChildren.sort(Comparator.comparingInt(pendingArea -> pendingArea.mMinLayer));
// 遍历所有的children
for (int i = 0; i < mChildren.size(); i++) {
final PendingArea child = mChildren.get(i);
// 为该children创建DisplayArea
final DisplayArea area = child.createArea(parent, areaForLayer);
if (area == null) {
// TaskDisplayArea 和 ImeContainer 可以不用设置
continue;
}
// 为父DisplayArea添加新建的DisplayArea,且放在最前面
// POSITION_TOP的值是Integer.MAX_VALUE, 这里的意思就是放在parent.mChildren集合的最末尾(Z轴来看就是最上面)
// parent就是DisplayContent本身
parent.addChild(area, WindowContainer.POSITION_TOP);
if (child.mFeature != null) {
areas.get(child.mFeature).add(area);
}
// 为每一个子PA遍历生成其子树的DisplayArea
child.instantiateChildren(area, areaForLayer, level + 1, areas);
}
}

mChildren中只有三个PA, 分别是: PA_for_WF_0, PA_for_HDC_32以及PA_TOKENS_36_36.

这个方法不难看出,就是简单的遍历我们之前创建的PA树,然后给出了root之外的所有PA生成其对应的DisplayArea。

注意这里的传入的parent就是mRoot, 也就是DisplayContent,所以DisplayContent.mChildren中存入的元素就是此时构建的DisplayArea。

2.5.2.1 PendingArea.createArea

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
private DisplayArea createArea(DisplayArea<DisplayArea> parent,
DisplayArea.Tokens[] areaForLayer) {
// 对于PA树来说,有且只有两个PA的mExisting不为null: PA_TASK_2_2_tda和PA_IME_15_16
if (mExisting != null) {
if (mExisting.asTokens() != null) {
// 为layer存储WindowToken的容器
fillAreaForLayers(mExisting.asTokens(), areaForLayer);
}
return mExisting;
}
if (mSkipTokens) {
return null;
}
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;
}
// 注意只有leafArea的mFeature才是null的(或者root,但是root不参与DisplayArea的创建)
if (mFeature == null) {
final DisplayArea.Tokens leaf = new DisplayArea.Tokens(parent.mWmService, type,
"Leaf:" + mMinLayer + ":" + mMaxLayer);
fillAreaForLayers(leaf, areaForLayer);
return leaf;
} else {
return mFeature.mNewDisplayAreaSupplier.create(parent.mWmService, type,
mFeature.mName + ":" + mMinLayer + ":" + mMaxLayer, mFeature.mId);
}
}

这里会创建PendingArea对应的DisplayArea,对于我们的PA树来说,有且只有两个PA的mExisting不为null: PA_TASK_2_2_tda和PA_IME_15_16!

这里我们先看看PA_TASK_2_2_tda的DisplayArea的创建:

1
2
3
4
5
6
7
8
// 对于PA树来说,有且只有两个PA的mExisting不为null: PA_TASK_2_2_tda和PA_IME_15_16
if (mExisting != null) {
if (mExisting.asTokens() != null) {
// 为layer存储WindowToken的容器
fillAreaForLayers(mExisting.asTokens(), areaForLayer);
}
return mExisting;
}

我们知道PA_TASK_2_2_tda的mExisting就是1.2.1.2 DisplayAreaPolicy的初始化中创建的”DefaultTaskDisplayArea”

final TaskDisplayArea defaultTaskDisplayArea = new TaskDisplayArea(content, wmService, “DefaultTaskDisplayArea”, FEATURE_DEFAULT_TASK_CONTAINER);

而TaskDisplayArea又是继承了DisplayArea, 其asTokens直接返回了null, 所以说PA_TASK_2_2_tda的DisplayArea就是其mExisting!

2-9

接下来是PA_IME_15_16的DisplayArea的创建:

因为mImeContainer是在DisplayContent初始化过程中创建的ImeContainer类的对象,其就是Tokens的子类。所以mExisting.asTokens()确实不为null:

1
2
3
4
5
private void fillAreaForLayers(DisplayArea.Tokens leaf, DisplayArea.Tokens[] areaForLayer) {
for (int i = mMinLayer; i <= mMaxLayer; i++) {
areaForLayer[i] = leaf;
}
}

其实就是将displayAreaForLayer中从minLayer到maxLayer都改成该PA的mExisting. 也就是 displayAreaForLayer[15], displayAreaForLayer[16]为mImeContainer!

当mExisting为null时PA对应的DisplayArea的创建:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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;
}
// 注意只有leafArea的mFeature才是null的(或者root,但是root不参与DisplayArea的创建)
if (mFeature == null) {
final DisplayArea.Tokens leaf = new DisplayArea.Tokens(parent.mWmService, type,
"Leaf:" + mMinLayer + ":" + mMaxLayer);
fillAreaForLayers(leaf, areaForLayer);
return leaf;
} else {
return mFeature.mNewDisplayAreaSupplier.create(parent.mWmService, type,
mFeature.mName + ":" + mMinLayer + ":" + mMaxLayer, mFeature.mId);
}

PA的mFeature只有leafArea的mFeature才是null的(或者root,但是root不参与DisplayArea的创建):

2-10

  1. 当mFeature为null时,也就是当前PA是leafArea(即PA为TOKENS、TASK或者IME时),直接创建Tokens,然后进行displayAreaForLayer的填充
  2. 当mFeature不为null时,通过mNewDisplayAreaSupplier创建DisplayArea
    1. 当feature为FEATURE_WINDOWED_MAGNIFICATION,通过DisplayArea.Dimmable::new创建一个Dimmable
    2. 其余feature通过DisplayArea::new创建一个DisplayArea

所以遍历PA树生成DisplayArea就是将PA树除root节点之外,给每一个子PA创建一个DisplayArea.

经过这步骤之后,displayAreaForLayer[0~36]中的元素如下:

  1. displayAreaForLayer[2] = PA_TASK_2_2_tda
  2. displayAreaForLayer[15,16] = mImeContainer
  3. 其余元素皆为新建的DisplayArea.Tokens

2.5.3 RootDisplayArea.onHierarchyBuilt

1
2
3
4
5
6
7
8
9
10
11
void onHierarchyBuilt(ArrayList<Feature> features, DisplayArea.Tokens[] areaForLayer,
Map<Feature, List<DisplayArea<WindowContainer>>> featureToDisplayAreas) {
if (mHasBuiltHierarchy) {
throw new IllegalStateException("Root should only build the hierarchy once");
}
mHasBuiltHierarchy = true;
mFeatures = Collections.unmodifiableList(features);
// 注意这里将构建的displayAreaForLayer保存在了mAreaForLayer中
mAreaForLayer = areaForLayer;
mFeatureToDisplayAreas = featureToDisplayAreas;
}

很简单,仅仅是将传入的参数保存起来。并且将mHasBuiltHierarchy标记为true, 表明RootTaskDisplayArea仅仅需要构建一次层次结构。

三. 小结

Activity的Window被添加至WMS其实就是该Activity的ViewRootImpl中的W作为IWindow、存储该Activity窗口属性信息的LayoutParams以及InputChannel被传入WMS,然后生成WindowToken, 当然之后WindowToken自然是保存在DisplayContent.mTokenMap中,该map的key即对应Activity中的mToken(LocalActivityRecord)。

这里的DisplayContent是继承了RootDisplayArea, 其对应的Feature是FEATURE_ROOT,表示是该逻辑显示设备上的根显示区域。DisplayContent的初始化中做了很多工作,包括创建SurfaceControl,创建DisplayPolicy等等,重点是初始化DisplayAreaPolicy,而DisplayAreaPolicy的初始化过程中也同时构建了DisplayContent的层次结构器。

这个层次结构的初始化过程稍显复杂,可以看 HierarchyBuilder.build 上面的分析,大致分为四个部分:

  1. 第一部分 Layer的数量为37个,最上层是root, 默认DisplayArea的Feature有六个
  2. 第二部分 构造PA树
    1. 按照定义的顺序遍历feature,以便数组前面的Feature位于层次结构的顶部
    2. 依次编译该feature中定义的mWindowLayer数组,该数组由37个boolean值组成,从小到大一次对应37层layer
    3. 如果mWindowLayer[layer]的值为true, 那么根据需要创建子PA
  3. 第三部分 遍历第二部分创建的PA树,根据window类型创建对应叶子节点的PA
  4. 第四部分 为PA树的每个PA创建对应的DisplayArea,并将结果存储在RootDisplayArea(DisplayContent)中

接下来我们分析WindowToke到底是如何管理的,包括WindowState的作用,而且我们分析WindowState创建的时候发现会抛出异常,这个问题我们下篇文章分析。

欢迎关注我的其它发布渠道