Android13 WMS窗口层级树

1,认识层级树
可以通过dumpsys activity containers 看到 WMS 层级树的结构

ACTIVITY MANAGER CONTAINERS (dumpsys activity containers)
ROOT type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
  #0 Display 0 name="Built-in Screen" type=undefined mode=fullscreen override-mode=fullscreen requested-bounds=[0,0][1440,2960] bounds=[0,0][1440,2960]
   #2 Leaf:36:36 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
    #1 WindowToken{6cc524e type=2024 android.os.BinderProxy@8ffb949} type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
     #0 888c47c ScreenDecorOverlayBottom type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
    #0 WindowToken{4346895 type=2024 android.os.BinderProxy@ad2b04c} type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
     #0 da08538 ScreenDecorOverlay type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
   #1 HideDisplayCutout:32:35 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
    #2 OneHanded:34:35 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
     #0 FullscreenMagnification:34:35 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
      #0 Leaf:34:35 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
    #1 FullscreenMagnification:33:33 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
     #0 Leaf:33:33 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
    #0 OneHanded:32:32 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
     #0 Leaf:32:32 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
   #0 WindowedMagnification:0:31 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
    #6 HideDisplayCutout:26:31 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
     #0 OneHanded:26:31 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
      #2 FullscreenMagnification:29:31 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
       #0 Leaf:29:31 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
      #1 Leaf:28:28 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
      #0 FullscreenMagnification:26:27 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
       #0 Leaf:26:27 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
    #5 Leaf:24:25 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
     #1 WindowToken{914ccc0 type=2024 android.os.BinderProxy@60ae43} type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
      #0 20f913e pip-dismiss-overlay type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
     #0 WindowToken{4af2b8f type=2019 android.os.BinderProxy@d5bc069} type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
      #0 e66fa25 NavigationBar0 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
    #4 HideDisplayCutout:18:23 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
     #0 OneHanded:18:23 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
      #0 FullscreenMagnification:18:23 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
       #0 Leaf:18:23 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
    #3 OneHanded:17:17 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
     #0 FullscreenMagnification:17:17 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
      #0 Leaf:17:17 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
       #0 WindowToken{2cb42e4 type=2040 android.os.BinderProxy@9c0076} type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
        #0 bbfae02 NotificationShade type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
    #2 HideDisplayCutout:16:16 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
     #0 OneHanded:16:16 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
      #0 FullscreenMagnification:16:16 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
       #0 Leaf:16:16 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
    #1 OneHanded:15:15 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
     #0 FullscreenMagnification:15:15 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
      #0 Leaf:15:15 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
       #0 WindowToken{36a0a7c type=2000 android.os.BinderProxy@57ea84e} type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
        #0 f20fb5a StatusBar type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
    #0 HideDisplayCutout:0:14 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
     #0 OneHanded:0:14 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
      #1 ImePlaceholder:13:14 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
       #0 ImeContainer type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
        #0 WindowToken{18b2fcb type=2011 android.os.Binder@f3da9a} type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
         #0 3a9ce61 InputMethod type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
      #0 FullscreenMagnification:0:12 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
       #2 Leaf:3:12 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
        #0 WindowToken{fc9ff07 type=2038 android.os.BinderProxy@d280149} type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
         #0 d5c8415 ShellDropTarget type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
       #1 DefaultTaskDisplayArea type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
        #2 Task=1 type=home mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
         #0 Task=24 type=home mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
          #0 ActivityRecord{7246b16 u0 com.android.launcher3/.uioverrides.QuickstepLauncher} t24} type=home mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
           #0 bd08f5d com.android.launcher3/com.android.launcher3.uioverrides.QuickstepLauncher type=home mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
        #1 Task=3 type=undefined mode=fullscreen override-mode=fullscreen requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
        #0 Task=4 type=undefined mode=fullscreen override-mode=fullscreen requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
         #1 Task=6 type=undefined mode=multi-window override-mode=multi-window requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
         #0 Task=5 type=undefined mode=multi-window override-mode=multi-window requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
       #0 Leaf:0:1 type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
        #0 WallpaperWindowToken{a0c14b1 token=android.os.Binder@c234b58} type=undefined mode=fullscreen override-mode=fullscreen requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]
         #0 b9a90a8 com.android.systemui.ImageWallpaper type=undefined mode=fullscreen override-mode=undefined requested-bounds=[0,0][0,0] bounds=[0,0][1440,2960]

