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。

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

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

相关文章

Android渲染流程

目录 缓冲区的不同生命周期代表当前缓冲区的状态&#xff1a; 多个源 ViewRootImpl&#xff1a; Android4.0&#xff1a; Android5.0&#xff1a; Android应用程序调用SurfaceFliger将测量&#xff0c;布局&#xff0c;绘制好的Surface借助GPU渲染显示到屏幕上。 一个Acti…

搜维尔科技:Varjo在心理学、医学研究、技术、工程学等领域都在使用

该软件用于心理学、医学研究、可用性、品牌和营销等领域。vajio头显组合到了运动8.0平台中,提供了在高保真虚拟环境中进行的行为研究,否则这些环境的成本太高,不切实际,甚至无法在现实世界中再现。 在心理学、医学研究、可用性、技术、工程学、市场营销等领域工作的学术和商业研…

【编程语言发展史】C语言的诞生及其影响

目录 一、C语言的历史背景 二、C语言的设计思想 三、C语言的语法特点 四、C语言的应用领域 五、C语言的影响 六、总结 C语言是一种高级计算机编程语言&#xff0c;它的诞生和发展对计算机科学和软件工程领域产生了深远的影响。本文将详细介绍C语言的诞生及其影响&#xf…

分类预测 | Matlab实现KOA-CNN-GRU-selfAttention多特征分类预测(自注意力机制)

分类预测 | Matlab实现KOA-CNN-GRU-selfAttention多特征分类预测&#xff08;自注意力机制&#xff09; 目录 分类预测 | Matlab实现KOA-CNN-GRU-selfAttention多特征分类预测&#xff08;自注意力机制&#xff09;分类效果基本描述程序设计参考资料 分类效果 基本描述 1.Matla…

AI图像识别初次尝试

1.人形识别结果 2.代码 pythonOpenCVyolov3训练库&#xff0c;代码如下&#xff1a; #!/usr/bin/env python3 # -*- coding: utf-8 -*- import cv2 import numpy as np import osimgFiles["pic03.jpg", "pic04.jpg"]netNone classesNone colorsNonedef r…

Python小试牛刀:GUI(图形界面)实现计算器UI界面(二)

上一篇&#xff1a;Python小试牛刀&#xff1a;GUI&#xff08;图形界面&#xff09;实现计算器UI界面&#xff08;一&#xff09;-CSDN博客 在上一篇文章中介绍了Python GUI常用的库&#xff0c;以及运用GUI标准库tkinter仅设计了计算器的UI界面。 而在本篇文章&#xff0c;…

【c++|opencv】二、灰度变换和空间滤波---5.中值滤波

every blog every motto: You can do more than you think. https://blog.csdn.net/weixin_39190382?typeblog 0. 前言 1. 中值滤波 #include<iostream> #include<opencv2/opencv.hpp> #include"Salt.h"using namespace cv; using namespace std;voi…

一篇文章认识【性能测试】

一、 性能测试术语解释 1. 响应时间 响应时间即从应用系统发出请求开始&#xff0c;到客户端接收到最后一个字节数据为止所消耗的时间。响应时间按软件的特点再可以细分&#xff0c;如对于一个 C/S 软件的响应时间可以细分为网络传输时间、应用服务器处理时间、数据库服务器…

python类如何实例化对象

python类如何实例化对象 1、把类看作是定制的数据类型。既然是类型&#xff0c;只能用来表示数据的类型&#xff0c;不能直接用来保存数据。**要保存数据&#xff0c;首先需要创建一个类似于这类容器的东西&#xff0c;称为对象(或例子)。通过类别产生对象的过程称为例子。 2、…

畅销书《Kali Linux高级渗透测试》更新版速速查收~

懒大王感谢大家的关注和三连支持~ 作者简介&#xff1a; 懒大王敲代码&#xff0c;正在学习嵌入式方向有关课程stm32&#xff0c;网络编程&#xff0c;数据结构C/C等 今天给大家推荐畅销书《Kali Linux高级渗透测试》&#xff0c;希望大家能觉得实用&#xff01; 欢迎大家点赞…

C语言--温度转化(把华氏度转换为摄氏度)

