frameworks 之 WMS层级树

devtools/2024/12/28 7:14:50/

frameworks 之 WMS层级树

  • 1. 容器类
    • 1. WindowContainer
    • 2. RootWindowContainer
    • 3. DisplayContent
    • 4. DisplayAreas
      • 4. 1 DisplayArea.Tokens
      • 4. 2 TaskDisplayArea
      • 4. 3 DisplayArea.Dimmable
    • 5. WindowToken
      • 5.1 WallpaperWindowToken
      • 5.2 ActivityRecord
    • 6. TaskFragment
      • 6.1 Task
    • 7 WindowState
  • 2. 层级树构建
    • 2.1 层级命令
    • 2.2 构建入口
    • 2.3 构建 DisplayContent
    • 2.4 构建对应屏幕的层级树入口
    • 2.5 层级树构建
      • 2.5.1 构建特征
      • 2.5.1 构建层级树

android 系统 为了方便各个层级的view管理。创建了对应的层级树。
涉及到的类如下

  • frameworks/base/services/core/java/com/android/server/wm/ActivityTaskManagerService.java
  • frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java
  • frameworks/base/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
  • frameworks/base/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java
  • frameworks/base/services/core/java/com/android/server/policy/WindowManagerPolicy.java
  • frameworks/base/core/java/android/window/DisplayAreaOrganizer.java

1. 容器类

1. WindowContainer

android 所有的容器类最终父类都都为 WindowContainer ,负责对view的添加和移除。他的子类通过泛型指定,各个子类重写一些的方法,实现对应的特有逻辑。一些重要属性如下

方法/属性意义
mParent父容器
mChildren添加到该容器子容器。列表的顺序也就是子容器出现在屏幕上的顺序,最顶层的子容器位于队尾
addChild添加子容器

2. RootWindowContainer

根窗口容器,树的开始节点。通过它遍历寻找,可以找到窗口树上的窗口。它的孩子是DisplayContent

public class RootWindowContainer extends WindowContainer<DisplayContent>

3. DisplayContent

该容器作用是对应着显示屏幕的,Android是支持多屏幕的,所以可能存在多个DisplayContent对象。
通过 【View】-【Tool Windows】-【Hierarchy】 , ctrl + h 打开面板 可以看到对应继承关系
在这里插入图片描述

4. DisplayAreas

DisplayArea由 DisplayAreaPolicy 管理,能够复写Configuration和被绑定到leash上。
DisplayArea可以包含嵌套的DisplayArea。
DisplayAreas有三种风格,以确保窗口具有正确的Z顺序:

BELOW_TASKS:只能包含位于任务下方的BELLOW_TASK显示区域和WindowToken。
ABOVE_TASKS:只能包含位于任务上方的ABOVE_TASK显示区域和WindowToken。
ANY:可以包含任何类型的DisplayArea,以及任何类型的WindowToken或Task容器。
@param<T>DisplayArea的子项的类型。

DisplayArea有三个直接子类,TaskDisplayArea,DisplayArea.Tokens和DisplayArea.Dimmable
在这里插入图片描述

4. 1 DisplayArea.Tokens

Tokens 负责装载 WindowToken 容器。他还有子类为 ImeContainer。所以他的容器也为tokens。

public static class Tokens extends DisplayArea<WindowToken>

在这里插入图片描述

4. 2 TaskDisplayArea

TaskDisplayArea代表了屏幕上的一个包含App类型的WindowContainer的区域。它的子节点可以是Task,或者是TaskDisplayArea。TaskDisplayArea为DisplayContent的孩子,对应着窗口层次的第2层。第2层作为应用层,看它的定义:int APPLICATION_LAYER = 2。DefaultTaskDisplay是TaskDisplayArea的别名

final class TaskDisplayArea extends DisplayArea<WindowContainer> 

在这里插入图片描述

4. 3 DisplayArea.Dimmable

Dimmable是DisplayArea的内部类,该容器添加模糊效果,并且Dimmable也是一个DisplayArea类型的DisplayArea容器。
可以通过Dimmer对象mDimmer施加模糊效果,模糊图层可以插入到以该Dimmable对象为根节点的层级结构之下的任意两个图层之间。
且它有一个直接子类,RootDisplayArea