可以看出,层级机构树一共有37层,层级越高,越接近我们的"眼睛",以上父子关系,做成图表格式,可以看的更直观,如下:

在这里插入图片描述
2,层级结构树的构建过程
SystemServer启动时,调用startOtherServices方法

private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
	//省略
		t.traceBegin("SetWindowManagerService");
   	 	mActivityManagerService.setWindowManager(wm);
        t.traceEnd();
        //省略
}

AMS.setWindowManager

 public void setWindowManager(WindowManagerService wm) {
        synchronized (this) {
            mWindowManager = wm;
            mWmInternal = LocalServices.getService(WindowManagerInternal.class);
            mActivityTaskManager.setWindowManager(wm);
        }
    }

ATMS.setWindowManager

public void setWindowManager(WindowManagerService wm) {
        synchronized (mGlobalLock) {
           //省略
            mRootWindowContainer.setWindowManager(wm);
            //省略
        }
    }

RootWindowContainer.setWindowManager

void setWindowManager(WindowManagerService wm) {
       //省略
        final Display[] displays = mDisplayManager.getDisplays();//得到系统有几个显示设备
        for (int displayNdx = 0; displayNdx < displays.length; ++displayNdx) {//遍历display
            final Display display = displays[displayNdx];
            final DisplayContent displayContent = new DisplayContent(display, this);//创建DisplayContent
            addChild(displayContent, POSITION_BOTTOM);//将DisplayContent挂在root下
            if (displayContent.mDisplayId == DEFAULT_DISPLAY) {
                mDefaultDisplay = displayContent;
            }
        }
		//省略
    }

对于每个显示设备,系统都会创建一个DisplayContent对象,并将DisplayContent对象挂在root下。接着来看一下DisplayContent的创建

DisplayContent(Display display, RootWindowContainer root) {
	//省略
	configureSurfaces(pendingTransaction);
	//省略
}

configureSurfaces

 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);
        }

 		//省略

getDisplayAreaPolicyProvider得到的是DefaultProvider对象,然后调用其instantiate方法。源码路径:frameworks/base/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) {
            final TaskDisplayArea defaultTaskDisplayArea = new TaskDisplayArea(content, wmService,
                    "DefaultTaskDisplayArea", FEATURE_DEFAULT_TASK_CONTAINER);//创建DefaultTaskDisplayArea
            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);//1
            if (content.isTrusted()) {
                // Only trusted display can have system decorations.
                configureTrustedHierarchyBuilder(rootHierarchy, wmService, content);//2
            }

            // 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);//3
        }

首先创建HierarchyBuilder对象,注释1处将imeContainer和tdaList(tdaList此时只有一个元素就是DefaultTaskDisplayArea)分别保存在HierarchyBuilder对象中的mImeContainer和mTaskDisplayAreas中。注释2处创建Feature并设置Feature的层级。注释3处开始构建层级树

2.1,创建Feature
configureTrustedHierarchyBuilder

//frameworks/base/services/core/java/com/android/server/wm/DisplayAreaPolicy.java
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)
                    .except(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY)
                    // Make the DA dimmable so that the magnify window also mirrors the dim layer.
                    .setNewDisplayAreaSupplier(DisplayArea.Dimmable::new)
                    .build());
            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()
                        .except(TYPE_NAVIGATION_BAR, TYPE_NAVIGATION_BAR_PANEL, TYPE_STATUS_BAR,
                                TYPE_NOTIFICATION_SHADE)
                        .build())
                        .addFeature(new Feature.Builder(wmService.mPolicy, "OneHanded",
                                FEATURE_ONE_HANDED)
                                .all()
                                .except(TYPE_NAVIGATION_BAR, TYPE_NAVIGATION_BAR_PANEL,
                                        TYPE_SECURE_SYSTEM_OVERLAY)
                                .build());
            }
            rootHierarchy
                    .addFeature(new Feature.Builder(wmService.mPolicy, "FullscreenMagnification",
                            FEATURE_FULLSCREEN_MAGNIFICATION)
                            .all()
                            .except(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY, TYPE_INPUT_METHOD,
                                    TYPE_INPUT_METHOD_DIALOG, TYPE_MAGNIFICATION_OVERLAY,
                                    TYPE_NAVIGATION_BAR, TYPE_NAVIGATION_BAR_PANEL)
                            .build())
                    .addFeature(new Feature.Builder(wmService.mPolicy, "ImePlaceholder",
                            FEATURE_IME_PLACEHOLDER)
                            .and(TYPE_INPUT_METHOD, TYPE_INPUT_METHOD_DIALOG)
                            .build());
        }
    }

