WindowManager相关容器类

news2024/10/7 20:28:17

窗口中容器类介绍:
本节内容较多,建议结合前面的内容一起阅读:
1、addWindow的宏观概念
2、WindowManager#addView_1
3、WindowManager#addView_2

1)、WindowContainer:

class WindowContainer<E extends WindowContainer> extends ConfigurationContainer<E>{
……………………
}

WindowContainer的定义如上,可见其是一个容器,容器内的元素是自己或自己的子类(如:RootWindowContainer、DisplayContent、DisplayArea、DisplayArea.Tokens、TaskDisplayArea、Task、ActivityRecord、WindowToken、WindowState),而WindowContainer类继承自ConfigurationContainer类,该类也是一个容器类,其元素为自己或自己的子类。
其中有一些属性比较重要:mParent和mChildren,分别代表父节点和子节点。其中子节点是WindowList类型的,其排列顺序是依据其在z轴上的高度排列的,尾部的节点在z轴上最高最容易被用户看到。

2)、RootWindowContainer:

class RootWindowContainer extends WindowContainer<DisplayContent>
        implements DisplayManager.DisplayListener {

WindowContainer的根容器,可通过该节点遍历到窗口树上的任意窗口。他的mChildren继承自WindowContainer,所以该List的元素为DisplayContent。
在这里插入图片描述

3)、DisplayContent:

class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.DisplayContentInfo {

对应显示屏幕的概念,一个屏幕对应一个DisplayContent。一般情况下都是只有一个。在同一个DisplayContent中的窗口都会显示在同一个屏幕内。

4)、WindowToken:

class WindowToken extends WindowContainer<WindowState> {

WindowToken类是WindowState的容器,WindowState之前说过,在WMS中代表了一个窗口。
在这里插入图片描述

5)、ActivityRecord:

final class ActivityRecord extends WindowToken implements WindowManagerService.AppFreezeListener {

该类继承自WindowToken,所以也是WindowState类的容器。
在这里插入图片描述

6)、WindowState:

class WindowState extends WindowContainer<WindowState> implements WindowManagerPolicy.WindowState,
        InsetsControlTarget, InputTarget {

其实WindowState也可以作为其他WindowState的容器;
在这里插入图片描述

7)、WallpaperWindowToken:

class WallpaperWindowToken extends WindowToken {

该类同样继承自WindowToken;
对以上4)-7)进行统一分析,所有的窗口分三大类:系统窗口、应用窗口、其他窗口;其中系统窗口在最上层,应用窗口次之,而系统窗口的容器则为WindowToken,应用窗口为ActivityRecord。而App以下的窗口,父容器就为WallpaperWindowToken。

8)、DisplayArea.Tokens:

是WindowToken的父容器,同时会在其中对WindowToken进行排序;
在这里插入图片描述

public static class Tokens extends DisplayArea<WindowToken> {
private final Comparator<WindowToken> mWindowComparator =
        Comparator.comparingInt(WindowToken::getWindowLayerFromType);//这就是排序算法的核心。
}
 

排序算法触发的前提就是addChild函数,和前一节说的一样,找到addChild函数:

void addChild(WindowToken token) {
    addChild(token, mWindowComparator);
}

这里可以看到在addChild的时候,通过mWindowComparator为WindowToken确认顺序,而mWindowComparator的初始化是通过WindowToken.java中的getWindowLayerFromType方法作为比较器的。最后的方法实现在WindowManagerPolicy.java中的getWindowLayerFromTypeLw()方法。这里在addWindow的宏观概念一文中有讲到,主要就是对比了窗口的类型,如果是应用窗口则直接返回2,如果是wallpaper窗口,直接返回1(在最底层);然后再根据窗口的类型返回其排序的权重,根据权重再决定该窗口在子窗口中该插入到哪个位置。其中这里的type是在WindowToken中的一个属性:从注释看就是WindowManager类中的LayoutParams属性,这个属性会在窗口初始化时确定。

/** The type of window this token is for, as per {@link WindowManager.LayoutParams} */
final int windowType;

