【Android 13源码分析】WindowContainer窗口层级-1-初识窗口层级树

在安卓源码的设计中,将将屏幕分为了37层,不同的窗口将在不同的层级中显示。
对这一块的概念以及相关源码做了详细分析,整理出以下几篇。

【Android 13源码分析】WindowContainer窗口层级-1-初识窗口层级树

【Android 13源码分析】WindowContainer窗口层级-2-构建流程

【Android 13源码分析】WindowContainer窗口层级-3-实例分析

【Android 13源码分析】WindowContainer窗口层级-4-Surface树

当前为第一篇,主要是基础知识介绍。

打开“电话应用”,然后按音量键出现一下界面:
电话应用按音量截图
提出2个问题:

  1. 为什么音量窗口会挡住应用窗口?
  2. 为什么不管打开哪个应用都能看到导航栏和状态栏?

再看下面这个图:

音量截图屏幕分级
左边是将第一张截图的每一个窗口提出来画了的模拟图, 想象一下每个窗口其实都是全屏的,那么他的前后顺序若右图(注意颜色是对应的)。 如果说是按右边的这种层级排序,那么音量键的窗口和状态栏的窗口就会挡住Activity的窗口。

在安卓中窗口是有先后顺序的,越靠近用户的就越靠前,就能挡住底下的窗口。 目前说这么一个结论可能为时过早,后面的内容将详细介绍。

1. 基础知识介绍

1.1 三维空间概念

1.1.1 三维空间概念–游戏3D世界

下面2个图来自Unity3d官网开发文档:
在这里插入图片描述
们玩的王者荣耀,原神等游戏的开发,都是类似在这么一个3D场景下进行的,开发者将使用3D建模工具来创建地形、建筑、植被等地图元素,并通过材质和贴图来增强地图的视觉效果。

比如这张图片里放了3个颜色的柱子。 除了这些物体外,可以看到还有一个摄像机(Camera),它是用来捕捉画面的, 毕竟手机屏幕是2D的,简单来说这个摄像机能捕捉到的画面就是我们手机屏幕上显示的内容,比如这张图片捕捉到的画面是右下角的内容。

像我们玩游戏移动角色,其实就是通过转动这个摄像机(Camera)来完成的。

下面这种图会更加像一个游戏的画面一些。

在这里插入图片描述
通过看游戏地图的3D开发场景,就是希望能狗更加生动的理解到在2D的手机屏幕下,其实有一个3D的空间,虽然我们的安卓开发不会像游戏开发那么复杂,但是原理也是一样的。

1.1.2 三维空间概念–Android的三维坐标系

在这里插入图片描述
Android坐标系其实就是一个三维坐标,Z轴向上,X轴向右,Y轴向下。

经常听到的 Z-Order 也就是指窗口在Z轴的排序,离用户越近,Z值越大。并且能够遮挡住后面的窗口。

为了再加深一下安卓窗口的层级关系,下面看一张应用开发的View层级的3D视角。

注意,下面的图片是View,不是本次要讲的窗口,只是为了加深一下层级印象

在这里插入图片描述
可以看到在布局了写了37层约束布局,其实第3层加了一个按钮。 右上角我们手机屏幕上只是一个按钮的界面,但是右下角通过Android Studio自带的工具可以发现,整个View树是有很多层的(代码写了37层)。

这里看到的View层级其实和后面要讲就窗口分层,是有相似之处的。 将这里是37层想象层安卓在窗口的分层也是可以的。

1.2 ViewTree

为了后续方便理解窗口树, 先介绍一下安卓开发都知道的View树
在这里插入图片描述
XML里如图写下2个简单的红绿布局,显示的UI效果绿色的会挡住一部分红色的。但是用工具其实可以发送红色控件其实也是完成绘制的。
在这里插入图片描述
也就是说在同一层级下,在ViewGropu孩子View数组这个集合中,下标越大的View会挡住后面的View。也可以理解层 层级越靠前,就会当初后面的View。

继续增加几个View

在这里插入图片描述

  1. 控件B下新增控件D,D下面放了一个文本控件G

  2. 控件C下面新增了一个文本控件F
    在这里插入图片描述
    左边看到对View做了层级处理,这个就是ViewTree,可以将其画成树图