可以看出,这里创建了几个Feature,以及它们对应的层级是:

  1. WindowedMagnification:0~31
  2. HideDisplayCutout: 0~ 14 16 18~ 23 26~35
  3. OneHanded:0~ 23 26~ 32 34~35
  4. FullscreenMagnification:0~ 12 15~ 23 26~ 27 29~ 31 33~35
  5. ImePlaceholder:13 14
    至于层级是怎么得出来的,具体查看Feature.Builder的 all,except等等方法。Feature以及对应的层级设置好之后,接下来就是要开始构建层级树了。

2.2, 层级树的构建
源码路径:frameworks/base/services/core/java/com/android/server/wm/DisplayAreaPolicyBuilder.java

Result build(WindowManagerService wmService) {
        validate();

        // Attach DA group roots to screen hierarchy before adding windows to group hierarchies.
        mRootHierarchyBuilder.build(mDisplayAreaGroupHierarchyBuilders);
       //省略
    }

mRootHierarchyBuilder就是之前创建的HierarchyBuilder对象,调用其build方法。构建层级树的核心就是该方法,来分步分析它

2.2.1 挂载 Feature

private void build(@Nullable List<HierarchyBuilder> displayAreaGroupHierarchyBuilders) {
            final WindowManagerPolicy policy = mRoot.mWmService.mPolicy;
            final int maxWindowLayerCount = policy.getMaxWindowLayer() + 1;// 37
            final DisplayArea.Tokens[] displayAreaForLayer =
                    new DisplayArea.Tokens[maxWindowLayerCount];
            final Map<Feature, List<DisplayArea<WindowContainer>>> featureAreas =
                    new ArrayMap<>(mFeatures.size());//前面创建了5个Feature,所以size为5
            for (int i = 0; i < mFeatures.size(); i++) {
                featureAreas.put(mFeatures.get(i), new ArrayList<>());
            }

      
            PendingArea[] areaForLayer = new PendingArea[maxWindowLayerCount];
            final PendingArea root = new PendingArea(null, 0, null);
            Arrays.fill(areaForLayer, root);//此时里面的每个元素都是root 0,0

            // Create DisplayAreas to cover all defined features.
            final int size = mFeatures.size();
            for (int i = 0; i < size; i++) {

                final Feature feature = mFeatures.get(i);
                PendingArea featureArea = null;
                for (int layer = 0; layer < maxWindowLayerCount; layer++) {
                    if (feature.mWindowLayers[layer]) {
   
                        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 {
                      
                        featureArea = null;
                    }
                }
            }

创建一个临时的层级结构树PendingArea(后面还需要转换成真正的层级树的),对于Feature,其构造结果如下
在这里插入图片描述
2.2.2,挂载leaf叶子结点

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;
                    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.
                        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.
                        leafArea.mExisting = mImeContainer;
                        leafArea.mSkipTokens = true;
                    }
                }
                leafArea.mMaxLayer = layer;
            }
}

首先也是创建临时的叶子PendingArea,leafType默认为LEAF_TYPE_TOKENS,在typeOfLayer方法中,对于IME和Application 有处理,将其leafType设为LEAF_TYPE_TASK_CONTAINERS和LEAF_TYPE_IME_CONTAINERS。注意对于这两种类型,有特殊的处理,mSkipTokens设置为了true,后续将临时的结构树转成真正的层级树的时候有用。
经过这一步,此时结构树的构造结果如下
在这里插入图片描述
2.2.3 将临时的结构树转成真正的结构树

