Android WMS——WMS窗口添加(十)

news2024/12/28 17:51:02

        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.coloradmin.cn/o/1155995.html

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

相关文章

验收测试的关键步骤是怎样的?

验收测试是项目管理中的一个关键步骤&#xff0c;旨在确保项目交付物(通常是软件、产品或服务)符合预期的质量标准和需求。这个过程有助于验证项目的可交付成果是否满足客户或利益相关者的期望&#xff0c;同时也为项目团队提供了机会来修复可能存在的问题和改进之前的工作。 一…

一个方法,教你快速监测蓄电池!

随着电力需求的不断增长和可再生能源的快速发展&#xff0c;蓄电池技术已经成为能源存储领域的重要组成部分。 蓄电池不仅在家庭和工业应用中发挥着重要作用&#xff0c;还在电网稳定性和可持续能源集成方面具有关键地位。然而&#xff0c;蓄电池的有效监控和管理对于确保其可靠…

【Qt控件之QMessageBox】详解

Qt控件之QMessageBox 描述基于属性的API富文本和文本格式属性严重程度以及图标和Pixmap属性静态函数API 高级用法默认按钮和退出按钮示例使用场景 描述 QMessageBox类提供了一个模态对话框&#xff0c;用于通知用户或向用户提问并接收答案。 消息框显示一个主要文本以提醒用户…

软件测试 —— 冒烟测试(Smoke Test,ST)

1. 核心 冒烟测试就是完成一个新版本的开发后&#xff0c;对该版本最基本的功能进行测试&#xff0c;保证基本的功能和流程能走通。 如果不通过&#xff0c;则打回开发那边重新开发&#xff1b; 如果通过测试&#xff0c;才会进行下一步的测试(功能测试&#xff0c;集成测试&a…

SQLyog连接数据库报plugin caching_sha2_password could not be loaded......解决方案

问题描述 问题分析 因为MySQL新版默认使用caching_sha2_password作为身份验证的插件&#xff0c;而旧版本使用的是mysql_native_password。当出现plugin caching_sha2_password could not be loaded报错&#xff0c;我们更换为旧版本 如何解决 先使用cmd命令登录MySQL&a…

从零开始的LINUX(四)

1.yum&#xff1a; 功能&#xff1a;软件包管理器&#xff0c;功能类似与手机上的应用商店。通过yum可以获取指令的下载地址&#xff0c;然后一键式安装指令。由于yum中的地址一般都是外网的&#xff0c;所以需要镜像源&#xff08;即国内的下载地址&#xff09;。 相关指令&…

2023最网最全软件测试基础知识【建议收藏】

​对于一个软件来说&#xff0c;总会存在各种各样的软件缺陷。因此我们需要通过软件测试来检查软件中存在的各种问题。 在下面的这篇文章中&#xff0c;将讲解软件测试的基础知识&#xff0c;让我们一起来了解一下吧 一、 软件缺陷的概述 1、什么是软件缺陷 ​ 软件缺陷就是…

博弈论学习笔记(3)——完全信息动态博弈

前言 在这个部分&#xff0c;我们学习的是完全信息动态博弈。主要内容包括扩展式博弈、子博弈精炼Nash均衡、重复博弈和子博弈精炼Nash均衡的应用。 一、扩展式博弈 1、扩展式博弈 1&#xff09;扩展式博弈是什么 扩展式博弈是博弈问题的一种规范性描述&#xff0c;扩展式博…

【MATLAB源码-第64期】matlab基于DWA算法的机器人局部路径规划包含动态障碍物和静态障碍物。

操作环境&#xff1a; MATLAB 2022a 1、算法描述 动态窗口法&#xff08;Dynamic Window Approach&#xff0c;DWA&#xff09;是一种局部路径规划算法&#xff0c;常用于移动机器人的导航和避障。这种方法能够考虑机器人的动态约束&#xff0c;帮助机器人在复杂环境中安全、…

java面向对象编程高级

1、static修饰符 1.1static修饰成员变量 static叫静态&#xff0c;可以修饰成员变量、成员方法 成员变量按照有无static修饰&#xff0c;分为两种 类变量 : 有static修饰&#xff0c;属于类&#xff0c;在计算机里只有一份&#xff0c;会被类的全部对象共享 在开发中&#…