在这里插入图片描述
这个就是View层的View树,能构建出View树的原因是因为在代码中定义了ViewGroup这个类

# View// 父容器protected ViewParent mParent;# ViewGroup// 所有子Viewprivate View[] mChildren;public void addView(View child, int index) {......// 内部实现是通过addInArray 将View添加到数组mChildren中addView(child, index, params);}@Overridepublic void removeView(View view) {......// 本质还是从mChildren移除}

在这里插入图片描述
应用布局用的场景的几个类,他们都有一个共同的父类–ViewGroup。

ViewGroup继承了View,所以有父亲(mParent),自身内部又维护了一个View数组(mChildren)表示它的孩子们。 上有父亲下有一群孩子,所以能构建出一个ViewTree。

如果只有一个孩子,那是线性结构,因此孩子必须是多个,所以ViewGroup也是一个View的容器类。 有这么一个类的存在,开发者就可以通过嵌套来行程一个非常复杂的ViewTree结构
这也是应用开发者能写出各种丰富UI的基础。

在窗口这一级别的开发中,也有窗口树,也有对应的容器结构。 后面会详细解释窗口容器类,在介绍之前先了解一下什么是窗口。

ViewTree在代码中也是真实存在的,在Activity中通过以下代码对应ViewTree的变化做监听

    View.getViewTreeObserver().addOnGlobalLayoutListener(......)

1.3 什么是窗口

从视觉上,用户在手机屏幕上看到的“一块区域”就是一个窗口,比如前面看到的这张图,每一块都是一个窗口

在这里插入图片描述
从代码上来说,应用端的窗口指的是Window, framework层的窗口指的是WindowState
在这里插入图片描述
为什么会用不一样的类来表示“窗口”呢?

比如说有一个人,他是唯一的,身份证号是他的唯一表示,但是他在不同的系统中,保存的他的数据是不一样的,比如在公司,公司系统对这个人保存的个人基本信息,在交警系统,保存的是这个人的车辆信息和违章信息。
在这里插入图片描述
在生活中,不同系统对一个人关注保存的数据是不一样,在代码中也是一样的。应用开发者为了降低模块的依赖,也会有这种设计。

2. 初识窗口层级树

2.1 窗口容器类介绍

前面看到了一些View树构建的类,也就是我们说的常见布局,现在列举构建窗口树用的的几个容器类。

在这里插入图片描述
WindowContainer:

类似ViewGroup的存在,是窗口容器的基类,后面介绍的其他窗口容器类都是它的子类。

有泛型限制,说明容器的内容是有限制的

mParent: 保存当前容器的父窗口引用。

mChildren :保存当前窗口的所有孩子窗口容器集合(有泛型)。根据注释,列表后面的子容器,z-order 越大,离屏幕越近。

父类为ConfigurationContainer,封装了配置的处理,当前类封装了容器的操作。

在这里插入图片描述
RootWindowContainer:*

根窗口容器,也是窗口层级树的根,管理DisplayContent。
在这里插入图片描述
DisplayContent:

代表一个屏幕,Android是支持多屏幕的。

继承关系为:
DisplayContent->RootDisplayArea->DisplayArea.Dimmable->DisplayArea->WindowContainer

孩子为DisplayArea(这个规则定义在DisplayArea的子类Dimmable中)

在这里插入图片描述
WindowState:

代表一个窗口,本身也是一个容器,比如子窗口就是它的孩子(Popupwindow场景)
在这里插入图片描述
DisplayArea:
注释:DisplayContent下的窗口容器集合。

说人话:表示一块显示区域,是DisplayContent的子容器。DisplayContent下安卓目前设计为分了37层,每一层都是一个DisplayArea。

有三个直接子类,TaskDisplayArea,DisplayArea.Tokens和DisplayArea.Tokens。

在这里插入图片描述
TaskDisplayArea:

注释:孩子可以是Task,或者是TaskDisplayArea。(不过目前看到的孩子都是Task类型)。

对应层级树的第二层,专门用来存放应用的窗口图层,非常重要,APP的窗口都在这。也是窗口层级树看的重点区域。

在这里插入图片描述
DisplayArea.Token:

DisplayArea的子类,并且是其内部类,表示WindowToken的容器。WindowToken的子类是WindowState