static class Dimmable extends DisplayArea<DisplayArea> {

5. WindowToken

:窗口管理器中一组相关窗口的容器。这通常是一个AppWindowToken,它是用于显示窗口的“活动”的句柄。对于嵌套窗口,会为父窗口创建一个WindowToken来管理其子窗口。
总而言之就是用WindowToken来管理WindowState他的管理的容器是 winowState,管理他的容器是 Tokens。他的子类有ActivityRecord, wallpaperwindowToken

class WindowToken extends WindowContainer<WindowState> {

在这里插入图片描述

5.1 WallpaperWindowToken

WallpaperWindowToken继承WindowToken,是用来存放和Wallpaper相关的窗口

5.2 ActivityRecord

ActivityRecord是WindowToken的子类,在WMS中一个ActivityRecord对象就代表一个Activity对象

6. TaskFragment

TaskFragment继承WindowContainer,一个基本容器,可用于包含Activity或其他TaskFragment,管理Activity生命周期并更新其中活动的可见性
从层级结构角度来说,与Activity嵌入(Activity Embedding)、平行视界等场景有关。平行视界的分屏模式时,两个Activity将同时显示,而Task本身不提供这个能力,而是由TaskFragment来实现。TaskFragment插入到Task和Activity之间,分割了Task在屏幕上的显示区域,提供给平行视界的两个Activity。
在这里插入图片描述

6.1 Task

Task的孩子可以是Task,也可以是ActivityRecord类型。是一个TaskFragment,它可以包含一组执行特定作业的Activity。具有相同任务相似性的Activity通常在同一任务中分组。任务也可以是显示在用户交互的作业的最近屏幕中的实体。任务还可以包含其他任务。
这些Activity可以是来自同一个App,也可以是来自不同的Apps,Activity之间不一定非得相关联。当我们按home键旁边那个方形键(recent-apps)时,屏幕上展示的就是一个个Task

7 WindowState

作为最底层的容器,一个WindowState对象就代表了一个窗口,其继承WindowContainer,这就说明WindowState同样可以作为其他窗口的父容器,例如我们常见的PopupWindow
在这里插入图片描述
最终显示的对应容器大概关系如下图
在这里插入图片描述

2. 层级树构建

2.1 层级命令

如何查看当前系统的层级,可以用 如下命令获取。#号前面数字表示该层级有多少个,启始从0开始。每缩进代表归属的下个层级。

adb shell dumpsys activity containers

在这里插入图片描述

2.2 构建入口

我们可以从dump出来的日志搜索关键字DefaultTaskDisplayArea 查找入口,或者我们从上面知道最顶层为 RootWindowContainer 查找该类添加入口。

grep "\"DefaultTaskDisplayArea" ./ -rn

找到后一直往上推或在在该位置打印日志堆栈可以知道。构建时机为 startOtherServices 里面的 AMS 的 setWindowManager 方法。

frameworks/base/services/java/com/android/server/SystemServer.java
mActivityManagerService.setWindowManager(wm);

AMS 会获取 WMS的 rootWindowContainer 并调用 setWindowManager 方法。

	public void setWindowManager(WindowManagerService wm) {synchronized (mGlobalLock) {mWindowManager = wm;mRootWindowContainer = wm.mRoot;...mRootWindowContainer.setWindowManager(wm);}}

2.3 构建 DisplayContent

rootWindowContainer 的 setWindowManager

