面试被面试官WMS问的道心破碎,抓紧翻出来我多年前的笔记补补WMS基础了。

wms的层级树

使用 adb shell dumpsys activity containers
可以看到层级树结构
shell
➜ ~ adb shell
redfin:/ $ dumpsys activity containers
ACTIVITY MANAGER CONTAINERS (dumpsys activity containers)
ROOT type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#0 Display 0 name="内置屏幕" type=undefined mode=fullscreen override-mode=fullscreen requested-bounds=[0,0][1080,2340] bounds=[0,0][1080,2340]
#2 Leaf:36:36 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#1 WindowToken{4c74f5b android.os.BinderProxy@7b57e6a} type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#0 e6cb3f8 ScreenDecorOverlayBottom type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#0 WindowToken{1079adc android.os.BinderProxy@9bb34f} type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#0 39427e5 ScreenDecorOverlay type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#1 HideDisplayCutout:32:35 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#0 OneHanded:32:35 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#1 FullscreenMagnification:33:35 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#0 Leaf:33:35 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#0 Leaf:32:32 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#0 WindowedMagnification:0:31 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#6 HideDisplayCutout:26:31 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#0 OneHanded:26:31 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#2 FullscreenMagnification:29:31 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#0 Leaf:29:31 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#1 Leaf:28:28 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#0 FullscreenMagnification:26:27 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#0 Leaf:26:27 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#5 Leaf:24:25 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#1 WindowToken{9ae296d android.os.BinderProxy@3fa0d84} type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#0 71509a2 pip-dismiss-overlay type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#0 WindowToken{4aa27f4 android.os.BinderProxy@49e7b06} type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#0 f578b1d NavigationBar0 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#4 HideDisplayCutout:20:23 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#0 OneHanded:20:23 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#0 FullscreenMagnification:20:23 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#0 Leaf:20:23 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#3 OneHanded:19:19 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#0 FullscreenMagnification:19:19 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#0 Leaf:19:19 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#0 WindowToken{ec72a3e android.os.BinderProxy@8c27dc0} type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#0 3a0109f NotificationShade type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#2 HideDisplayCutout:18:18 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#0 OneHanded:18:18 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#0 FullscreenMagnification:18:18 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#0 Leaf:18:18 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#1 OneHanded:17:17 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#0 FullscreenMagnification:17:17 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#0 Leaf:17:17 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#0 WindowToken{1ff17bb android.os.BinderProxy@d208db5} type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#0 87a44d8 StatusBar type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#0 HideDisplayCutout:0:16 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#1 OneHanded:2:16 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#1 ImePlaceholder:15:16 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#0 ImeContainer type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#0 WindowToken{ec7930c android.os.Binder@48ec8e0} type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#0 f7e2abe InputMethod type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#0 FullscreenMagnification:2:14 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#1 Leaf:3:14 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#0 WindowToken{5cc70f android.os.BinderProxy@eba41ed} type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#0 9d7d070 ShellDropTarget type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#0 DefaultTaskDisplayArea type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#6 Task=1 type=home mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#0 Task=10 type=home mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#0 ActivityRecord{b80f2a6 u0 com.android.launcher3/.uioverrides.QuickstepLauncher t10} type=home mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#0 2dcc518 com.android.launcher3/com.android.launcher3.uioverrides.QuickstepLauncher type=home mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#5 Task=50 type=standard mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#0 ActivityRecord{c94e9e4 u0 com.android.dialer/.main.impl.MainActivity t50} type=standard mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#0 715cee5 com.android.dialer/com.android.dialer.main.impl.MainActivity type=standard mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#4 Task=31 type=undefined mode=fullscreen override-mode=fullscreen requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#3 Task=32 type=undefined mode=multi-window override-mode=multi-window requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#2 Task=33 type=undefined mode=multi-window override-mode=multi-window requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#1 Task=34 type=undefined mode=split-screen-primary override-mode=split-screen-primary requested-bounds=[0,0][1080,1162] bounds=[0,0][1080,1162]
#0 Task=35 type=undefined mode=split-screen-secondary override-mode=split-screen-secondary requested-bounds=[0,1190][1080,2340] bounds=[0,1190][1080,2340]
#0 OneHandedBackgroundPanel:0:1 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#0 OneHanded:0:1 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#0 FullscreenMagnification:0:1 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#0 Leaf:0:1 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#0 WallpaperWindowToken{ee59505 token=android.os.Binder@374087c} type=undefined mode=fullscreen override-mode=fullscreen requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#0 fcd2dd2 com.android.systemui.ImageWallpaper type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
怎么理解WMS的层级树呢?
说真的,我自己去看dump出来的层技树我都眼晕,尤其对不好行数的话,直接就翻白眼了。 我学习这部分是拿个纸板挡着挨个看。 最底部是0,然后#0 代表 就是那个孩子, #2 就有俩孩子 ,然后看空格找自己的孩子 很像python
Leaf
叶子。leaf 36 :36 第一个36 就是层级是36 第二个36 是到36 也就说我只占了36一层,有的34:35 就代表 要了34-35两层。
可以看出,Wallpaper
处于0-1层节点,Activity
处于DefaultTaskDisplayArea
也就是第2层,InputMethod
处于13-14层,StatusBar
处于15层,NotificationShade
处于17层,NavigationBar0
处于24-25层
值越大越往上,简单来说可以理解为0-36层的帧布局(方便理解),放置的时候往对应层级放。壁纸在最下层 然后普通activity
在上面, 状态栏就高很多了。
相关类
RootWindowContainer
: 最顶层的管理者,根窗口容器,树的根是它。通过它遍历寻找,可以找到窗口树上的窗口。它的孩子是DisplayContent。 给可以直接持有窗口的自己或它的孩子定义了一些公共的方法和属性,像RootWindowContainer
、DisplayContent
、DisplayArea
、DisplayArea.Tokens
、TaskDisplayArea
、Task
、ActivityRecord
、WindowToken
、WindowState
都是直接或间接的继承该类。DisplayContent
: 该类是对应着显示屏幕的,Android是支持多屏幕的,所以可能存在多个DisplayContent
对象。上图只画了一个对象的结构,其他对象的结构也是和画的对象的结构是相似的。DisplayArea
: 该类是对应着显示屏幕下面的,代表一组窗口合集,具有多个子类,如Tokens,TaskDisplayArea等TaskDisplayArea
:它为DisplayContent
的孩子,对应着窗口层次的第2层。第2层作为应用层,看它的定义:int APPLICATION_LAYER = 2
,应用层的窗口是处于第2层。TaskDisplayArea
的孩子是Task
类,其实它的孩子类型也可以是TaskDisplayArea
。而Task的孩子则可以是ActivityRecord
,也可以是Task
。Tokens
:代表专门包含WindowTokens
的容器,它的孩子是WindowToken
,而WindowToken
的孩子则为WindowState
对象。WindowState
是对应着一个窗口的。ImeContainer
:它是输入法窗口的容器,它的孩子是WindowToken类型。WindowToken
的孩子为WindowState
类型,而WindowState
类型则对应着输入法窗口。Task
: 代表着一个节点,就是咱们说的栈。它的孩子可以是Task
,也可以是ActivityRecord
类型。ActivityRecord
: :是对应着应用进程中的Activity
的。ActivityRecord
是继承WindowToken
的,它的孩子类型为WindowState
。WindowState
:WindowState
是对应着一个窗口的.
比如 如下 FullscreenMagnification
是2-14
但是#1站了3:14,那 DefaultTaskDisplayArea
就剩下 2了,
这里好奇比如有些 FullscreenMagnification 为什么会占据 2-14 层 是因为他作用主要是放大镜,拖动的时候 可能会在需要方法的层级进行显示,所以需要跨层。(我特地查的)
shell
#0 FullscreenMagnification:2:14 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#1 Leaf:3:14 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#0 WindowToken{39faa68 android.os.BinderProxy@2588e77} type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#0 768e27c ShellDropTarget type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#0 DefaultTaskDisplayArea type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
#8 Task=1 type=home mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1080,2340]
js
//个人理解基类 可以找到父亲 也知道自己儿子
//子节点的list顺序代表就是z轴的层级显示顺序,list尾巴在比list的头的z轴层级要高。
class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<E>
implements Comparable<WindowContainer>, Animatable, SurfaceFreezer.Freezable {
private WindowContainer<WindowContainer> mParent = null;
protected final WindowList<E> mChildren = new WindowList<E>();
}
//这个是根节点, DisplayContent 可以理解为屏幕 DisplayListener 负责监听屏幕的添加和移除 和变化
class RootWindowContainer extends WindowContainer<DisplayContent>
implements DisplayManager.DisplayListener {}
层级构建的原理
java
private final SurfaceSession mSession = new SurfaceSession();
DisplayContent(Display display, RootWindowContainer root) {
//省略
//构建 SurafaceControl
final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(mSession)
.setOpaque(true)
.setContainerLayer()
.setCallsite("DisplayContent");
mSurfaceControl = b.setName("Root").setContainerLayer().build();
//instantiate 会调用到默认实现 DefaultProvider 在 DisplayAreaPolicy里面
//前面说倒 TaskDisplayArea:它为DisplayContent的孩子,对应着窗口层次的第2层。
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;
}
instantiate
的作用
java
//services/core/java/com/android/server/wm/DisplayAreaPolicy.java
static final class DefaultProvider implements DisplayAreaPolicy.Provider {
@Override
public DisplayAreaPolicy instantiate(WindowManagerService wmService,
DisplayContent content, RootDisplayArea root,
DisplayArea.Tokens imeContainer) {
//先构建了一个 叫DefaultTaskDisplayArea 我们再dumpus 里面也能找到对应层级
final TaskDisplayArea defaultTaskDisplayArea = new TaskDisplayArea(content, wmService,
"DefaultTaskDisplayArea", FEATURE_DEFAULT_TASK_CONTAINER);
//构建了一个集合
final List<TaskDisplayArea> tdaList = new ArrayList<>();
//添加默认的defaultTaskDisplayArea 也就吧 DefaultTaskDisplayArea 加进去了
tdaList.add(defaultTaskDisplayArea);
//构建一个 HierarchyBuilder
final HierarchyBuilder rootHierarchy = new HierarchyBuilder(root);
rootHierarchy.setImeContainer(imeContainer).setTaskDisplayAreas(tdaList);
//是否信任
if (content.isTrusted()) {
//看下这里面构建了什么
configureTrustedHierarchyBuilder(rootHierarchy, wmService, content);
}
//把添加完 层级设置为root
return new DisplayAreaPolicyBuilder().setRootHierarchy(rootHierarchy).build(wmService);
}}
java
private void configureTrustedHierarchyBuilder(HierarchyBuilder rootHierarchy,
WindowManagerService wmService, DisplayContent content) {
//构建了WindowedMagnification 0:31
rootHierarchy.addFeature(new Feature.Builder(wmService.mPolicy, "WindowedMagnification",
FEATURE_WINDOWED_MAGNIFICATION)
.upTo(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY)
.except(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY)
.setNewDisplayAreaSupplier(DisplayArea.Dimmable::new)
.build());
//如果是默认屏幕
if (content.isDefaultDisplay) {
//构建 HideDisplayCutout
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())
//添加 OneHandedBackgroundPanel 0:1
.addFeature(new Feature.Builder(wmService.mPolicy,
"OneHandedBackgroundPanel",
FEATURE_ONE_HANDED_BACKGROUND_PANEL)
.upTo(TYPE_WALLPAPER)
.build())
//添加 OneHanded
.addFeature(new Feature.Builder(wmService.mPolicy, "OneHanded",
FEATURE_ONE_HANDED)
.all()
.except(TYPE_NAVIGATION_BAR, TYPE_NAVIGATION_BAR_PANEL)
.build());
}
rootHierarchy
//添加FullscreenMagnification
.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())
//添加 ImePlaceholder 15:16
.addFeature(new Feature.Builder(wmService.mPolicy, "ImePlaceholder",
FEATURE_IME_PLACEHOLDER)
.and(TYPE_INPUT_METHOD, TYPE_INPUT_METHOD_DIALOG)
.build());
}
HierarchyBuilder addFeature(DisplayAreaPolicyBuilder.Feature feature) {
mFeatures.add(feature);
return this;
}
总结:添加了 一堆Feature
进了 rootHierarchy
的 mFeatures
里面。分别是
-
WindowedMagnification
-
HideDisplayCutout
-
OneHandedBackgroundPanel
-
OneHanded
-
FullscreenMagnification
-
ImePlaceholder
合计6个
configureTrustedHierarchyBuilder
方法里我们看到添加了我们很眼熟的 WindowedMagnification
,ImePlaceholder
等 但是我们看到如all()
,except()
等方法,看下对应方法
java
//services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
default int getMaxWindowLayer() {
return 36;
}
Builder(WindowManagerPolicy policy, String name, int id) {
mPolicy = policy;
mName = name;
mId = id;
mLayers = new boolean[mPolicy.getMaxWindowLayer() + 1];
//all 就是0-36全都是true
Builder all() {
Arrays.fill(mLayers, true);
return this;
}
//and 就是 传入的这些为true
Builder and(int... types) {
for (int i = 0; i < types.length; i++) {
int type = types[i];
set(type, true);
}
return this;
}
//except这些我不要
Builder except(int... types) {
for (int i = 0; i < types.length; i++) {
int type = types[i];
set(type, false);
}
return this;
}
//这个比较麻烦 我们需要追下代码 -> getWindowLayerFromTypeLw()
//看完 getWindowLayerFromTypeLw 我们理解
//我们传入的 返回的层级之前都得是true 然后我是true
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;
}
//set 最后也会调用到getWindowLayerFromTypeLw
private void set(int type, boolean value) {
mLayers[layerFromType(type, true)] = value;
if (type == TYPE_APPLICATION_OVERLAY) {
mLayers[layerFromType(type, true)] = value;
mLayers[layerFromType(TYPE_SYSTEM_ALERT, false)] = value;
mLayers[layerFromType(TYPE_SYSTEM_OVERLAY, false)] = value;
mLayers[layerFromType(TYPE_SYSTEM_ERROR, false)] = value;
}
}
default int getWindowLayerFromTypeLw(int type, boolean canAddInternalSystemWindow,
boolean roundedCornerOverlay) {
//我们传入了false 肯定不走这个
if (roundedCornerOverlay && canAddInternalSystemWindow) {
return getMaxWindowLayer();
}
//大于1 小于 99 返回 APPLICATION_LAYER 为2
if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {
return APPLICATION_LAYER;
}
switch (type) {
case TYPE_WALLPAPER:
return 1;
case TYPE_PRESENTATION:
case TYPE_PRIVATE_PRESENTATION:
case TYPE_DOCK_DIVIDER:
case TYPE_QS_DIALOG:
case TYPE_PHONE:
return 3;
case TYPE_SEARCH_BAR:
case TYPE_VOICE_INTERACTION_STARTING:
return 4;
case TYPE_VOICE_INTERACTION:
return 5;
case TYPE_INPUT_CONSUMER:
return 6;
case TYPE_SYSTEM_DIALOG:
return 7;
case TYPE_TOAST:
return 8;
case TYPE_PRIORITY_PHONE:
return 9;
case TYPE_SYSTEM_ALERT:
return canAddInternalSystemWindow ? 13 : 10;
case TYPE_APPLICATION_OVERLAY:
return 12;
case TYPE_INPUT_METHOD:
return 15;
case TYPE_INPUT_METHOD_DIALOG:
return 16;
case TYPE_STATUS_BAR:
return 17;
case TYPE_STATUS_BAR_ADDITIONAL:
return 18;
case TYPE_NOTIFICATION_SHADE:
return 19;
case TYPE_STATUS_BAR_SUB_PANEL:
return 20;
case TYPE_KEYGUARD_DIALOG:
return 21;
case TYPE_VOLUME_OVERLAY:
return 22;
case TYPE_SYSTEM_OVERLAY:
return canAddInternalSystemWindow ? 23 : 11;
case TYPE_NAVIGATION_BAR:
return 24;
case TYPE_NAVIGATION_BAR_PANEL:
return 25;
case TYPE_SCREENSHOT:
return 26;
case TYPE_SYSTEM_ERROR:
return canAddInternalSystemWindow ? 27 : 10;
case TYPE_MAGNIFICATION_OVERLAY:
return 28;
case TYPE_DISPLAY_OVERLAY:
return 29;
case TYPE_DRAG:
return 30;
case TYPE_ACCESSIBILITY_OVERLAY:
return 31;
case TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY:
return 32;
case TYPE_SECURE_SYSTEM_OVERLAY:
return 33;
case TYPE_BOOT_PROGRESS:
return 34;
case TYPE_POINTER:
return 35;
default:
Slog.e("WindowManager", "Unknown window type: " + type);
return 3;
}
}
//在构建的时候 第36 默认是被占据的
private boolean mExcludeRoundedCorner = true;
Feature build() {
if (mExcludeRoundedCorner) {
mLayers[mPolicy.getMaxWindowLayer()] = false;
}
//主要这里把 mLayers 赋值给了 mWindowLayers
return new Feature(mName, mId, mLayers.clone(), mNewDisplayAreaSupplier);
}
理解完这些 我们回看
java
/**
upTo(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY) 返回了32
除了 getWindowLayerFromTypeLw 里查到 TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY 是 32 所以32就不支持
所以 WindowedMagnification占据了 0-31
**/
rootHierarchy.addFeature(new Feature.Builder(wmService.mPolicy, "WindowedMagnification",
FEATURE_WINDOWED_MAGNIFICATION)
.upTo(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY)
.except(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY)
.setNewDisplayAreaSupplier(DisplayArea.Dimmable::new)
.build());
//看下 OneHanded
/*
*
首先 0-36我都要, getWindowLayerFromTypeLw 查一下 24和 25 我不要
我们在dump 出来的数据里也可以看到 OneHanded 没有24和25 但是为什么没有36 我不都全都要了么
因为build里面 36是false
**/
ddFeature(new Feature.Builder(wmService.mPolicy, "OneHanded",
FEATURE_ONE_HANDED)
.all()
.except(TYPE_NAVIGATION_BAR, TYPE_NAVIGATION_BAR_PANEL)
.build());
/**
在看下 HideDisplayCutout
首先 首先 0-36我都要 其实 我不要 24 25 19 17
所以他的层级是 0-16 ,18,20-23, 26-31,32-35 在日志中可以查看到
**/
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())
我们知道添加了这些层级 那么他们是怎么排布出来的
DisplayAreaPolicyBuilder().build();
到这步代码的时候我们已经知道 rootHierarchy
有一堆Future
但是只是添加进去了,还没有排序
js
//services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
private final ArrayList<HierarchyBuilder> mDisplayAreaGroupHierarchyBuilders =
new ArrayList<>();
Result build(WindowManagerService wmService) {
validate();
//mRootHierarchyBuilder就是我们添加了一堆Future 是HierarchyBuilder
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);
}
// 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);
}
看下 HierarchyBuilder
的build
js
//https://blog.csdn.net/qq_34211365/article/details/122349862
private void build(@Nullable List<HierarchyBuilder> displayAreaGroupHierarchyBuilders) {
final WindowManagerPolicy policy = mRoot.mWmService.mPolicy;
//前面说过 //36+1 =37
final int maxWindowLayerCount = policy.getMaxWindowLayer() + 1;
//搞个数组长度37
final DisplayArea.Tokens[] displayAreaForLayer =
new DisplayArea.Tokens[maxWindowLayerCount];
//搞个map 为上面的几个大爷构建做好准备
final Map<Feature, List<DisplayArea<WindowContainer>>> featureAreas =
new ArrayMap<>(mFeatures.size());
//configureTrustedHierarchyBuilder 里mFeatures 里面添加了 等 遍历到到集合里
for (int i = 0; i < mFeatures.size(); i++) {//每个Feature下面都挂了个集合
featureAreas.put(mFeatures.get(i), new ArrayList<>());
}
//搞个 PendingArea 数组 37长度 PendingArea 可以理解为链表那种我知道自己父亲和儿子是谁
PendingArea[] areaForLayer = new PendingArea[maxWindowLayerCount];
/**
PendingArea(Feature feature, int minLayer, PendingArea parent) {
mMinLayer = minLayer;//最低层级
mFeature = feature;//不解释
mParent = parent;//父亲
}**/
//搞个空的 给所有的future 当root节点用
final PendingArea root = new PendingArea(null, 0, null);
//当前塞满了root
Arrays.fill(areaForLayer, root);
//获取mFeatures的长度
final int size = mFeatures.size();
//写到这里了
for (int i = 0; i < size; i++) {
//遍历
final Feature feature = mFeatures.get(i);
PendingArea featureArea = null;
//37 因为小于其实是0-36
for (int layer = 0; layer < maxWindowLayerCount; layer++) {
//上面构建的时候 如果 对应的feature 包含对应层级则为true 比如0-18, 20-21 则19是false 其他是true
if (feature.mWindowLayers[layer]) {
//第一次肯定为null, 如果他的父亲 不等于 当前节点
if (featureArea == null || featureArea.mParent != areaForLayer[layer]) {
//构建一个 PendingArea 父亲是areaForLayer 的当前areaForLayer[layer] 当其实null
featureArea = new PendingArea(feature, layer, areaForLayer[layer]);
// 当前位置的mChildren 添加 该 PendingArea
areaForLayer[layer].mChildren.add(featureArea);
}
//将featureArea 放到该位置
areaForLayer[layer] = featureArea;
} else {
featureArea = null;
}
}
}
/****
PendingArea(Feature feature, int minLayer, PendingArea parent) {
mMinLayer = minLayer;
mFeature = feature;
mParent = parent;
}
到这里是怎么回事呢, android 里面每一层都会挂满叶子 Leaf , 我们在构建的时候 先添加的WindowedMagnification 所以他在mFeatures的前面,我们回看一下 mFeatures 里面一共加了6个Features , 进入layer 这个循环的时候, WindowedMagnification是0-31,那么areaForLayer 这个36长度的数组,第0位就被创建了一个 PendingArea 0 并塞入进去了,然后
现在的情况就是 areaForLayer[0]-->PendingArea0 { 父亲是root , 层级是0, feature 是 WindowedMagnification}
然后将areaForLayer 当前0 位置的 mChildren 将 该PendingArea 加进去
然后areaForLayer 的第0 为 就是 PendingArea 0
第一圈结束 就相当于 然后areaForLayer[0] == PendingArea0 { 父亲是root , 层级是0, feature 是 WindowedMagnification}
依次类推
直到32 不符合了 那这里areaForLayer[32]=null 到36都是null
比如 到了 在看下 HideDisplayCutout
0-36我都要 不要 24 25 19 17
第0 个 我要, 然后构建 PendingArea 但是 areaForLayer 现在第0位置已经塞进去 一个了,是 WindowedMagnification 那一圈赛进去的, 这里的节点 就是
上次 存进去的 , 加入到 该节点的 mChildren 里面然后将 areaForLayer[layer] 换位HideDisplayCutout 新构建的节点
这么跑到32就导致 所有节点虽然都变更了,但是 自己的父节点都是上次的节点
到33了 新节点 没被WindowedMagnification 占据 ,那么最高层的节点就归属于HideDisplayCutout
可以理解为先到先得,晚的都得认上次站坑的人 当爹
**/
PendingArea leafArea = null;
int leafType = LEAF_TYPE_TOKENS;//设置为token
//这里主要是挂载 在每个节点
for (int layer = 0; layer < maxWindowLayerCount; layer++) {
int type = typeOfLayer(policy, layer);//要么容器 要么叶子类型 除了输入法和app 都返回token类型
// 符合三种条件都会在对应角标下挂载新的节点,大体理解就是该节点哪怕被其他类型站了 ,我就挂载在他下面,
if (leafArea == null || leafArea.mParent != areaForLayer[layer]
|| type != leafType) {
// 创建 PendingArea 标识不是feature 将父节点 也就是标feature加入
//这里会给每一个节点上面都挂一个 LEAF_TYPE_TOKENS类型的节点
leafArea = new PendingArea(null /* feature */, layer, areaForLayer[layer]);
areaForLayer[layer].mChildren.add(leafArea);//加入feature下
leafType = type;
//但是如果是输入法和app 就会特殊处理一下
if (leafType == LEAF_TYPE_TASK_CONTAINERS) {
//areaForLayer[layer] 就是父节点
addTaskDisplayAreasToApplicationLayer(areaForLayer[layer]);//针对app节点做了处理 会直接挂载到DefaultTaskDisplayArea下面
addDisplayAreaGroupsToApplicationLayer(areaForLayer[layer],
displayAreaGroupHierarchyBuilders);
leafArea.mSkipTokens = true;
} else if (leafType == LEAF_TYPE_IME_CONTAINERS) {
leafArea.mExisting = mImeContainer;//针对输入做了特殊处理 DisplayArea.Tokens
leafArea.mSkipTokens = true;//他俩都会mSkipTokens=true
}
}
leafArea.mMaxLayer = layer;
}
root.computeMaxLayer();
//到这里才将displayAreaForLayer 挂载到 根节点 也就是我们看到的root
//在该方法内部对根据level 进行了重新排序
root.instantiateChildren(mRoot, displayAreaForLayer, 0, featureAreas);
//添加完成通知可以使用
mRoot.onHierarchyBuilt(mFeatures, displayAreaForLayer, featureAreas);
}
//根据类型返回 对应参数 app 和输入法都返回 LEAF_TYPE_IME_CONTAINERS 其他为token
private static int typeOfLayer(WindowManagerPolicy policy, int layer) {
if (layer == APPLICATION_LAYER) {//app
return LEAF_TYPE_TASK_CONTAINERS;
}
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;
}
}
//添加app 都会会挂载到 DefaultTaskDisplayArea 下
private void addTaskDisplayAreasToApplicationLayer(PendingArea parentPendingArea) {
final int count = mTaskDisplayAreas.size();
for (int i = 0; i < count; i++) {
PendingArea leafArea =
new PendingArea(null /* feature */, APPLICATION_LAYER, parentPendingArea);
leafArea.mExisting = mTaskDisplayAreas.get(i);// DefaultTaskDisplayArea->TaskDisplayArea
leafArea.mMaxLayer = APPLICATION_LAYER;
parentPendingArea.mChildren.add(leafArea);
}
}
private void addDisplayAreaGroupsToApplicationLayer(
DisplayAreaPolicyBuilder.PendingArea parentPendingArea,
@Nullable List<HierarchyBuilder> displayAreaGroupHierarchyBuilders) {
//因为displayAreaGroupHierarchyBuilders 我看了半天没发现赋值的地方是个null 就直接返回了
if (displayAreaGroupHierarchyBuilders == null) {
return;
}
final int count = displayAreaGroupHierarchyBuilders.size();
for (int i = 0; i < count; i++) {
DisplayAreaPolicyBuilder.PendingArea
leafArea = new DisplayAreaPolicyBuilder.PendingArea(
null /* feature */, APPLICATION_LAYER, parentPendingArea);
leafArea.mExisting = displayAreaGroupHierarchyBuilders.get(i).mRoot;
leafArea.mMaxLayer = APPLICATION_LAYER;//这是2
parentPendingArea.mChildren.add(leafArea);
}
}
看下instantiateChildren
做了什么
java
// 参数分别为 mRoot, displayAreaForLayer, 0, featureAreas
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);
//调用createArea 先去看下
final DisplayArea area = child.createArea(parent, areaForLayer);
if (area == null) {
//如果是null 就继续转
continue;
}
//root 添加 添加到 mChildren 并且 设置父类为root 这里如果是输入法 就是 ImeContainer
//这个addChild的时候 会有各自的实现 父类是WindowContainer,子类task和TaskDisplayear 有各自的实现
parent.addChild(area, WindowContainer.POSITION_TOP);
if (child.mFeature != null) {
areas.get(child.mFeature).add(area);
}
//子节点也去遍历挂载
child.instantiateChildren(area, areaForLayer, level + 1, areas);
}
}
js
@Nullable
private DisplayArea createArea(DisplayArea<DisplayArea> parent,
DisplayArea.Tokens[] areaForLayer) {
if (mExisting != null) {//输入法和app 不是null 输入法是DisplayArea.Tokens类型
if (mExisting.asTokens() != null) {//输入法是token 进入
fillAreaForLayers(mExisting.asTokens(), areaForLayer);
}
//app 返回 DefaultTaskDisplayArea 输入法返回 ImeContainer
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;
}
if (mFeature == null) {
//如果没有层级,默认就是叶子Leaf +层级 现在看只有36:36是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);
}
}
js
//services/core/java/com/android/server/wm/TaskDisplayArea.java
void addChild(WindowContainer child, int position) {
if (child.asTaskDisplayArea() != null) {
super.addChild(child, position);
} else if (child.asTask() != null) {
addChildTask(child.asTask(), position);
} else {
throw new IllegalArgumentException(
"TaskDisplayArea can only add Task and TaskDisplayArea, but found "
+ child);
}
}
最终app类都会挂载到 DefaultTaskDisplayArea
下 , 输入类都挂载到 mImeContainer
下。
第一个循环 挂载的时候 ,保证了0-36层 挨个都挂载上对应的 feature
, 比如WindowedMagnification 是0-31 那么转他的时候,areaForLayer
的0-31都是WindowedMagnification
然后剩下的是 32-35 是 HideDisplayCutout ,但是HideDisplayCutout
人 1-31的也要,进循环里判断 创建一个 新的 节点 父亲变为WindowedMagnification
,然后将 HideDisplayCutout
放在1-31上,但是已经形成了一个链表,这么转圈下来,6个层级跑完,虽然最后的已经变了,但是 每个节点都找到了自己的对应位置。你可以理解为 按照最新的6个层级, 最后添加的层级 如果需要该层级,则会挂载在最下面,他的父亲是上个占据该层级的Future. 第二个循环比较好理解, 比如 FullscreenMagnification
(F1) 占 1-12 ImePlaceholder (F2)占据了13-14 他们挂在OneHanded (F3)1-14下
那么第一次leafArea
是null
, 进来 在 0位置 下面挂一个叶子节点 ,该节点父亲是F1 , 第二次进来 不是null, 父亲一样,类别一样,那么就不用挂在 直到循环到F2的时候,相当于 F1 下面挂了个Lefa
,F2 下面也挂了一个Leaf
。 然后如果是输入法或者app
则这里在mExisting
和 mSkipTokens
做了处理 然后取出最大的层级 35 调用 instantiateChildren
的时候 一层层的 将组装好的数据,一层层从0到36 罗列下来, 在 createArea
中 ,输入法和app 不会创建叶子,会直接挂载到对应的层级上, 其他类型会创建一个 DisplayArea
挂载上去。有名字的会将名字和层级挂载上去,没名字的 就叫Leaf
.
其他应用挂载之前必须构建一个windowToken
,都挂载在token
下。
总结
层级树的加载逻辑代码,筒子们不要记也不要费心看,我看这部分代码的时候,感觉脑子都不够用了(主要我脑子内存小)。 大体上知道,对应类型的window 会挂载到对应的层级上,实在感觉理解难受的,就理解为0-36层帧布局, 为了保证从上到下看起来没问题,对应的会挂载到对应的帧布局上。 问你就说 Activity
的对应Task
挂载在 DefaultTaskDisplayArea
下就可以了。 然后摸着不存在的胡子,微笑看着对象,表现出一种这种简单问题还问我?装个大大的B。