在这里插入图片描述
DisplayArea.Dimmable:

DisplayArea的子类,并且是其内部类,DisplayContent的父类,带模糊效果。孩子是DisplayArea。

在这里插入图片描述
ImeContainer:

输入法容器,父类是DisplayArea.Token,那么孩子也是WindowToken。输入法专用

在这里插入图片描述
Task:

父类TaskFragment继承WindowContainer,泛型没有限制孩子的类型。但是实际情况下孩子是Task和ActivityRecord类型。

开发过程中经常见到的类,也是应用开发,多窗口开发经常会遇到的。

在这里插入图片描述
WindowToken:

理器中一组相关窗口的容器,是窗口的Token,而窗口的定义WindowState。一般在窗口层级树中WindowToken下面就会挂载一个WindowState。

壁纸用到的WallpaperWindowToken也是其子类。

在这里插入图片描述
ActivityRecord:

对应着一个Activity。

是WindowToken的子类,所以孩子也是WindowState。 从应用开发角度,一个Activity下也有一个Window。并且一般作为是Task的孩子。

2.2 Feature介绍

为什么有这个Feature(特征)呢?

AOSP既然将屏幕分了37层,那说明图层之间是有区别的,有不一样的特性,这个就是Feature,比如这一层是不是支持单手操作。
这里列举5个常见的Feature,已经它们所在的层级。

WindowedMagnification

拥有特征的层级: 0-31
特征描述: 支持窗口缩放的一块区域,一般是通过辅助服务进行缩小或放大

HideDisplayCutout

拥有特征的层级: 0-14 16 18-23 26-35
特征描述:隐藏剪切区域,即在默认显示设备上隐藏不规则形状的屏幕区域,比如在代码中打开这个功能后,有这个功能的图层就不会延伸到刘海屏区域。

OneHanded

拥有特征的层级:0-23 26-32 34-35
特征描述:表示支持单手操作的图层,这个功能在手机上还是挺常见的

FullscreenMagnification

拥有特征的层级:0-12 15-23 26-27 29-31 33-35
特征描述:支持全屏幕缩放的图层,和上面的不同,这个是全屏缩放,前面那个可以局部

ImePlaceholder

拥有特征的层级: 13-14
特征描述:输入法相关

在这里插入图片描述

3 WMS的层级结构树

可以通过以下命令来看获取到设备当前的层级结构树

adb shell dumpsys activity containers

在开完机后的launcher就执行了dump命令,然后就能得到下面这么一段输出,乍一看很容易劝退,但是实际上这些东西都是有规律,而且很简单。目前可以先不看,稍后再详细解释。