9)、Task:

ActivityRecord的容器,作为应用窗口的容器,ActivityRecord也是需要容器的。
在这里插入图片描述

class Task extends TaskFragment {

从定义可见Task是继承自TaskFragment的一个类。而TaskFragment则也是继承自WindowContainer的类。
class TaskFragment extends WindowContainer {
Task可以是Task的容器,也可以是ActivityRecord的容器,最形象的理解就是多任务界面的一个窗口就是一个Task。TaskFragment这个类型我了解不多,只知道和平行视界相关。平行视界中两个Activity需要同时显示,Task实现不了这个功能,所以再Task和ActivityRecord之间加入了TaskFragment。而一般情况下因为TaskFragment是Task的父类,所以大家都会通过TaskFragment创建对象,但是最终创建的还是Task类型的对象
这里的排序的话比较简单:老规矩找到addchild方法。

void addChild(WindowContainer child, int index) {
    index = getAdjustedChildPosition(child, index);
    super.addChild(child, index);

    ProtoLog.v(WM_DEBUG_ADD_REMOVE, "addChild: %s at top.", this);

    // A rootable task that is now being added to be the child of an organized task. Making
    // sure the root task references is keep updated.
    if (mTaskOrganizer != null && mCreatedByOrganizer && child.asTask() != null) {
        getDisplayArea().addRootTaskReferenceIfNeeded((Task) child);
    }

    // Make sure the list of display UID allowlists is updated
    // now that this record is in a new task.
    mRootWindowContainer.updateUIDsPresentOnDisplay();

    // Only pass minimum dimensions for pure TaskFragment. Task's minimum dimensions must be
    // passed from Task constructor.下面应该就是平行视界这类相关的操作了
    final TaskFragment childTaskFrag = child.asTaskFragment();
    if (childTaskFrag != null && childTaskFrag.asTask() == null) {
        childTaskFrag.setMinDimensions(mMinWidth, mMinHeight);

        // The starting window should keep covering its task when a pure TaskFragment is added
        // because its bounds may not fill the task.
        final ActivityRecord top = getTopMostActivity();
        if (top != null) {
            top.associateStartingWindowWithTaskIfNeeded();
        }
    }
}

这个方法比较直接,直接通过index指定了位置。虽然指定了位置,但是程序中还是要对其进行调整的。

private int getAdjustedChildPosition(WindowContainer wc, int suggestedPosition) {
    final boolean canShowChild = wc.showToCurrentUser();

    final int size = mChildren.size();

    // Figure-out min/max possible position depending on if child can show for current user.
    int minPosition = (canShowChild) ? computeMinUserPosition(0, size) : 0;
    int maxPosition = minPosition;
    if (size > 0) {
        maxPosition = (canShowChild) ? size - 1 : computeMaxUserPosition(size - 1);
    }

    // Factor in always-on-top children in max possible position.
    if (!wc.isAlwaysOnTop()) {
        // We want to place all non-always-on-top containers below always-on-top ones.
        while (maxPosition > minPosition) {
            if (!mChildren.get(maxPosition).isAlwaysOnTop()) break;
            --maxPosition;
        }
    }

    // preserve POSITION_BOTTOM/POSITION_TOP positions if they are still valid.
    if (suggestedPosition == POSITION_BOTTOM && minPosition == 0) {
        return POSITION_BOTTOM;
    } else if (suggestedPosition == POSITION_TOP && maxPosition >= (size - 1)) {
        return POSITION_TOP;
    }

    // Increase the maxPosition because children size will grow once wc is added.
    if (!hasChild(wc)) {
        ++maxPosition;
    }

    // Reset position based on minimum/maximum possible positions.
    return Math.min(Math.max(suggestedPosition, minPosition), maxPosition);
}

这里其实就是根据最大最小值和指定的值进行对比然后修正一下。

不过还有另一个形式的addChild算法:

void addChild(WindowContainer child, final boolean toTop, boolean showForAllUsers) {
    Task task = child.asTask();//这里如果child是task则返回一个值,否则返回null。
    try {
        if (task != null) {
            task.setForceShowForAllUsers(showForAllUsers);
        }
        // We only want to move the parents to the parents if we are creating this task at the
        // top of its root task.
        addChild(child, toTop ? MAX_VALUE : 0, toTop /*moveParents*/);
    } finally {
        if (task != null) {
            task.setForceShowForAllUsers(false);
        }
    }
}

这个算法参数都不同了,有三个参数,这里第一步直接判断子元素是不是task类型。如果是的话,就执行setForceShowForAllUsers该方法,但是这里也没啥,就是对mForceShowForAllUsers进行赋值,然后就是对其进行比较器为null的排序,这里就不重复讲了,比较的过程在WindowContainer.java中的protected void addChild(E child, Comparator comparator) 该方法中。

10)、TaskDisplayArea:

Task或者TaskDisplayArea的容器。继承自DisplayArea类。代表的是屏幕上一块专门用来存放app窗口的区域。其父容器是DisplayContent。

在这里插入图片描述

final class TaskDisplayArea extends DisplayArea<WindowContainer> {
这个类对其中元素排序的方法依然可以通过addChild方法去查看,
void addChild(WindowContainer child, int position) {
    if (child.asTaskDisplayArea() != null) {
        if (DEBUG_ROOT_TASK) {
            Slog.d(TAG_WM, "Set TaskDisplayArea=" + child + " on taskDisplayArea=" + this);
        }
        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);
    }
}

其对于两个子类的元素有着不同的排序方法。第一种就是直接调用WindowContainer.java中的addChild(E child, int index)方法,直接在指定位置添加。
第二个的话就是如果元素是task类的话其实和上面也差不多,就是通过当前已有的一些子类对指定的位置进行一些修正。

11)、DisplayArea:

该类用于将WindowContainer分组到DisplayContent下方的容器。DisplayArea是被DisplayAreaPolicy管理的,能够复写Configuration和被绑定到leash上,并且可以嵌套DisplayArea,该类有三种风格:
BELOW_TASKS:只能包含位于任务下方的BELLOW_TASK显示区域和WindowToken。
ABOVE_TASKS:只能包含位于任务上方的ABOVE_TASK显示区域和WindowToken。
ANY:可以包含任何类型的DisplayArea,以及任何类型的WindowToken或Task容器。

12)、RootDisplayArea:

DisplayArea的根节点。根据注释来看:从等级制度上是DisplayArea的根节点。同时也可以是 逻辑屏幕上DisplayContent的根节点,或者逻辑屏幕上一个群组DisplayAreaGroup的根节点。

/**
 * Root of a {@link DisplayArea} hierarchy. It can be either the {@link DisplayContent} as the root
 * of the whole logical display, or a {@link DisplayAreaGroup} as the root of a partition of the
 * logical display.
 */
class RootDisplayArea extends DisplayArea.Dimmable {

所以这里将屏幕上的窗口分为两个逻辑,一个是等级制度,一个是物理屏幕。
在这里插入图片描述

13)、DisplayAreaGroup:

继承自RootDisplayArea,可以理解为一个集群,一个DisplayArea和DisplayContent之间的一个集群。 DisplayContent是整个屏幕上DisplayArea的根节点,但是一部分DisplayArea也可以挂载在DisplayAreaGroup上 。

/** The root of a partition of the logical display. */
class DisplayAreaGroup extends RootDisplayArea {

14)、另外还有一个容器ImeContainer:

这是输入法相关的,我暂时没了解,看定义大概能知道也是一类WindowToken的容器。

