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眼镜的光学成像系统由微型显示屏和光学镜片组成,可以将其理解为智能手机的屏幕。 增强现实,从本质上说,是将设备生成的影像与现实世界进行叠加融合。这种技术基本就是通过光学镜片组件对微型显示屏幕发出的光线…...
语义分割 实例分割的异同点
语义分割和实例分割是计算机视觉领域中两个相关但不同的任务,它们都涉及对图像像素进行分类和标记,但关注的对象和目标有所不同。 目标对象: 语义分割:语义分割的目标是将图像中的每个像素标记为对应的语义类别,即将…...
【Oracle APEX开发小技巧12】
有如下需求: 有一个问题反馈页面,要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据,方便管理员及时处理反馈。 我的方法:直接将逻辑写在SQL中,这样可以直接在页面展示 完整代码: SELECTSF.FE…...
Java 8 Stream API 入门到实践详解
一、告别 for 循环! 传统痛点: Java 8 之前,集合操作离不开冗长的 for 循环和匿名类。例如,过滤列表中的偶数: List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...
Day131 | 灵神 | 回溯算法 | 子集型 子集
Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣(LeetCode) 思路: 笔者写过很多次这道题了,不想写题解了,大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...
土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等
🔍 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术,可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势,还能有效评价重大生态工程…...
[Java恶补day16] 238.除自身以外数组的乘积
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O(n) 时间复杂度…...
站群服务器的应用场景都有哪些?
站群服务器主要是为了多个网站的托管和管理所设计的,可以通过集中管理和高效资源的分配,来支持多个独立的网站同时运行,让每一个网站都可以分配到独立的IP地址,避免出现IP关联的风险,用户还可以通过控制面板进行管理功…...
从 GreenPlum 到镜舟数据库:杭银消费金融湖仓一体转型实践
作者:吴岐诗,杭银消费金融大数据应用开发工程师 本文整理自杭银消费金融大数据应用开发工程师在StarRocks Summit Asia 2024的分享 引言:融合数据湖与数仓的创新之路 在数字金融时代,数据已成为金融机构的核心竞争力。杭银消费金…...
怎么让Comfyui导出的图像不包含工作流信息,
为了数据安全,让Comfyui导出的图像不包含工作流信息,导出的图像就不会拖到comfyui中加载出来工作流。 ComfyUI的目录下node.py 直接移除 pnginfo(推荐) 在 save_images 方法中,删除或注释掉所有与 metadata …...
Spring Boot + MyBatis 集成支付宝支付流程
Spring Boot MyBatis 集成支付宝支付流程 核心流程 商户系统生成订单调用支付宝创建预支付订单用户跳转支付宝完成支付支付宝异步通知支付结果商户处理支付结果更新订单状态支付宝同步跳转回商户页面 代码实现示例(电脑网站支付) 1. 添加依赖 <!…...