ACTIVITY MANAGER CONTAINERS (dumpsys activity containers)
ROOT type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#0 Display 0 name="Built-in Screen" type=undefined mode=fullscreen override-mode=fullscreen requested-bounds=[0,0][720,1600] bounds=[0,0][720,1600]#2 Leaf:36:36 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#1 WindowToken{451b2bd type=2024 android.os.BinderProxy@4526826} type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#0 47e1803 ScreenDecorOverlayBottom type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#0 WindowToken{69b9325 type=2024 android.os.BinderProxy@3a8ab1c} type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#0 799b2ab ScreenDecorOverlay type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#1 HideDisplayCutout:32:35 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#2 OneHanded:34:35 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#0 FullscreenMagnification:34:35 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#0 Leaf:34:35 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#1 FullscreenMagnification:33:33 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#0 Leaf:33:33 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#0 OneHanded:32:32 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#0 Leaf:32:32 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#0 WindowedMagnification:0:31 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#6 HideDisplayCutout:26:31 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#0 OneHanded:26:31 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#2 FullscreenMagnification:29:31 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#0 Leaf:29:31 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#1 Leaf:28:28 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#0 FullscreenMagnification:26:27 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#0 Leaf:26:27 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#5 Leaf:24:25 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#1 WindowToken{922c2bc type=2024 android.os.BinderProxy@d50168e} type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#0 48a6245 pip-dismiss-overlay type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#0 WindowToken{1a3a19a type=2019 android.os.BinderProxy@1ec36bc} type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#0 50a3d66 NavigationBar0 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#4 HideDisplayCutout:18:23 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#0 OneHanded:18:23 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#0 FullscreenMagnification:18:23 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#0 Leaf:18:23 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#3 OneHanded:17:17 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#0 FullscreenMagnification:17:17 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#0 Leaf:17:17 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#0 WindowToken{7472fe2 type=2040 android.os.BinderProxy@1bfb9c4} type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#0 4b26f73 NotificationShade type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#2 HideDisplayCutout:16:16 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#0 OneHanded:16:16 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#0 FullscreenMagnification:16:16 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#0 Leaf:16:16 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#1 OneHanded:15:15 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#0 FullscreenMagnification:15:15 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#0 Leaf:15:15 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#0 WindowToken{3da7d5c type=2000 android.os.BinderProxy@e2c682e} type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#0 7619865 StatusBar type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#0 HideDisplayCutout:0:14 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#0 OneHanded:0:14 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#1 ImePlaceholder:13:14 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#0 ImeContainer type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#0 WindowToken{1397896 type=2011 android.os.Binder@23bebb1} type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#0 d0c3c51 InputMethod type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#0 FullscreenMagnification:0:12 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#2 Leaf:3:12 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#0 WindowToken{82fa61a type=2038 android.os.BinderProxy@2f86adc} type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#0 33a873c ShellDropTarget type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#1 DefaultTaskDisplayArea type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#2 Task=1 type=home mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#0 Task=7 type=home mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#0 ActivityRecord{bd2b1d4 u0 com.android.launcher3/.uioverrides.QuickstepLauncher} t7} type=home mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#1 ae1df9b com.android.launcher3/com.android.launcher3.uioverrides.QuickstepLauncher type=home mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#0 b9fa2f0 com.android.launcher3/com.android.launcher3.uioverrides.QuickstepLauncher type=home mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#1 Task=2 type=undefined mode=fullscreen override-mode=fullscreen requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#0 Task=3 type=undefined mode=fullscreen override-mode=fullscreen requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#1 Task=6 type=undefined mode=multi-window override-mode=multi-window requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#0 Task=5 type=undefined mode=multi-window override-mode=multi-window requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#0 Leaf:0:1 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#0 WallpaperWindowToken{4b4c99a token=android.os.Binder@9258e45} type=undefined mode=fullscreen override-mode=fullscreen requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#0 f3495ce com.android.systemui.ImageWallpaper type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]

先看看点击桌面的“电话“进入电话界面后,再执行这个dump命令有什么区别

在这里插入图片描述
tips: com.google.android.dialer 这个包名是"电话"这个应用
这个区别就比较好看出来了,在#1 DefaultTaskDisplayArea下多了一些东西。看到里面也知道这个是增加一些与“电话”这个应用的Activity相关的东西。
然后再按一下音量键盘,看一下区别

在这里插入图片描述
在其他的场景比如按power出现的弹窗, 出现toast的时候,或者进入分屏都可以进行dump,对比一下差异。
现在可以有以下信息:

  1. 开完机层级结构树就存在
  2. 界面上有相关的Window的操作,都会在层级结构树上体现
  3. 不同的window会被挂在到对应的位置,这个其实就是层级结构树的关键。
    当然哪个Window应该挂在到那一层,怎么个先后顺序,这个我们其实无需过于在意,这个是产品设计。

tips: 可以试试不同Activity启动模式后的区别

3.1 简单分析层级结构树

现在来分析上面那一团输出信息怎么看。(从上到下,从左往右)

ROOT type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]#0 Display 0 name="Built-in Screen" type=undefined mode=fullscreen override-mode=fullscreen requested-bounds=[0,0][720,1600] bounds=[0,0][720,1600]

上面的ROOT 表示根节点,暂时可以忽略。下面的 #0 Display 0 name="Built-in Screen"表示当前的手机的第0个屏幕,目前也可忽略,主要是下面那一部分内容。
下面的内容虽然很长,但是我们不要看全部,抓住几个点就够了,以前面这段为例

 #2 Leaf:36:36 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][720,1600]

1. 看所在位置 #X