一.问题描述: 有人用温度计测量出用华氏法表示的温度,今要求把它转换为以摄氏法表示的温度,转换公式如下: 这个代码是C语言的入门代码&#xff0c;难点在于5/9在程序中如何写出。在计算机中5/9等于1。 /符号&#xff1a;整除问题(整数/整数 结果是丢弃小数的整数) 5/2 2。那…

十五、redis的使用

目录 一、简介1.1 nosql介绍1.2 redis特性1.3 redis优势1.4 redis应用场景 二、安装2.1 Macos下安装2.2 Linux下安装2.4 客户端连接2.5 切换数据库 三、数据库操作3.1 string类型3.2 键的操作3.3 Hash类型3.4 list类型3.5 set类型3.6 zset类型 四、和python交互4.1 安装redis包…

【AI视野·今日Sound 声学论文速览 第三十二期】Tue, 24 Oct 2023

AI视野今日CS.Sound 声学论文速览 Tue, 24 Oct 2023 Totally 20 papers &#x1f449;上期速览✈更多精彩请移步主页 Interesting: &#x1f4da;nvas3d, 基于任意录音和室内3D信息合成重建不同听角&#xff08;位置&#xff09;处的新的声音。(from apple cmu) website: htt…

数据结构详细笔记——二叉树

文章目录 二叉树的定义和基本术语特殊的二叉树满二叉树完全二叉树二叉排序树平衡二叉树 二叉树的常考性质完全二叉树的常考性质二叉树的存储结构顺序存储链式存储 二叉树的先中后序遍历先序遍历&#xff08;空间复杂度&#xff1a;O&#xff08;h&#xff09;&#xff09;中序遍…

有奖快来抱走全新HUAWEI WATCH GT4

亲爱的openGauss用户&#xff0c; 为了给您提供更好的社区体验&#xff0c;现诚邀您参与openGauss社区满意度问卷调研。您的每一个宝贵建议都是我们进步的方向。 手机扫描二维码即可填写问卷&#xff0c;请根据您真实的体验情况填写问卷&#xff0c;问卷反馈越真实有效越有机…

vue3写nav滚动事件中悬停在顶部

1. 防抖类Animate, 使用requestAnimationFrame代替setTimeout 也可以使用节流函数, lodash有现成的防抖和节流方法 _.debounce防抖 _.throttle节流 export default class Animate {constructor() {this.timer null;}start (fn) > {if (!fn) {throw new Error(需要执行…

pmp到底有多难考?

单说通过&#xff0c;难度不算特别高&#xff0c;但也有几点难度&#xff1a; 1、试卷难度&#xff1a;是笔试&#xff0c;180道题&#xff0c;题量大&#xff0c;比较机试耗时间&#xff1b; 2、题目难度&#xff1a;题目是中英文对照&#xff0c;可能有翻译不到位的地方&am…

0基础学习PyFlink——事件时间和运行时间的窗口

大纲 定制策略运行策略Reduce完整代码滑动窗口案例参考资料 在 《0基础学习PyFlink——时间滚动窗口(Tumbling Time Windows)》一文中&#xff0c;我们使用的是运行时间(Tumbling ProcessingTimeWindows)作为窗口的参考时间&#xff1a; reducedkeyed.window(TumblingProcess…

第4章_运算符

文章目录 1. 算术运算符1.1 加法与减法运算符1.2 乘法与除法运算符1.3 求模运算符 2. 比较运算符2.1 等号运算符2.2 安全等于运算符2.3 不等于运算符2.4 空运算符2.5 非空运算符2.6 最小值运算符2.7 最大值运算符2.8 BETWEEN AND运算符2.9 IN运算符2.10 NOT IN运算符2.11 LIKE运…

代码随想录算法训练营第三十九天丨 动态规划part02

62.不同路径 思路 动态规划 机器人从(0 , 0) 位置出发&#xff0c;到(m - 1, n - 1)终点。 按照动规五部曲来分析&#xff1a; 确定dp数组&#xff08;dp table&#xff09;以及下标的含义 dp[i][j] &#xff1a;表示从&#xff08;0 &#xff0c;0&#xff09;出发&#…