Android WMS——WMS窗口添加(十)
Android 的 WMS(Window Manager Service)是一个关键组件,负责管理窗口的创建、显示、布局和交互等。Window 的操作有两大部分,一部分是 WindowManager 来处理,一部分是 WMS 来处理,如下图所示:

WindowManager 中,通过 WindowManagerGlobal 创建 ViewRootImpl ,也就是 View 的根。在 ViewRootImpl 中完成对 View 的绘制等操作,然后通过 IPC 获取到 Session,最终通过 WMS 来进行处理。WindowManager 部分的管理流程前面已经介绍,这里我们来看一下 WMS 对 Window 的管理。
一、调用流程
1、WM到WMS
我们都知道 Window 的添加最后是通过 ViewRootImpl.addTodisplay 方法来完成的,我们先来看一下:
ViewRootImpl
源码位置:/frameworks/base/core/java/android/view/ViewRootImpl.java
final IWindowSession mWindowSession;public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView, int userId) {synchronized (this) {if (mView == null) {……try {……res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,getHostVisibility(), mDisplay.getDisplayId(), userId,mInsetsController.getRequestedVisibility(), inputChannel, mTempInsets,mTempControls);}……}}
}
这里调用了 mWindowSession.addToDisplayAsUser 来完成最后的添加,可以看到 IWindowSession 是一个接口类,真正实现该接口的是 Session 类。
Session
源码位置:/frameworks/base/services/core/java/com/android/server/wm/Session.java
final WindowManagerService mService;@Override
public int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs,int viewVisibility, int displayId, int userId, InsetsState requestedVisibility,InputChannel outInputChannel, InsetsState outInsetsState,InsetsSourceControl[] outActiveControls) {return mService.addWindow(this, window, attrs, viewVisibility, displayId, userId,requestedVisibility, outInputChannel, outInsetsState, outActiveControls);
}
可以看到,最终是通过 WMS 来完成添加的。需要注意的是,WMS 并不关心 View 的具体内容,他只关心各个应用显示的界面大小、层级值等,这些数据到包含在 WindowManager.LayoutParams 中。也就是上面的 atrs 属性。
addWindow 的第二个参数是一个 IWindow 类型,这是 App 暴露给 WMS 的抽象实例,在 ViewRootImp 中实例化,与 ViewRootImpl 一一对应,同时也是 WMS 向 App 端发送消息的 Binder 通道。
二、WMS窗口添加
WindowManagerService
源码位置:/frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
public int addWindow(Session session, IWindow client, LayoutParams attrs, int viewVisibility,int displayId, int requestUserId, InsetsState requestedVisibility,InputChannel outInputChannel, InsetsState outInsetsState,InsetsSourceControl[] outActiveControls) {Arrays.fill(outActiveControls, null);int[] appOp = new int[1];final boolean isRoundedCornerOverlay = (attrs.privateFlags & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0;// 1.检查权限,mPolicy的实现类是PhoneWindowManager。int res = mPolicy.checkAddPermission(attrs.type, isRoundedCornerOverlay, attrs.packageName, appOp);if (res != ADD_OKAY) {return res;}……synchronized (mGlobalLock) {……// 2.通过displayId获取Window要添加到哪个DisplayContent。final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token);if (displayContent == null) {// 没有找到对应的显示屏幕return WindowManagerGlobal.ADD_INVALID_DISPLAY;}……// 3.判断type的窗口类型(100-1999),如果是子类型,必须要有父窗口,并且父窗口不能是子窗口类型。if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {parentWindow = windowForClientLocked(null, attrs.token, false);if (parentWindow == null) {// 试图添加带有非窗口令牌的窗口return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;}if (parentWindow.mAttrs.type >= FIRST_SUB_WINDOW && parentWindow.mAttrs.type <= LAST_SUB_WINDOW) {// 试图添加带有子标记的窗口return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;}}……ActivityRecord activity = null;final boolean hasParent = parentWindow != null;// 4.对子窗口使用与父窗口使用的令牌,因此我们可以对它们应用相同的策略。WindowToken token = displayContent.getWindowToken(hasParent ? parentWindow.mAttrs.token : attrs.token);// 如果这是一个子窗口,与父窗口类型相同的检查规则。final int rootType = hasParent ? parentWindow.mAttrs.type : type;boolean addToastWindowRequiresToken = false;// 5. token为null。if (token == null) {if (hasParent) {// 对子窗口使用现有的父窗口令牌。token = parentWindow.mToken;} else if (mWindowContextListenerController.hasListener(windowContextToken)) {// 如果用户提供的话,尊重窗口上下文令牌final IBinder binder = attrs.token != null ? attrs.token : windowContextToken;final Bundle options = mWindowContextListenerController.getOptions(windowContextToken);token = new WindowToken.Builder(this, binder, type).setDisplayContent(displayContent).setOwnerCanManageAppTokens(session.mCanAddInternalSystemWindow).setRoundedCornerOverlay(isRoundedCornerOverlay).setFromClientToken(true).setOptions(options).build();} else {// 6.且不是应用窗口或者是其他类型的窗口,则窗口就是系统类型(例如 Toast)。// 进行隐式创建 WindowToken,这说明我们添加窗口时是可以不向WMS提供WindowToken的。final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();token = new WindowToken.Builder(this, binder, type).setDisplayContent(displayContent).setOwnerCanManageAppTokens(session.mCanAddInternalSystemWindow).setRoundedCornerOverlay(isRoundedCornerOverlay).build();}} else if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {// 7、判断是否为应用窗口,如果是,将WindowToken转换为应用程序窗口的ActivityRecord。activity = token.asActivityRecord();if (activity == null) {// 试图添加带有非应用程序令牌的窗口return WindowManagerGlobal.ADD_NOT_APP_TOKEN;} else if (activity.getParent() == null) {// 试图添加具有退出应用程序的窗口return WindowManagerGlobal.ADD_APP_EXITING;} else if (type == TYPE_APPLICATION_STARTING) {if (activity.mStartingWindow != null) {// 试图用已经存在的开始窗口向令牌添加开始窗口return WindowManagerGlobal.ADD_DUPLICATE_ADD;}if (activity.mStartingData == null) {// 试图向令牌添加起始窗口,但已被清除return WindowManagerGlobal.ADD_DUPLICATE_ADD;}}} else if (rootType == TYPE_INPUT_METHOD) {if (token.windowType != TYPE_INPUT_METHOD) {// 试图添加带有错误令牌的输入法窗口return WindowManagerGlobal.ADD_BAD_APP_TOKEN;}}……// 8.创建WindowState,保存窗口的所有状态信息,在WMS中,WindowState与窗口是一一对应的关系。final WindowState win = new WindowState(this, session, client, token, parentWindow,appOp[0], attrs, viewVisibility, session.mUid, userId, session.mCanAddInternalSystemWindow);// 9.判断请求添加窗口的客户端是否已经死亡,如果死亡则不会执行下面逻辑。if (win.mDeathRecipient == null) {return WindowManagerGlobal.ADD_APP_EXITING;}if (win.getDisplayContent() == null) {return WindowManagerGlobal.ADD_INVALID_DISPLAY;}final DisplayPolicy displayPolicy = displayContent.getDisplayPolicy();// 10.根据窗口的type类型对窗口的LayoutParams的一些成员变量进行修改。displayPolicy.adjustWindowParamsLw(win, win.mAttrs);win.updateRequestedVisibility(requestedVisibility);// 11.准备将窗口添加到系统中res = displayPolicy.validateAddingWindowLw(attrs, callingPid, callingUid);if (res != ADD_OKAY) {return res;}……// 12.将WindowState添加到mWindowMap中,mWindowMap是各种窗口的集合。mWindowMap.put(client.asBinder(), win);……boolean imMayMove = true;// 13.添加窗口。将WindowState添加到对应的WindowToken中(实际上就是保存在WindowToken的父类 WindowContainer),这样WindowToken就包含了相同组件的WindowState。win.mToken.addWindow(win);}return res;
}
WMS 的 addWindow 方法返回的是 addWindow 的各种状态,例如添加成功、失败、无效的 display 等,这些状态定义在 WindowManagerGloabl 中 。
通过上面的流程,App 到 WMS 注册窗口的流程就完了,WMS 为窗口创建了用来描述状态的 WindowState,接下来就会为新建的窗口显示次序,然后再去申请 Surface,才算是真正的分配了窗口。
这里对 WMS 的 addWindow 流程做一个总结 :
首先检查权限
接着从 mRoot(RootWindowContainer)中获取 DisplayContent ,如果没有就会根据 displayId 创建一个新的 DisplayContent
接着就是 type 类型的判断,如果是子类型,就必须要获取到他的父窗口,
接着使用 DisplayContent 获取当前或者父窗口获取 token,如果为 null 就排除子窗口和其他的窗口,剩下的就是可以不用携带 token 的窗口,WMS 会隐式的创建窗口 token。如果不等于 null 就判断是应用窗口就将 token 转为 ActivityRecord,后面还有一大堆窗口判断,只要是不满足就直接 return。
类型判断完成后,就会创建 WindowState,并且传入 WMS、IWindow、token 等。WindowState 里面保存了窗口的所有信息。WindowState 与窗口一一对应。
接着就执行调用了 WindowState 的 attache 、initAppOpsState 等方法。WindowState 创建完成后就会被添加到 mWindowMap 中,可以 IWindow 的 Binder 为 key,WindowState 为 value 添加进去。
最后就是 win.mToken.addWindow(win) ,然后将 WindowState 添加到 WindowToken 中。因为 WindowToken 是可以复用的,所以这里的关系就是,每个 WindowToken 都会保存对应的 WindowState,而每个 WindowState 也都会都持有 WindowToekn。
相关文章:
Android WMS——WMS窗口添加(十)
Android 的 WMS(Window Manager Service)是一个关键组件,负责管理窗口的创建、显示、布局和交互等。Window 的操作有两大部分,一部分是 WindowManager 来处理,一部分是 WMS 来处理,如下图所示: …...
CVPR 2023 | 主干网络FasterNet 核心解读 代码分析
本文分享来自CVPR 2023的论文,提出了一种快速的主干网络,名为FasterNet。 论文提出了一种新的卷积算子,partial convolution,部分卷积(PConv),通过减少冗余计算和内存访问来更有效地提取空间特征。 创新在于部分卷积…...
【进阶C语言】数组笔试题解析
本节内容以刷题为主,大致目录: 1.一维数组 2.字符数组 3.二维数组 学完后,你将对数组有了更全面的认识 在刷关于数组的题目前,我们先认识一下数组名: 数组名的意义:表示数组首元素的地址 但是有两个例外…...
vue-router学习(四) --- 动态添加路由
我们一般使用动态添加路由都是后台会返回一个路由表前端通过调接口拿到后处理(后端处理路由)。比如不同权限显示不同的路由。 主要使用的方法就是router.addRoute 添加路由 动态路由主要通过两个函数实现。router.addRoute() 和 router.removeRoute()。它们只注册一个新的路…...
科东软件受邀参加2023国家工业软件大会,共话工业软件未来
10月28日,由中国自动化学会主办的2023国家工业软件大会在浙江湖州开幕。大会以“工业软件智造未来”为主题,一批两院院士、千余名专家学者齐聚一堂,共同探讨工业软件领域前沿理论和技术创新应用问题,共同谋划我国工业软件未来发展…...
ros启动节点的launch文件你真的会写吗?
<launch><!-- 启动节点 --><node name="lidar_data_feature_detection_node" pkg="lidar_data_feature_detection" type="lidar_data_feature_detection" output="screen" />...
AMEYA360:循序积累立体布局,北京君正实景展示AI-ISP
北京君正集成电路股份有限公司(下称“北京君正”)是国内较早深耕智能安防及泛视觉解决方案的芯片供应商之一,也是国内同时掌握CPU、VPU、ISP、AIE等核心技术的创新企业之一,自成立以来始终深耕行业,并持续迭代创新产品及创新方案。 在2023 CP…...
10.31 知识总结(选择器、css属性相关)
一、选择器 1.1 属性选择器 通过标签的属性来查找标签,标签都有属性 <div class"c1" id"d1"></div> id值和class值是每个标签都自带的属性,还有另外一种:自定义属性 <div class"c1" id"d1…...
【网络协议】聊聊TCP如何做到可靠传输的
网络是不可靠的,所以在TCP协议中通过各种算法等机制保证数据传输的可靠性。生活中如何保证消息可靠传输的,那么就是采用一发一收的方式,但是这样其实效率并不高,所以通常采用的是累计确认或者累计应答。 如何实现一个靠谱的协议&…...
记一次flask框架环境综合渗透测试
PART.01 登入过程 访问靶场地址http://101.43.22.226/?name2023,框架为Flask。 2. 测试存在ssti注入。 3. 直接执行以下命令。 http://101.43.22.226/?name{% for c in [].class.base.subclasses() %} {% if c.name ‘catch_warnings’ %} {% for b in c.i…...
博弈论学习笔记(2)——完全信息静态博弈
前言 这部分我们学习的是完全信息静态博弈,主要内容包括博弈论的基本概念、战略式博弈、Nash均衡、Nash均衡解的特性、以及Nash均衡的应用。 零、绪论 1、什么是博弈论 1)博弈的定义 博弈论:研究决策主体的行为发生直接相互作用时候的决策…...
【COMP304 LEC4 LEC5】
LEC 4 1. Truth-Functionality Propositional logic 的connectives(连接词)are truth-functional 但是,有时候的描述不是true-functional的,比如:"Knowing that", "It is necessary that",&quo…...
表白墙(服务器)
目录 0.需求 1.创建Maven项目 2.给pom.xml内引入三个依赖 3.完善目录,并补充web.xml中的内容 4.编写代码 后端代码 编辑前端代码 5.引入数据库 创建message表 创建工具类 往MessageServlet类中添加方法 0.需求 前面写好了表白墙页面,但存…...
在 Mac 中卸载 Node.js
在 Mac 中卸载 Node.js,可以选择以下两种方法: 使用命令行卸载 Node.js 第一步:打开终端,输入以下命令显示 Node.js 的安装路径: which node 执行该命令后,会显示安装路径:/usr/local/bin/n…...
Hafnium构建选项及FVP模型调用
安全之安全(security)博客目录导读 目录 一、Hafnium构建选项 二、FVP模型调用 一、Hafnium构建选项 本节解释了在支持基于FF-A的SPM (SPMD位于EL3, SPMC位于S-EL1、S-EL2或EL3)的情况下进行构建时涉及的TF-A构建选项:...
第44天:前端及html、Http协议
前端 前端是所有跟用户直接打交道的都可以称之为是前端,比如:PC页面、手机页面、平板页面、汽车显示屏、大屏幕展示出来的都是前端内容。 前端的用处: 学了前端以后我们就可以做全栈工程师(会后端、会前端、会DB、会运维等),能够写一些简单的…...
shell_63.Linux产生信号
Linux 系统信号 信号 值 描述 1 SIGHUP 挂起(hang up)进程 2 SIGINT 中断(interrupt)进程 3 SIGQUIT 停止(stop)进程 9 …...
互联网摸鱼日报(2023-11-01)
互联网摸鱼日报(2023-11-01) 36氪新闻 毫末智行张凯:2023年高阶智能辅助驾驶市场迎来大爆发 撕开三星、金士顿市场,国产老牌存储器企业出海三年,营收翻三倍|insight全球 给医生一双“透视眼”,「锦瑟医疗」专注开…...
AR的光学原理?
AR智能眼镜的光学成像系统 AR眼镜的光学成像系统由微型显示屏和光学镜片组成,可以将其理解为智能手机的屏幕。 增强现实,从本质上说,是将设备生成的影像与现实世界进行叠加融合。这种技术基本就是通过光学镜片组件对微型显示屏幕发出的光线…...
语义分割 实例分割的异同点
语义分割和实例分割是计算机视觉领域中两个相关但不同的任务,它们都涉及对图像像素进行分类和标记,但关注的对象和目标有所不同。 目标对象: 语义分割:语义分割的目标是将图像中的每个像素标记为对应的语义类别,即将…...
label-studio的使用教程(导入本地路径)
文章目录 1. 准备环境2. 脚本启动2.1 Windows2.2 Linux 3. 安装label-studio机器学习后端3.1 pip安装(推荐)3.2 GitHub仓库安装 4. 后端配置4.1 yolo环境4.2 引入后端模型4.3 修改脚本4.4 启动后端 5. 标注工程5.1 创建工程5.2 配置图片路径5.3 配置工程类型标签5.4 配置模型5.…...
React hook之useRef
React useRef 详解 useRef 是 React 提供的一个 Hook,用于在函数组件中创建可变的引用对象。它在 React 开发中有多种重要用途,下面我将全面详细地介绍它的特性和用法。 基本概念 1. 创建 ref const refContainer useRef(initialValue);initialValu…...
阿里云ACP云计算备考笔记 (5)——弹性伸缩
目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...
安宝特方案丨XRSOP人员作业标准化管理平台:AR智慧点检验收套件
在选煤厂、化工厂、钢铁厂等过程生产型企业,其生产设备的运行效率和非计划停机对工业制造效益有较大影响。 随着企业自动化和智能化建设的推进,需提前预防假检、错检、漏检,推动智慧生产运维系统数据的流动和现场赋能应用。同时,…...
1.3 VSCode安装与环境配置
进入网址Visual Studio Code - Code Editing. Redefined下载.deb文件,然后打开终端,进入下载文件夹,键入命令 sudo dpkg -i code_1.100.3-1748872405_amd64.deb 在终端键入命令code即启动vscode 需要安装插件列表 1.Chinese简化 2.ros …...
【2025年】解决Burpsuite抓不到https包的问题
环境:windows11 burpsuite:2025.5 在抓取https网站时,burpsuite抓取不到https数据包,只显示: 解决该问题只需如下三个步骤: 1、浏览器中访问 http://burp 2、下载 CA certificate 证书 3、在设置--隐私与安全--…...
相机从app启动流程
一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...
linux 下常用变更-8
1、删除普通用户 查询用户初始UID和GIDls -l /home/ ###家目录中查看UID cat /etc/group ###此文件查看GID删除用户1.编辑文件 /etc/passwd 找到对应的行,YW343:x:0:0::/home/YW343:/bin/bash 2.将标红的位置修改为用户对应初始UID和GID: YW3…...
Typeerror: cannot read properties of undefined (reading ‘XXX‘)
最近需要在离线机器上运行软件,所以得把软件用docker打包起来,大部分功能都没问题,出了一个奇怪的事情。同样的代码,在本机上用vscode可以运行起来,但是打包之后在docker里出现了问题。使用的是dialog组件,…...
Java线上CPU飙高问题排查全指南
一、引言 在Java应用的线上运行环境中,CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时,通常会导致应用响应缓慢,甚至服务不可用,严重影响用户体验和业务运行。因此,掌握一套科学有效的CPU飙高问题排查方法&…...