每一行最前面都是 “#+数字”的形式打头,比如现在看的是 “#2 Leaf:36:36”这里的数字表示这个图层在当前父容器的位置,从0开始。其实和ViewGrop或许childView的一样的。
当前这个为#2 所以他的父容器一共有3个子容器,当前这个处于第三个,也就是最上面。那么和他同级的另外2个怎么找呢?
需要往下看,找到和当前 #2 前面空格一样多的#1 和#0 就是他同级的2个容器了,按照规则能能找下面这2个与他同级的。
需要注意这里说的同级并不是在同一图层,而是在层级树这个树的结构是同级关系

#1 HideDisplayCutout:32:35
#0 WindowedMagnification:0:31

2. 层级name 名字+起始层级:结束层级
指的是 “Leaf:36:36” 这一段信息,这个的格式为“容器名 起始层级:结束层级”
体现在当前就是这个 #2 的容器叫 “Leaf”,表示一个叶子节点,比较特殊,主要看后面的频繁出现的HideDisplayCutout,ImePlaceholder,OneHanded等这些,都有具体的意义,我们知道所有的东西在源码中都能找到对应的代码, 像提到的HideDisplayCutout,ImePlaceholder,OneHanded在源码中称之为Feature(特征),即表示当前这个容器有具有这个特征,暂时知道就可以,后面会详细介绍源码对这些Feature的具体定义。
然后就是后面的起始层级:结束层级,因为虽然一共是分为37层,但是并不是说有37个Feature,比如“#1 ImePlaceholder:13:14 ” ImePlaceholder看着就是和输入法相关,那就代表着13,14都是和输入法相关的window。
android 13目前一共也只有5个Feature。

另外提一下这里的 “Leaf”代表的不是Feature,而且说当前是某个叶子节点,下面是要挂着具体Window的。

3. 看其他属性,比如type,mode

知道上面这4点基本上就能看到层级结构树的信息了,内容虽然很多,但是我们其实主要关心的还是下面“#1 DefaultTaskDisplayArea”的部分,因为这里放的才是应用相关的窗口,其他的一般都是系统窗口。像应用操作,分屏,小窗,自由窗口操作导致层级改变都体现在这一层,
另外可以留意一下如果是 WindowToken +WindowState的都是系统窗口,比如下面这种形式:

        #0 WindowToken{1397896#0 d0c3c51 InputMethod

d0c3c51 这个应该是WindowState的对象名,后面的InputMethod是具体的窗口名
而 ActivityRecord+WindowState就是应用了比如:

         #0 ActivityRecord{91c971c#0 9c20028 com.google.android.dialer

这些有个印象就行,不需要硬背,以后看的多了自然就有感觉了。

3.2 层级结构树可视化

单看层级树可能过于枯燥,在刚开始学习的时候一般都会根据信息画出一个层级结构图。
首先根据前面看dump内容的方式,先画出一部分内容如下:

在这里插入图片描述

这里是DisplayContent下的3个孩子,可以看到已经覆盖了 0-36层。
然后按照这种方式将所有的内容都画出来就可以得到下面这完整的树图:

在这里插入图片描述
强烈建议想学这一块的同学一定要手动画出这么一个图

android版本相同,画出来的图基本上都是一样的,只会根据出现不同的Window在响应的Leaf,也就是叶子节点会有不同。
这里将叶子节点的颜色涂上了,发现叶子节点下要么为空,要么就是 WindowToken,除了最底层的壁纸,其实壁纸叶子节点下的WallpaperWindowToken这个类,也是继承的WindowToken。

可能之前通过文本的形式还有点陌生,但是转换成图片后,一些常见的东西就都清楚了,比如launcher,StatusBar,NavigationBar,Wallpaper

窗口树和View树还是有差距的,View树上都是View,而窗口树上只有叶子节点上挂着窗口,其他大都都是一些容器和一些。

窗口树是固定37层的, 然后各个图层都有自己支持的Feature这些都是代码中固定好的。 实际开发中,开发中再将自己的窗口根据需求挂到对应的叶子节点上,这个和View树是有区别的。

3.3 为什么这么设计

方便管理
在写应用的时候也会这样定义,这样如果说某个业务的View无论怎么写,他就只能在自己所在的“层级”上显示,不会影响到其他。

窗口这样设计的原因可能还有其他的考虑,在远古时期窗口的顺序好像是经过规则计算的,那么这种计算很麻烦出了问题也不好定位。