在build方法的最后,调用instantiateChildren方法,进行结构树的转换

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中,对于IME和Application Layer是有特殊处理的,删掉上面对应结点下的leaf叶子,最终转换好的结构树如下
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【每日刷题】Day75

【每日刷题】Day75 &#x1f955;个人主页&#xff1a;开敲&#x1f349; &#x1f525;所属专栏&#xff1a;每日刷题&#x1f34d; &#x1f33c;文章目录&#x1f33c; 1. 1833. 雪糕的最大数量 - 力扣&#xff08;LeetCode&#xff09; 2. 面试题 17.14. 最小K个数 - 力扣…

【数据库】Oracle安装报错(win10安装oracle提示环境不满足最低要求)

目录 一、问题场景&#xff1a; 二、问题描述 三、原因分析&#xff1a; 四、解决方案&#xff1a; 一、问题场景&#xff1a; 安装Oracle数据库 二、问题描述 安装之前提示&#xff08; [INS-13001]环境不满足最低要求。 是否确实要继续? &#xff09; 如图所示&…

C# unknow column “p0.TaskTypeId‘ in ‘field list‘

这个问题就是数据库出现问题&#xff0c;去 日志中去看 &#xff0c;找个具体表去 看实体类&#xff0c;与数据库中的表&#xff0c;是否存在字段。

若依项目实战------企业人力资源管理平台

一、数据库名称规范化及建表相关 1.术语名称 1.系统名称&#xff1a;企业人力资源管理平台英文翻译&#xff1a;Enterprise Human Resource Management Platform缩写&#xff1a;EHR 2.员工信息管理&#xff08;Employee Information Management 缩写&#xff1a;EIM&#…

Vue-双向数据绑定指令

v-model指令 双向数据绑定就是当数据设置给表单元素时&#xff0c;修改这个数据会修改表单元素的值&#xff0c; 修改表单元素的值同样也会修改这个数据 <body><div id"app"><input type"text" v-model"name"><p>{{name…

GPTCache:革新大模型缓存,降低成本,提升效率

GPTCache介绍 随着应用程序越来越受欢迎并遇到更高的流量水平,与 LLM API 调用相关的费用可能会变得相当可观。此外,LLM 服务的响应时间可能会很慢,尤其是在处理大量请求时。GPTCache是一个致力于构建用于存储 LLM 响应的语义缓存的项目。 项目架构 数字人助力传统客服 1…

成熟ICT测试系统与LabVIEW定制开发的比较

ICT&#xff08;In-Circuit Test&#xff09;测试系统是电子制造行业中用于电路板&#xff08;PCB&#xff09;组件检测的重要工具。市场上有许多成熟的ICT测试系统&#xff0c;如Keysight、Teradyne、SPEA等公司提供的商用解决方案。此外&#xff0c;LabVIEW作为一种强大的图形…

Dubbo运行原理

目录 Dubbo通讯协议 Dubbo负载均衡策略 RPC和HTTP有什么区别&#xff1f; 让你设计一个RPC框架&#xff0c;如何考虑数据序列化问题&#xff1f; Dubbo 是一款高性能、轻量级的开源 RPC&#xff08;远程过程调用&#xff09;框架&#xff0c;主要用于构建分布式服务和微服务…

分享一个微信管理工具

聚合管理多个微信号 1、聚合聊天 支持多微信账号消息&#xff0c;在统一端口接收回复 2、话题素材库 可提前把话题准备好用到的时候一键发送 3、朋友圈互动 支持在聊天窗口直接看到当前客户朋友圈实时点赞评论互动 批量多号添加好友 1、批量添加联系人&#xff0c;导入联系…

蓝牙模块在车载系统中的应用与集成:现状、挑战与未来展望

随着科技的快速发展&#xff0c;蓝牙技术已经深入到我们生活的方方面面&#xff0c;其中车载系统中的应用尤为显著。蓝牙模块作为一种无线通信技术&#xff0c;不仅为驾驶者提供了更加便捷的操作体验&#xff0c;同时也提升了驾驶的安全性。本文旨在分析蓝牙模块在车载系统中的…

LabVIEW在脑机接口(BCI)研究中的应用