private static class ImeContainer extends DisplayArea.Tokens {

综上最后得到一个关系图:其中我将一个层级的都用同一个颜色标注出来。
请添加图片描述

然后再通过类图对其中涉及到的类进行归类。
请添加图片描述

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

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

相关文章

先激活还是先插卡?流量卡的激活顺序你知道吗?

拿到流量卡后&#xff0c;先激活还是先插卡吗&#xff1f;你知道是流量卡的激活顺序吗&#xff1f; 在这里&#xff0c;小编提醒大家&#xff0c;拿到卡后先别着急着操作&#xff0c;一定要先看一遍激活流程。 以下为流量卡的激活方法&#xff1a; 如果你是快递激活的话&…

ProxySQL + MySQL MGR 实现读写分离实战

文章目录 前言1、ProxySQL 介绍1.1、ProxySQL 如何工作1.2、ProxySQL 工作原理 2、ProxySQL 安装3、ProxySQL MGR 读写分离3.1、读写分离配置3.2、读写分离测试3.3、SpringBoot 整合 前言 该文章实践之前&#xff0c;需要搭建MySQL MGR集群&#xff0c;关于 MySQL MGR 集群搭…

Java 18 新功能概述

Java 18 在 2022 年 3 月 22 日正式发布&#xff0c;Java 18 不是一个长期支持版本。 包含多项新特性和改进&#xff0c;如文件系统链接、文本块、表达式求值API、ForkJoinPool优化、Optional新方法等。 亮点还包括预览特性&#xff1a;Record Pattern Matching for Switch和增…

BIM分析简明教程

大多数建筑师和工程师都会遇到过建筑信息模型 (BIM) 的概念。 这是一种可以为你的公司带来巨大利益的方法。 建筑信息模型允许你集中项目的信息。 你可以将所有相关数据集中到一个数据库中&#xff0c;而不是处理大量文档。 这样做的好处是显而易见的。 随着你的项目变得越来越…

k8s:实现一个pod两个容器

# 制作两个容器的镜像 通过以下Dockerfile创建一个镜像 cd /chz/install/docker vim Dockerfile <<<< 内容如下&#xff1a; FROM centosRUN sed -i -e "s|mirrorlist|#mirrorlist|g" /etc/yum.repos.d/CentOS-* RUN sed -i -e "s|#baseurlhttp:/…

Typesense-开源的轻量级搜索引擎

Typesense-开源的轻量级搜索引擎 Typesense是一个快速、允许输入错误的搜索引擎&#xff0c;用于构建愉快的搜索体验。 开源的Algolia替代方案& 易于使用的弹性搜索替代方案 官网: https://typesense.org/ github: https://github.com/typesense/typesense 目前已有18.4k…

2024最值得入手的五款骨传导耳机推荐!走过路过不要错过!

耳机发展到现在已经经历了无数次的迭代更新。从有线耳机到如今的无线耳机以及骨传导耳机&#xff0c;功能也更加的全面&#xff0c;从当初的只是用来听音乐&#xff0c;到如今的追求音质、舒适、防水等功能&#xff0c;在无线耳机的市场中&#xff0c;骨传导耳机尤为受欢迎&…

如何提升推广链接辨识度与可信度?试试自定义链接后缀

各位大佬&#xff0c;咱今天来聊聊短信营销这个事儿。这可是好多企业都在用的营销手段啊&#xff0c;一条几分钱的短信&#xff0c;就能搭起用户和企业的桥梁&#xff0c;能增强粘性、促成交易或者推动复购&#xff0c;那真是高覆盖、低成本、高效率。 但现在问题来了&#xf…

短剧系统投流版开发,为运营公司投流业务赋能

短剧系统投流版开发是一项复杂的任务&#xff0c;旨在为运营公司的投流业务提供强大的技术支持和赋能。以下是一些关键步骤和考虑因素&#xff0c;以确保短剧系统投流版的成功开发&#xff1a; 一、明确业务需求与目标 首先&#xff0c;需要深入了解运营公司的业务需求、目标…

分享:618学习狂欢节活动来啦——大数据培训班

618狂欢学习节 限时优惠大放送 活动时间&#xff1a;6月1日—6月18日

opencv-python(一)

1. 图片加载与显示 import cv2 import numpy as npif __name__"__main__":rose cv2.imread(./rose.jpeg)print(rose.shape)print(type(rose))cv2.imshow(rose,rose) # 弹出窗口cv2.waitKey() # 等待键盘输入&#xff0c;任意输入&#xff0c;触发这行代码&#xff…

回溯--字母迷宫

1.题目描述 字母迷宫游戏初始界面记作 m x n 二维字符串数组 grid&#xff0c;请判断玩家是否能在 grid 中找到目标单词 target。 注意&#xff1a;寻找单词时 必须 按照字母顺序&#xff0c;通过水平或垂直方向相邻的单元格内的字母构成&#xff0c;同时&#xff0c;同一个单…

十、C语言:数据存储(大端小端存储,整型、浮点型存储)

一、大端存储与小端存储 大端字节序&#xff1a; 数据的低位字节序内容存放在高地址处&#xff0c;高位字节序内容存放在低地址处 小端字节序&#xff1a; 数据的低位字节序内容存放在低地址处&#xff0c;高位字节序内容存放在高地址处 //每个地址单元对应一个字节 二、 整型存…

通过一次下载任务抓包分析TCP/IP协议

TCP/IP协议分析 一、实验简介 本实验主要讲解TCP/IP协议的应用&#xff0c;通过一次下载任务&#xff0c;抓取TCP/IP数据报文&#xff0c;对TCP连接和断开的过程进行分析&#xff0c;查看TCP“三次握手”和“四次挥手”的数据报文&#xff0c;并对其进行简单的分析。 二、实…

告别冗长代码:Java Lambda 表达式如何简化你的编程

在现代软件开发中&#xff0c;高效和简洁的代码变得越来越重要。Java作为一门成熟而广泛使用的编程语言&#xff0c;一直在不断进化&#xff0c;以满足开发者的需求。Java 8的推出标志着一次重要的飞跃&#xff0c;其中最引人注目的特性之一便是Lambda表达式。 Lambda表达式为J…

网络协议三

数据中心 一、DNS 现在网站的数目非常多&#xff0c;常用的网站就有二三十个&#xff0c;如果全部用 IP 地址进行访问&#xff0c;恐怕很难记住 根 DNS 服务器 &#xff1a;返回顶级域 DNS 服务器的 IP 地址 顶级域 DNS 服务器&#xff1a;返回权威 DNS 服务器的 IP 地址 …

60V大功率半桥GaN半桥驱动器替代LMG1210

1. 产品特性&#xff08;替代LMG1210&#xff09; ➢ 工作频率高达 10MHz ➢ 20ns 典型传播延迟 ➢ 5ns 高侧/低侧匹配 ➢ 两种输入控制模式 ➢ 具有可调死区时间的单个 PWM 输入、 独立输入模式 ➢ 1.5A 峰值拉电流和 3A 峰值灌电流 ➢ 内置 5V LDO ➢ 欠压保护 ➢ 过…

前端怎么预览pdf

1.背景 后台返回了一个在线的pdf地址&#xff0c;需要我这边去做一个pdf的预览&#xff08;需求1&#xff09;&#xff0c;并且支持配置是否可以下载&#xff08;需求2&#xff09;&#xff0c;需要在当前页就能预览&#xff08;需求3&#xff09;。之前我写过一篇预览pdf的文…

人工智能智能体的五大能力等级:从工具到超人

在人工智能(AI)的世界中,智能体的性能和能力可以被划分为不同的等级。这些等级从简单的工具到超越人类能力的超人智能体。本文将探讨AI智能体的五个能力等级,以及每个等级的关键技术、性能、能力和使用场景。 等级 0:无AI的基础工具 在这个阶段,我们没有真正的AI,只有…

目标检测——铁轨表面裂纹数据集

引言 亲爱的读者们&#xff0c;您是否在寻找某个特定的数据集&#xff0c;用于研究或项目实践&#xff1f;欢迎您在评论区留言&#xff0c;或者通过公众号私信告诉我&#xff0c;您想要的数据集的类型主题。小编会竭尽全力为您寻找&#xff0c;并在找到后第一时间与您分享。 …