现在的这种设计就很合理,符合“单一原则”,各个类型的窗口在自己的层级上,不会影响到其他。

方便功能开发
另外一个优点我认为是这种设计为小窗,分屏这种功能提供了开发的便利,因为只需要将对应的窗口移动到所在的Task就可以了。

像现在的Activity启动,在system_service进程的处理的很多逻辑都是围绕着这个层级树来做的。
比如看一眼“电话”和“短信”2个应用进行分屏操作的前后对比:

在这里插入图片描述
通过对比可以发现

  1. Task=5,6 这2个Task是分屏用到的Task,它们有共同的父亲-- Task =4。 这3个Task 在开机的时候就创建好了,默认在DefaultTaskDisplayArea孩子里是最后面。
  2. 启动分屏后其实对应窗口容器这边做了2件事
    1. 将Task=4 移到栈顶
    2. 将2个分屏应用的Task分别移到到Task5,6 下

这样分屏就完成了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/52838.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端技术(七)——less 教程

一、less简介 1. less是什么? less是一种动态样式语言,属于css预处理器的范畴,它扩展了CSS语言,增加了变量、Mixin、函数等特性,使CSS 更易维护和扩展LESS 既可以在 客户端 上运行 ,也可以借助Node.js在服…

Semaphore UI --Ansible webui

1、安装python python下载地址 https://www.python.org/downloads/ 选好版本下载 wget https://www.python.org/ftp/python/3.11.9/Python-3.11.9.tar.xz安装编译工具 sudo dnf groupinstall "Development Tools"安装依赖包 dnf install bzip2-devel ncurses-deve…

IDEA 常用配置和开发插件

件市场中搜索并安装“Git Integration”插件。 一、前言 在本篇文章中我会为大家总结一些我自己常用的配置和开发插件,此外也给大家提供一个建议,可以根据自己的项目需求和个人偏好选择适合的插件。另外,IDEA 也在不断更新,可能会…

哈希表、算法

哈希表 hash: 在编程和数据结构中,"hash" 通常指的是哈希函数,它是一种算法,用于将数据(通常是字符 串)映射到一个固定大小的数字(哈希值)。哈希函数在哈希表中尤为重要…

使用vue2+axios+chart.js画折线图 ,出现 RangeError: Maximum call stack size exceeded 错误

目录 效果图 解决方案 修正要点 效果图 修改前App.vue代码&#xff1a; <template><div id"app"><canvas id"myChart"></canvas></div> </template><script> import axios from axios; import { Chart, regis…

stm32 W25Q数据存储

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、cubemx配置二、keil中文件修改与配置三、几个重要函数的说明四、DMA方式传输&#xff08;待写&#xff09;总结 前言 W25Q128 容量为128位 128/8 16 也就…

Mac 上终端使用 MySql 记录

文章目录 下载安装终端进入 MySql常用操作查看数据库选择一个数据库查看当前选择的数据库Navcat 打开提示报错参考文章 下载安装 先下载社区版的 MySql 安装的过程需要设置 root 的密码&#xff0c;这个是要进入数据库所设定的&#xff0c;所以要记住 终端进入 MySql 首先输…

Linux基础-Makefile的编写、以及编写第一个Linux程序:进度条(模拟在 方便下载的同时,更新图形化界面)

目录 一、Linux项目自动化构建工具-make/Makefile ​编辑 背景&#xff1a; makefile小技巧&#xff1a; 二、Linux第一个小程序&#xff0d;进度条 先导&#xff1a; 1.如何利用/r,fflush(stdout)来实现我们想要的效果&#xff1b; 2.写一个倒计时&#xff1a; 进度条…

智能家居环境监测系统设计(论文+源码)

1. 系统方案 系统由9个部分构成&#xff0c;分别是电源模块、烟雾传感器模块、GSM发送短信模块、报警模块、温度传感器模块、人体红外感应模块、按键设置模块、显示模块、MCU模块。各模块的作用如下&#xff1a;电源模块为系统提供电力&#xff1b;烟雾传感器模块检测烟雾浓度&…

猫狗识别大模型——基于python语言

