WindowManager相关容器类
窗口中容器类介绍:
本节内容较多,建议结合前面的内容一起阅读:
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 {
综上最后得到一个关系图:其中我将一个层级的都用同一个颜色标注出来。

然后再通过类图对其中涉及到的类进行归类。

相关文章:
WindowManager相关容器类
窗口中容器类介绍: 本节内容较多,建议结合前面的内容一起阅读: 1、addWindow的宏观概念 2、WindowManager#addView_1 3、WindowManager#addView_2 1)、WindowContainer: class WindowContainer<E extends WindowC…...
零售行业运营有哪些业务场景?详解各业务场景的分析指标和维度
在当今这个数字化迅速发展的时代,零售行业正经历着前所未有的变革。传统的零售模式正在被新兴的技术和创新的业务场景所颠覆,消费者的需求和购物习惯也在不断地演变。零售行业的运营,作为连接消费者、产品和市场的关键环节,对于零…...
无锡哲讯携手SAP,赋能装备制造业数字化转型
在当今快速发展的工业4.0时代,装备制造业作为国民经济的重要支柱,正面临着前所未有的机遇与挑战。无锡哲讯智能科技有限公司凭借其深厚的行业经验和专业的SAP实施能力,为装备制造业提供全面的数字化解决方案,助力企业实现智能化、…...
TPM仿真环境搭建
文章目录 背景及注意事项一、CMake二、m4三、GNU MP Library四、TPM_Emulator五、TSS协议栈(trousers-0.3.14.tar.gz)六、 tpm-tools七、查看是否安装成功八、测试 TPM环境(需要开三个终端分别运行)8.1 启动TPM (第一个…...
提高篇(五):使用Processing创作互动艺术:从灵感到实现
提高篇(五):使用Processing创作互动艺术:从灵感到实现 引言 互动艺术将观众从被动的观察者转变为主动参与者,通过创意编程和技术手段,让艺术品具备感知和回应的能力。Processing作为一种强大的创意编程工具,提供了丰富的功能和灵活的编程环境,帮助艺术家和设计师实现他…...
华为od-C卷100分题目-3用连续自然数之和来表达整数
华为od-C卷100分题目-3用连续自然数之和来表达整数 题目描述 一个整数可以由连续的自然数之和来表示给定一个整数,计算该整数有几种连续自然数之和的表达式,且打印出每种表达式 输入描述 一个目标整数T(1<T<1000) 输出描述 该整数的所有表达…...
Chrome 自动执行 JS 脚本 | Tampermonkey 插件
文章目录 第 1 步:安装插件 Tampermonkey第 2 步:固定到工具栏第 3 步:在网站上启用 Tampermonkey第 4 步:查看效果第 5 步:调试 JS 代码😂 背景:有个网站,每次进去都要点 3 次才能把相关页面展开。而且,页面经常会自己刷新,导致展开的页面又收回去了。【这一天天的…...
ffmplay 源码解读
stream_open 讲解 // 定义一个静态函数用于初始化并返回VideoState结构体指针,用于管理播放状态 static VideoState* stream_open(const char* filename, AVInputFormat* iformat) {VideoState* is; // 创建VideoState结构体指针// 分配内存并初始化VideoState结构…...
java web如何调用py脚本文件
Controller public class IndexController {RequestMapping("/pythonTest")ResponseBodypublic String pythonTest(){// 假设你的Python脚本名为script.pyString pythonScriptPath "D:\\project\\c1\\hello.py";ProcessBuilder processBuilder new Proce…...
K8s:无状态
无状态服务 无状态服务是指服务的实例之间没有持久化状态,每个实例都是相同的,可以互换使用。 调度器 ReplicationController 简称 RC是 Kubernetes 早期版本中用来确保 Pod 副本始终运行的 API 对象。它通过监控 Pod 副本的数量,确保任何…...
Docker 入门篇(九)-- 使用 Maven 插件 构建 Docker 镜像
在这篇教程中,我们将学习如何使用 Maven 插件为 Spring Boot 应用构建 Docker 镜像。我们将使用 spring-boot-maven-plugin 和 dockerfile-maven-plugin 这两个插件。 一、前提条件 已安装 Docker。已安装 JDK 8 或以上版本。已安装 Maven。 二 创建一个 Spring …...
网络协议三
数据中心 一、DNS 现在网站的数目非常多,常用的网站就有二三十个,如果全部用 IP 地址进行访问,恐怕很难记住 根 DNS 服务器 :返回顶级域 DNS 服务器的 IP 地址 顶级域 DNS 服务器:返回权威 DNS 服务器的 IP 地址 …...
LeetCode LRU缓存
题目描述 请你设计并实现一个满足 LRU (最近最少使用) 缓存 约束的数据结构。 实现 LRUCache 类: LRUCache(int capacity) 以 正整数 作为容量 capacity 初始化 LRU 缓存int get(int key) 如果关键字 key 存在于缓存中,则返回关键字的值,…...
Parallels Desktop for Mac 19.4.0更新了哪些内容?有什么改进?
带来了重新设计的共享 Mac 文件夹版本,这些文件夹现在是符号链接,像指针一样指向您的 Mac 文件夹中的文件,同时仍然显示在 Windows 的本地磁盘上。 修复了由于共享文件夹问题导致 NinjaTrader 无法正常启动的问题。 修复了由于共享文件夹问…...
Python 将CSV文件转为PDF文件
CSV文件通常用于存储大量的数据,而PDF文件则是一种通用的文档格式,便于与他人共享和打印。将CSV文件转换成PDF文件可以帮助我们更好地管理和展示数据。本文将介绍如何通过Python编程将CSV文件导出为PDF文件。 Python Excel库安装及介绍 在 Python 中&am…...
4_XMR交易过程
XMR交易过程 参考文档 书: 《精通门罗币 : 私密交易的未来》(Mastering Monero) 书中的代码示例: 《精通门罗币 : 私密交易的未来》深入探究门罗币与密码学门罗币的环签名分析官方介绍视频 1.隐匿地址 Stealth Address_Monero官方介绍视频2.环签名 Ring Signature_Monero官方…...
02_共享锁和排他锁
共享锁和排他锁 文章目录 共享锁和排他锁简介共享锁(Shared Lock, S Lock)简介原理使用方式加锁流程使用场景 排他锁(Exclusive Lock, X Lock)简介原理使用方式加锁流程使用场景 对比注意事项结论 简介 MySQL 中的共享锁和排他锁…...
Ubuntu的启动过程
尽管通常情况下Ubuntu的启动并不需要用户过多地参与,但是Ubuntu系统的启动本身是一个非常复杂的过程。在这个过程中,有硬件的检测、系统内核的准备以及各种系统服务的启动等。作为系统管理员,需要深入了解其中所经历的阶段,才能在…...
c# 下 ScintillaNET 显示XML信息并折叠节点
winform下显示XML信息(非WPF) 之前使用的是FastColoredTextBox,github地址如下: https://github.com/PavelTorgashov/FastColoredTextBox 但是有个问题,它支持中文,wordwraptrue,自动换行时&…...
什么叫防御式编程
防御式编程是一种编程策略,主要目的是提高代码的健壮性和可靠性。它假设任何错误都可能发生,并且在设计和编写代码时采取预防措施以防止这些错误导致程序崩溃或产生错误结果。 以下是一些防御式编程的常见实践: 输入验证:总是验证…...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...
业务系统对接大模型的基础方案:架构设计与关键步骤
业务系统对接大模型:架构设计与关键步骤 在当今数字化转型的浪潮中,大语言模型(LLM)已成为企业提升业务效率和创新能力的关键技术之一。将大模型集成到业务系统中,不仅可以优化用户体验,还能为业务决策提供…...
JavaSec-RCE
简介 RCE(Remote Code Execution),可以分为:命令注入(Command Injection)、代码注入(Code Injection) 代码注入 1.漏洞场景:Groovy代码注入 Groovy是一种基于JVM的动态语言,语法简洁,支持闭包、动态类型和Java互操作性,…...
基于FPGA的PID算法学习———实现PID比例控制算法
基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容:参考网站: PID算法控制 PID即:Proportional(比例)、Integral(积分&…...
【WiFi帧结构】
文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成:MAC头部frame bodyFCS,其中MAC是固定格式的,frame body是可变长度。 MAC头部有frame control,duration,address1,address2,addre…...
Maven 概述、安装、配置、仓库、私服详解
目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...
Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信
文章目录 Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket(服务端和客户端都要)2. 绑定本地地址和端口&#x…...
Java编程之桥接模式
定义 桥接模式(Bridge Pattern)属于结构型设计模式,它的核心意图是将抽象部分与实现部分分离,使它们可以独立地变化。这种模式通过组合关系来替代继承关系,从而降低了抽象和实现这两个可变维度之间的耦合度。 用例子…...
华为OD机考-机房布局
import java.util.*;public class DemoTest5 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseSystem.out.println(solve(in.nextLine()));}}priv…...
Python 实现 Web 静态服务器(HTTP 协议)
目录 一、在本地启动 HTTP 服务器1. Windows 下安装 node.js1)下载安装包2)配置环境变量3)安装镜像4)node.js 的常用命令 2. 安装 http-server 服务3. 使用 http-server 开启服务1)使用 http-server2)详解 …...