  1. 调用 DisplayManager 的 getDisplays 方法获取屏幕数组
  2. 遍历创建对应 DisplayContent,并通过 addChild 添加到 rootWindowContainer 中
  3. 获取默认屏幕的DefaultTaskDisplayArea
  4. 调用 getOrCreateRootHomeTask 创建home的task
	void setWindowManager(WindowManagerService wm) {mWindowManager = wm;mDisplayManager = mService.mContext.getSystemService(DisplayManager.class);mDisplayManager.registerDisplayListener(this, mService.mUiHandler);mDisplayManagerInternal = LocalServices.getService(DisplayManagerInternal.class);// 遍历每个屏幕,构造层级树 在DisplayContent的构造方法里 DefaultTaskDisplayArea 在里面添加final Display[] displays = mDisplayManager.getDisplays();for (int displayNdx = 0; displayNdx < displays.length; ++displayNdx) {final Display display = displays[displayNdx];final DisplayContent displayContent = new DisplayContent(display, this);addChild(displayContent, POSITION_BOTTOM);if (displayContent.mDisplayId == DEFAULT_DISPLAY) {mDefaultDisplay = displayContent;}}calculateDefaultMinimalSizeOfResizeableTasks();// 获取默认屏幕的 DefaultTaskDisplayAreafinal TaskDisplayArea defaultTaskDisplayArea = getDefaultTaskDisplayArea();// 创建home的taskdefaultTaskDisplayArea.getOrCreateRootHomeTask(ON_TOP);positionChildAt(POSITION_TOP, defaultTaskDisplayArea.mDisplayContent,false /* includingParents */);}

2.4 构建对应屏幕的层级树入口

DisplayContent 初始化在其构造方法的内,会开始构建对应的层级树。
构建方法 在 configureSurfaces

DisplayContent(Display display, RootWindowContainer root) {super(root.mWindowManager, "DisplayContent", FEATURE_ROOT);...// 开始创建窗口树configureSurfaces(pendingTransaction);...}

configureSurfaces 主要内容

  1. 构建对应的SurfaceControl
  2. 判断构建显示策略mDisplayAreaPolicy 是否为空,为空创建,作为第一次肯定为空,调用WMS构造方法创建好的进行返回,通过方法 DisplayAreaPolicy.Provider.fromResources 读取的是资源字符串string的config_deviceSpecificDisplayAreaPolicyProvider值是否为空,为空则返回默认的 isplayAreaPolicy.DefaultProvider 构建,不为空则反射创建
  3. 调用 instantiate 方法进行初始化
private void configureSurfaces(Transaction transaction) {final SurfaceControl.Builder b = mWmService.makeSurfaceBuilder(mSession).setOpaque(true).setContainerLayer().setCallsite("DisplayContent");mSurfaceControl = b.setName(getName()).setContainerLayer().build();if (mDisplayAreaPolicy == null) {// Setup the policy and build the display area hierarchy.// Build the hierarchy only after creating the surface so it is reparented correctly// 第一次为空,创建mDisplayAreaPolicy = mWmService.getDisplayAreaPolicyProvider().instantiate(mWmService, this /* content */, this /* root */,mImeWindowsContainer);}...}

2.5 层级树构建

instantiate 方法传进参数 DisplayContent 和 RootDisplayArea 都为this当前的对象,并传进已构建好的 imeContainer 。方法具体内容

  1. 创建对应的 TaskDisplayArea ,名称为 DefaultTaskDisplayArea
  2. 创建 TaskDisplayArea 的数组并将其添加进去
  3. 传建对应的分层构建对象 HierarchyBuilder ,传进入的root 为 当前的DisplayContent,并设置 输入法容器 和显示区域容器
  4. 调用 configureTrustedHierarchyBuilder 开始配置特征(2.5.1)
  5. 将创建好的特征放到 通过 setRootHierarchy 方法放到 DisplayAreaPolicyBuilder 对象,并调用build方法进村构建(2.5.2)
	public DisplayAreaPolicy instantiate(WindowManagerService wmService,DisplayContent content, RootDisplayArea root,DisplayArea.Tokens imeContainer) {final TaskDisplayArea defaultTaskDisplayArea = new TaskDisplayArea(content, wmService,"DefaultTaskDisplayArea", FEATURE_DEFAULT_TASK_CONTAINER);final List<TaskDisplayArea> tdaList = new ArrayList<>();tdaList.add(defaultTaskDisplayArea);// Define the features that will be supported under the root of the whole logical// display. The policy will build the DisplayArea hierarchy based on this.final HierarchyBuilder rootHierarchy = new HierarchyBuilder(root);// Set the essential containers (even if the display doesn't support IME).rootHierarchy.setImeContainer(imeContainer).setTaskDisplayAreas(tdaList);if (content.isTrusted()) {// Only trusted display can have system decorations.// 进行创建configureTrustedHierarchyBuilder(rootHierarchy, wmService, content);}// Instantiate the policy with the hierarchy defined above. This will create and attach// all the necessary DisplayAreas to the root.return new DisplayAreaPolicyBuilder().setRootHierarchy(rootHierarchy).build(wmService);}

2.5.1 构建特征

configureTrustedHierarchyBuilder 方法 不断通过 addFeature,将对应的特征加入里面的 mFeatures 数组
Feature.Builder 的类负责构建对应的内容,并通过 build 方法创建。

  1. 里面的 mLayers 数组大小根据 WindowManagerPolicy 的 getMaxWindowLayer 最大的层级获取,默认为36 。该数组值 false 表示该特征不占用这层,true则为是
  2. 调用方法Build 传建feature的时候会将最大的层级置为 false
		Feature build() {if (mExcludeRoundedCorner) {// Always put the rounded corner layer to the top most layer.// 创建的时候将最大的层级排除mLayers[mPolicy.getMaxWindowLayer()] = false;}return new Feature(mName, mId, mLayers.clone(), mNewDisplayAreaSupplier);}

涉及主要方法如下

方法意义
all将数组全部置为true,表示全部可用
and将指定的层级置为true
except将指定的层级置为false,表示不可用
upTo将从0到指定的层级置为true
set将对应的层级,设置为指定的属性,控制可用不可用

对应特征的创建代码范围标注如下

	private void configureTrustedHierarchyBuilder(HierarchyBuilder rootHierarchy,WindowManagerService wmService, DisplayContent content) {// WindowedMagnification should be on the top so that there is only one surface// to be magnified.rootHierarchy.addFeature(new Feature.Builder(wmService.mPolicy, "WindowedMagnification",FEATURE_WINDOWED_MAGNIFICATION).upTo(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY) // [0,32].except(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY) //32// Make the DA dimmable so that the magnify window also mirrors the dim layer..setNewDisplayAreaSupplier(DisplayArea.Dimmable::new).build()); // [0, 31]if (content.isDefaultDisplay) {// Only default display can have cutout.// See LocalDisplayAdapter.LocalDisplayDevice#getDisplayDeviceInfoLocked.rootHierarchy.addFeature(new Feature.Builder(wmService.mPolicy, "HideDisplayCutout",FEATURE_HIDE_DISPLAY_CUTOUT).all() // [0 ,36].except(TYPE_NAVIGATION_BAR, TYPE_NAVIGATION_BAR_PANEL, TYPE_STATUS_BAR,TYPE_NOTIFICATION_SHADE) // 24, 25, 20, 19.build()) // [0, 18] [21, 23] [26 35].addFeature(new Feature.Builder(wmService.mPolicy,"OneHandedBackgroundPanel",FEATURE_ONE_HANDED_BACKGROUND_PANEL).upTo(TYPE_WALLPAPER) //1.build()) // 1.addFeature(new Feature.Builder(wmService.mPolicy, "OneHanded",FEATURE_ONE_HANDED).all() // [0 ,36].except(TYPE_NAVIGATION_BAR, TYPE_NAVIGATION_BAR_PANEL) // 24, 25.build()); // [0, 23] [26, 35]}rootHierarchy.addFeature(new Feature.Builder(wmService.mPolicy, "FullscreenMagnification",FEATURE_FULLSCREEN_MAGNIFICATION).all() // [0 ,36].except(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, TYPE_INPUT_METHOD,TYPE_INPUT_METHOD_DIALOG, TYPE_MAGNIFICATION_OVERLAY,TYPE_NAVIGATION_BAR, TYPE_NAVIGATION_BAR_PANEL) // 32 15 16 28 25 26.build()) // [0, 14] [17 ,24] [27, 27] [29, 31] [33, 35].addFeature(new Feature.Builder(wmService.mPolicy, "ImePlaceholder",FEATURE_IME_PLACEHOLDER).and(TYPE_INPUT_METHOD, TYPE_INPUT_METHOD_DIALOG) // 15 , 16.build()); //[15, 16]}

2.5.1 构建层级树

创建完特征后会传建 DisplayAreaPolicyBuilder 并调用setRootHierarchy 传进入 HierarchyBuilder对象,在调用 build方法。最主要是调用对应 HierarchyBuilder 的buld 方法开始创建。

  1. 根据getMaxWindowLayer 最大层级数量,创建 DisplayArea.Tokens 数组
  2. 创建 featureAreas map key为Feature,value为 DisplayArea 数组。
  3. 定一个个36位数组 areaForLayer,用于存放意向 , 并将全部填充为0 即根节点
  4. 传建临时变量 featureArea用于接下来遍历判断是否和上一个层级一样,不是则创建并添加到对应的childs。避免重复添加一样的。
  5. 开始遍历对应的特征,每个特征遍历之前设置好的 windowLayers 布尔数组。如果为true代表该层级可用
	private void build(@Nullable List<HierarchyBuilder> displayAreaGroupHierarchyBuilders) {final WindowManagerPolicy policy = mRoot.mWmService.mPolicy;final int maxWindowLayerCount = policy.getMaxWindowLayer() + 1;final DisplayArea.Tokens[] displayAreaForLayer =new DisplayArea.Tokens[maxWindowLayerCount];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<>());}...// 定一个个36位数组,用于存放构建树, 先将全部填充为0 即根节点PendingArea[] areaForLayer = new PendingArea[maxWindowLayerCount];final PendingArea root = new PendingArea(null, 0, null);Arrays.fill(areaForLayer, root);// Create DisplayAreas to cover all defined features.final int size = mFeatures.size();// 遍历上面添加的对应的特征for (int i = 0; i < size; i++) {// Traverse the features with the order they are defined, so that the early defined// feature will be on the top in the hierarchy.final Feature feature = mFeatures.get(i);PendingArea featureArea = null;// 遍历每个特征里面每个层级的允许情况for (int layer = 0; layer < maxWindowLayerCount; layer++) {if (feature.mWindowLayers[layer]) {// This feature will be applied to this window layer.//// We need to find a DisplayArea for it:// We can reuse the existing one if it was created for this feature for the// previous layer AND the last feature that applied to the previous layer is// the same as the feature that applied to the current layer (so they are ok// to share the same parent DisplayArea).// 如果不为空,或在上一个的父节点和当前的每个层级的节点不一样,就要新建并放到对应的儿节点// 好比特征区间是 0到31 不会root下面添加多个该节点只会添加1个// 如果区间 是 0到31 33到35 则会添加2个,中间因为为false featureArea会被置为空// 但是数组areaForLayer从0到31都会放该特征,始终维持该数组为树的最下层数据,为下次特征遍历添加子节点if (featureArea == null || featureArea.mParent != areaForLayer[layer]) {// No suitable DisplayArea:// Create a new one under the previous area (as parent) for this layer.// 创建关联对应的父节点featureArea = new PendingArea(feature, layer, areaForLayer[layer]);// 父节点添加他areaForLayer[layer].mChildren.add(featureArea);}areaForLayer[layer] = featureArea;} else {// This feature won't be applied to this window layer. If it needs to be// applied to the next layer, we will need to create a new DisplayArea for// that.featureArea = null;}}}...}
  1. 特征添加完毕后,就开始添加叶子节点,构建临时 leafArea 用于判断。
  2. 根据最大层级树开始遍历
  3. 遍历的开始调用 typeOfLayer 区分特殊的层级,TYPE_INPUT_METHOD 和 TYPE_INPUT_METHOD_DIALOG 返回 LEAF_TYPE_IME_CONTAINERS,APPLICATION_LAYER 返回 LEAF_TYPE_TASK_CONTAINERS
	private static int typeOfLayer(WindowManagerPolicy policy, int layer) {if (layer == APPLICATION_LAYER) {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;}}
  1. 跟上面特征一样,也是通过判断 叶子和上一个层级是否一样,不一样则创建新的叶子节点并添加到 mChildren 中,特殊的是还判断了叶子的类型是否一样,不一样也传建新的
  2. 判断不一样后,在传建的时候还会判断该叶子节点属于什么类型。
    ` 如果是 应用层级 LEAF_TYPE_TASK_CONTAINERS 类型,则通过 addTaskDisplayAreasToApplicationLayer 方法,遍历显示区域,因为设置进去只有一个(DefaultTaskDisplayArea),所以则会添加一个 PendingArea 并将 DefaultTaskDisplayArea 赋值给 mExisting 属性。通过 addDisplayAreaGroupsToApplicationLayer 方法 添加 PendingArea,不过目前数组为0 所以不会添加。最后设置对应叶子节点 mSkipTokens 为 true
	// 添加多一个 TaskDisplayAreaprivate 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);leafArea.mMaxLayer = APPLICATION_LAYER;parentPendingArea.mChildren.add(leafArea);}}

如果是 输入法层级 LEAF_TYPE_IME_CONTAINERS ,则将传进入的 mImeContainer 赋值给 mSkipTokens。并mSkipTokens 为 true

private void build(@Nullable List<HierarchyBuilder> displayAreaGroupHierarchyBuilders) {...// Create Tokens as leaf for every layer.// 为每个层级添加一个叶子节点PendingArea leafArea = null;int leafType = LEAF_TYPE_TOKENS;for (int layer = 0; layer < maxWindowLayerCount; layer++) {// 对输入法和应用层级返回特殊的判断int type = typeOfLayer(policy, layer);// Check whether we can reuse the same Tokens with the previous layer. This happens// if the previous layer is the same type as the current layer AND there is no// feature that applies to only one of them.if (leafArea == null || leafArea.mParent != areaForLayer[layer]|| type != leafType) {// Create a new Tokens for this layer.leafArea = new PendingArea(null /* feature */, layer, areaForLayer[layer]);areaForLayer[layer].mChildren.add(leafArea);leafType = type;// 特殊层级会将 mSkipTokens 置为true 构建时候会跳过该对象if (leafType == LEAF_TYPE_TASK_CONTAINERS) {// We use the passed in TaskDisplayAreas for task container type of layer.// Skip creating Tokens even if there is no TDA.// 如果是应用层级会添加一个TaskDisplayArea层级addTaskDisplayAreasToApplicationLayer(areaForLayer[layer]);addDisplayAreaGroupsToApplicationLayer(areaForLayer[layer],displayAreaGroupHierarchyBuilders);leafArea.mSkipTokens = true;} else if (leafType == LEAF_TYPE_IME_CONTAINERS) {// We use the passed in ImeContainer for ime container type of layer.// Skip creating Tokens even if there is no ime container.// 如果是输入法层级直接赋值对应的 mImeContainerleafArea.mExisting = mImeContainer;leafArea.mSkipTokens = true;}}leafArea.mMaxLayer = layer;}...}
  1. 调用 computeMaxLayer 递归 获取最大层级,并保存到 mMaxLayer 变量。
  2. 调用 instantiateChildren 方法将对应 pendingArea 转化为 DisplayArea,通过遍历递归对应 mChildren。调用createArea方法创建对象,并通过 addChild 添加到 RootDisplayArea 中, 也根据特征放到对应的map 中方法一开始传进去的为 displayContent
		void instantiateChildren(DisplayArea<DisplayArea> parent, DisplayArea.Tokens[] areaForLayer,int level, Map<Feature, List<DisplayArea<WindowContainer>>> areas) {mChildren.sort(Comparator.comparingInt(pendingArea -> pendingArea.mMinLayer));for (int i = 0; i < mChildren.size(); i++) {final PendingArea child = mChildren.get(i);final DisplayArea area = child.createArea(parent, areaForLayer);if (area == null) {// TaskDisplayArea and ImeContainer can be set at different hierarchy, so it can// be null.continue;}// 创建parent.addChild(area, WindowContainer.POSITION_TOP);if (child.mFeature != null) {areas.get(child.mFeature).add(area);}// 循环递归child.instantiateChildren(area, areaForLayer, level + 1, areas);}}

createArea 方法 步骤

  1. 判断 mExisting 是否为空,并且为 tokens 则调用 fillAreaForLayers,将该属于该层级的赋值给传进来的 DisplayArea.Tokens[] areaForLayer 数组。为 ImeContainer
  2. 判断 mSkipTokens 是否为true, 之前的 应用层级 返回 null 。
  3. 判断对应的层级跟 APPLICATION_LAYER 相比。定义对应的类型。
  4. 判断 mFeature 是否为 null 。为null为叶子节点 为 DisplayArea.Tokens。 不为null 为特征节点。根据 mNewDisplayAreaSupplier 进行创建,调用的是对应的构造函数,大部分为 DisplayAreaWindowedMagnification 为 DisplayArea.Dimmable
		private DisplayArea createArea(DisplayArea<DisplayArea> parent,DisplayArea.Tokens[] areaForLayer) {// 特殊层级的赋值 mExisting 不为空if (mExisting != null) {// 输入法的不为空 走这里if (mExisting.asTokens() != null) {// Store the WindowToken container for layersfillAreaForLayers(mExisting.asTokens(), areaForLayer);}return mExisting;}// 之前特殊层级设置了为true,就会直接跳过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) {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);}}
  1. 调用 onHierarchyBuilt 将填充好的参数 赋值给 mRoot 也即为 displayContent。这样层技术也就构建完成。

http://www.ppmy.cn/devtools/146042.html

相关文章

【QT】自定义QLabel

QLabel 既可以加载图片也可以绘制图形。 本章通过QLabel实现图片加载并在QLabel上绘制任意图形。 在使用 QLabel 实现加载图片功能&#xff0c;并在图片上绘制任意图形时&#xff0c;图片发生缩放后会存在图形拉伸情况&#xff0c;导致结果存在偏差。&#xff08;原因分析这里不…

[按键精灵IOS安卓版][脚本基础知识]按键post基本写法

这一期我们来讲按键post的写法&#xff0c;希望通过本期的学习&#xff0c;实现常见的post提交都能编写。 下面开始讲解&#xff1a; 一、使用的命令&#xff1a;url.httppost 选用这个命令的理由是它的参数比较全。 二、post请求都有哪些参数&#xff08;可能用到&#xf…

windows下vscode使用msvc编译器出现中文乱码

文章目录 [toc]1、概述2、修改已创建文件编码3、修改vscode默认编码 更多精彩内容&#x1f449;内容导航 &#x1f448;&#x1f449;C &#x1f448;&#x1f449;开发工具 &#x1f448; 1、概述 在使用MSVC编译器时&#xff0c;出现中文报错的问题可能与编码格式有关。UTF-…

qt创建线程的四种方式

第一种 继承QThread类&#xff0c;然后重写run()函数 // 线程一 Mythread1 new CMyThread; connect(Mythread1,&CMyThread::sig_num,this,&MainWindow::slots_thread1); connect(ui->pushButton,&QPushButton::clicked,[](){Mythread1->m_state ThreadSt…

[计算机网络]OSPF协议

开放最短路径优先OSPF 1&#xff09;OSPF的工作方式 1>和谁交换消息 使用洪泛法&#xff0c;向本自治系统的所有路由器发送消息。 2>交换什么消息 发送的消息就是与本路由器相邻的所有路由器的链路状态&#xff0c;但这只是路由器所知道的部分信息。 链路状态就是说…

【机器学习】因微知著,穷数通灵:微积分与机器学习的量化之美

文章目录 微积分基础&#xff1a;理解变化与累积的数学前言一、多重积分的基本概念与计算1.1 多重积分的定义与重要性1.1.1 多重积分的基本组成1.1.2 多重积分在机器学习中的应用 1.2 多重积分的历史与发展1.2.1 多重积分的历史1.2.2 多重积分的发展 二、微分方程的基本概念与解…

RCE-PLUS (学习记录)

源码 <?php error_reporting(0); highlight_file(__FILE__); function strCheck($cmd) {if(!preg_match("/\;|\&|\\$|\x09|\x26|more|less|head|sort|tail|sed|cut|awk|strings|od|php|ping|flag/i", $cmd)){return($cmd);}else{die("i hate this"…

大语言模型的token和向量

现在大语言模型火了&#xff0c;像 ChatGPT 什么的&#xff0c;能回答问题、写文章&#xff0c;。但它们为啥这么聪明呢&#xff1f;这就和向量、Token 有关系。那怎么通过向量、Token来理解我们的问题呢。看完这篇文章就知道了 token Token 就像是语言里的小积木&#xff0c…