目录 1.猫狗识别 2.数据集介绍 3.猫狗识别核心原理 4.程序思路 4.1数据文件框架 4.2 训练模型 4.3 模型使用 4.4 识别结果 5.总结 1.猫狗识别 人可以直接分辨出图片里的动物是猫还是狗&#xff0c;但是电脑不可以&#xff0c;要想让电脑也分辨出图片里的动物是猫还是小…

C++面试3

一、常用设计模式 https://blog.csdn.net/m0_71530237/article/details/141140118?spm1001.2014.3001.5501 二、死锁以及解决方式&#xff1f; 死锁&#xff1a;一种常见的并发问题&#xff0c;发生在多个进程或线程因为竞争资源而陷入相互等待的状态&#xff0c;导致这些进…

Flutter之SystemChrome全局设置

一、简介 SystemChrome作为一个全局属性&#xff0c;很像 Android 的 Application&#xff0c;功能很强大。 二、使用详解 2.1 setPreferredOrientations 设置屏幕方向 在我们日常应用中可能会需要设置横竖屏或锁定单方向屏幕等不同要求&#xff0c;通过 setPreferredOrien…

JavaScript高级——作用域和作用链

1、概念理解&#xff1a; —— 就是一块“地盘”&#xff0c;一个代码所在的区域 —— 静态的&#xff08;相对于上下文对象&#xff09;&#xff0c;在编写代码时就确定了 2、分类 ① 全局作用域 ② 函数作用域 ③ 没有块作用域&#xff08;ES6有了&#xff09; 3、作用 …

WPF利用Path自定义画头部导航条(TOP)样式

1;新建两个多值转换器&#xff0c;都有用处&#xff0c;用来动态确定PATH的X,Y州坐标的。 EndPointConverter 该转换器主要用来动态确定X轴&#xff0c;和Y轴。用于画线条的。 internal class EndPointConverter : IMultiValueConverter {public object Convert(object[] val…

GIS 中的 3D 分析

GIS 中的 3D 分析 3D 分析已成为 GIS 的一个发展趋势&#xff0c;因为它能够更好地表现现实世界。 这不仅仅是为了得到漂亮的图片。对于某些类型的问题&#xff0c;3D 分析有时是解决它们的唯一方法。 3D 数据类型的激增也推动了这一需求。例如&#xff0c;LiDAR、BIM、UAV、…

VS Code 配置 Rust-Analyzer 报错

报错信息&#xff1a; Bootstrap Error" rust-analyzer requires glibc > 2.28 in latest build. 参考了好多地方&#xff0c; https://github.com/rust-lang/rust-analyzer/issues/11558 https://blog.csdn.net/aLingYun/article/details/120923694 https://rust-anal…

C++——⼆叉搜索树

文章目录 一、 ⼆叉搜索树的概念二、⼆叉搜索树的性能分析三、⼆叉搜索树的插⼊四、⼆叉搜索树的查找五、⼆叉搜索树的删除六、二叉搜索树的有序遍历七、⼆叉搜索树的实现代码八、二叉搜索树key与key_value的应用key的应用key_value的应用key/value⼆叉搜索树代码实现 一、 ⼆叉…

C++类与对象深度解析(一):从抽象到实践的全面入门指南

文章目录 C 类与对象——详细入门指南前言1. 类的定义1.1 类定义的基本格式示例代码解释 1.2 访问限定符示例代码解释 1.3 类域示例代码解释 1.4 成员命名规范常见的命名约定&#xff1a;示例&#xff1a;拓展&#xff1a; 1.5 class与struct的默认访问权限示例&#xff1a; 2.…

搭建Windows下的Rust开发环境

【图书介绍】《Rust编程与项目实战》-CSDN博客 《Rust编程与项目实战》(朱文伟&#xff0c;李建英)【摘要 书评 试读】- 京东图书 (jd.com) Rust编程与项目实战_夏天又到了的博客-CSDN博客 2.1.1 安装vs_buildtools 在Windows系列操作系统中&#xff0c;Rust开发环境需要依…

归并排序(Merge Sort)

什么是归并排序 归并排序&#xff08;Merge Sort&#xff09;是一种经典的排序算法&#xff0c;它采用分治法&#xff08;Divide and Conquer&#xff09;策略&#xff0c;将一个大数组分为两个小数组&#xff0c;分别进行排序&#xff0c;然后将这两个已排序的小数组合并成一个…