脑机接口&#xff08;Brain-Computer Interface&#xff0c;BCI&#xff09;技术通过解读大脑活动&#xff0c;将人类思维与计算机或其他设备连接起来&#xff0c;广泛应用于神经康复、认知研究和人机交互等领域。LabVIEW作为强大的图形化编程环境&#xff0c;在BCI研究中发挥着…

Firefox 火狐浏览器现在允许您在其 Nightly 版本中选择您喜欢的 AI 聊天机器人

Firefox Nightly版本是Mozilla推出的一个特殊的频道&#xff0c;用户可以在这里试用最新的功能和更改。这个版本每天都会更新&#xff0c;并且持续发布新的功能和修复。例如&#xff0c;在2023年10月8日发布的版本中&#xff0c;引入了一个新的按钮&#xff0c;用于快速重置隐私…

避免柔宇科技破产,唯有掌门人完成六项修炼

柔宇科技&#xff0c;创始团队来自清华与斯坦福科学家、估值一度高达435亿元的独角兽&#xff0c;但不幸的是&#xff0c;已经正式进入破产清算流程。 据深圳市中级人民法院公告&#xff0c;已裁定受理柔宇科技破产清算一案&#xff0c;定于9月13日上午召开第一次债权人会议。…

贪吃蛇项目:GameRun与GameEnd部分:游戏的主体运行与善后部分

准备工作&#xff1a;打印得分信息 在进行GameStart之前&#xff0c;我们需要在地图的右侧打印帮助信息&#xff0c;以及目前玩家的得分情况和一个食物在当前速度下的得分情况&#xff08;加速的状态下按比例增加食物的分数&#xff0c;减速的状态下则相反&#xff09;&#xf…

第 27 篇 : 搭建maven私服nexus

官网文档 1. 下载应该很慢, 最好是能翻墙 nexus-3.69.0-02-java8-unix.tar.gz 2. 上传到/usr/local/src, 解压及重命名 tar -zxvf nexus-3.69.0-02-java8-unix.tar.gz rm -rf nexus-3.69.0-02-java8-unix.tar.gz mv nexus-3.69.0-02 nexus ls3. 修改配置 cd /usr/local/sr…

人工智能与大数据:新时代的技术融合与未来展望

引言 在信息化和数字化迅猛发展的今天&#xff0c;人工智能&#xff08;AI&#xff09;和大数据&#xff08;Big Data&#xff09;已成为推动社会变革和技术进步的两大支柱。随着互联网的普及、计算能力的提升以及数据获取手段的多样化&#xff0c;AI和大数据技术的应用愈发广泛…

GD32调试篇:ST-LINK utility工具下载安装

本文章基于兆易创新GD32 MCU所提供的2.2.4版本库函数开发 向上代码兼容GD32F450ZGT6中使用 后续项目主要在下面该专栏中发布&#xff1a; https://blog.csdn.net/qq_62316532/category_12608431.html?spm1001.2014.3001.5482 感兴趣的点个关注收藏一下吧! 电机驱动开发可以跳转…

怎么打印加密的pfd文件,有那些方法?

现在人们的保密意识越来越强了,越来越多的人在完成pdf文档后就会对文档进行保护&#xff0c;但有的PDF文档被添加了密码&#xff0c;限制了打印的权限&#xff0c;导致我们想打印PDF文档的时候就提示我们要输入密码。面对这种情况&#xff0c;我们要怎样才能把PDF文档打印出来呢…

Blast L2空投教学,好用的Blast钱包推荐bitget

什么是 Blast L2&#xff1f; Blast&#xff08;web3.bitget.com/en/&#xff09;是一个与 EVM 兼容的第 2 层 (L2) 区块链网络&#xff0c;旨在通过原生收益产生收益。该项目由匿名联合创始人 PacmanBlur 领导&#xff0c;并已成功从 Paradigm 和 Standard Crypto 等知名投资…

计算机网络面试TCP篇之TCP三次握手与四次挥手

TCP 三次握手与四次挥手面试题 任 TCP 虐我千百遍&#xff0c;我仍待 TCP 如初恋。 巨巨巨巨长的提纲&#xff0c;发车&#xff01;发车&#xff01; PS&#xff1a;本次文章不涉及 TCP 流量控制、拥塞控制、可靠性传输等方面知识&#xff0c;这些知识在这篇&#xff1a; TCP …