处理固定资产折旧报错 AFAB “根据记帐循环, 您必须接下来对期间 001记帐”

会计在运用进行固定资产折旧时&#xff0c;发现有个报错“根据记帐循环, 您必须接下来对期间 001记帐”&#xff0c; 根据记帐循环, 您必须接下来对期间 001记帐 消息编号 AA683 诊断 不可以在指定的期间过帐折旧&#xff0c;因为此操作会遗漏过帐期间。 系统响应 该期间不能进…

Nginx域名重定向(如何访问的域名和实际的数据请求路径不同,可解决前端跨域)

感情需要被抑制&#xff0c;不能泛滥… 当需要将一个域名重定向到另一个域名并且用户仍然看到原始域名时&#xff0c;Nginx是一个强大的工具。这种场景通常涉及到反向代理或重写URL的技巧。在本篇博客中&#xff0c;我们将详细介绍如何使用Nginx来实现这个目标&#xff0c;以及…

SPSS两独立样本t检验

前言&#xff1a; 本专栏参考教材为《SPSS22.0从入门到精通》&#xff0c;由于软件版本原因&#xff0c;部分内容有所改变&#xff0c;为适应软件版本的变化&#xff0c;特此创作此专栏便于大家学习。本专栏使用软件为&#xff1a;SPSS25.0 本专栏所有的数据文件请点击此链接下…

VOD: 视频共享点播服务实现

目录 一.项目背景及原理 1.背景 2.原理 二.技术栈及项目环境 1.技术栈 2.项目环境 三. 模块划分 四.遇到的问题及其解决方法 1.查询视频模糊匹配问题 2.前端界面的修改 五.项目预览 六. 最终版代码 前言: 这里实现一个视频点播服务, 可以用来上传视频, 下载视频, 删…

springboot读取application.properties中文乱码问题

目录 1 前言&#xff1a; 2 本地环境中的解决方案&#xff08;以idea为例&#xff09; 3 全部解决方案 1 前言&#xff1a; 初用properties,读取java properties文件的时候如果value是中文&#xff0c;会出现乱码的问题。我们首先需要明了乱码问题的根源。在 Java 中&#x…

基于jsp,ssm物流快递管理系统

开发工具&#xff1a;eclipse&#xff0c;jdk1.8 服务器&#xff1a;tomcat7.0 数据库&#xff1a;mysql5.7 技术&#xff1a; springspringMVCmybaitsEasyUI 项目包括用户前台和管理后台两部分&#xff0c;功能介绍如下&#xff1a; 一、用户(前台)功能&#xff1a; 用…

Servlet 上下文参数

7)Servlet上下文对象&#xff1a;ServletContext生活中的例子&#xff1a;张三和李四在不远处窃窃私语&#xff0c;并且频繁的对着你坏笑。你肯定会跑过去问&#xff1a;你们俩在聊什么&#xff1f;注意&#xff1a;此处的聊什么&#xff0c;其实就是你在咨询他们聊天的上下文&…

九河云多云管理平台优劣势分析,使用评测!

多数情况下&#xff0c;仅依靠公有云不足以支撑企业发展。企业的某些特定需求还需要继续依赖私有云和传统IT服务。混合、多云架构往往是企业的唯一选择&#xff0c;但其设置和运行却非常复杂&#xff0c;为了管理混合架构&#xff0c;大多数企业已逐步转向多云模式&#xff0c;…

Spring Task(定时任务)框架

文章目录 一、Spring Task介绍二、cron表达式1.cron表达式介绍2.cron表达式在线生成器 三、fixedDelay四、fixedRate五、initialDelay六、Spring Task的使用1.导入maven坐标spring-context2.启动类添加注解EnableScheduling开启任务调度3.自定义定时任务类 一、Spring Task介绍…

Realtek 5G pcie网卡 RTL8126-CG简介

总shu&#xff1a;PCIE 5G网卡方案“RTL8126-CG”采用QFN56封装&#xff0c;面积8 x 8毫米&#xff0c;非常小巧&#xff0c;提供一个RJ-45网口、两个USB 3.x接口。它走的是PCIe 3.0 x1系统通道&#xff0c;搭配超五类网线&#xff0c;可以在长达100米的距离上提供满血的5Gbps网…