Android 窗口那些事儿

目录

1. 📂 前言

你,是否有过这些疑问?

2. 🔱 Window

2.1 认识 Window 的几个阶段

1)阶段一:Window 约等于 Activity

2)阶段二:Window 约等于 View

3)阶段三:Window 是个抽象封装概念

2.2 Android 中的 Window 定义

2.3 Window 到底是什么?

3. 💠 Window 相关

3.1 WindowManager

1)入参1:View

2)入参2:WindowManager.LayoutParams

3) 三个方法

3.2 DecorView 与 ViewRootImpl

3.3 WindowManagerService(WMS)

3.4 Layer

3.5 SurfaceFlinger

4. ⚛️ Window 深入

4.1 Activity/Window/View 的关系

4.2 Activity/Window/DecorView/ViewRootImpl 的创建时机

4.3 DecorView 什么时候被 WindowManager 添加到 Window 中

4.4 Android 多窗口原理

4.5 Window 点击与双击事件的区别

5. ✅ Window  底层

5.1 Window/View 添加过程

5.2 布局加载流程

5.3 View 绘制流程

1)addView 流程

2)performTraversals 流程

3)Measure、Layout、Draw 三大流程

5.4 View 绘制屏幕刷新

5.5 View 事件分发机制

这些疑问,你都解决了吗?


1. 📂 前言

你,是否有过这些疑问?

        Android 中窗口的定义是什么?

        Activity、Window、View 之间的关系?

        窗口到 View 的事件分发机制,是怎样的?

        WMS 是如何管理屏幕上显示的诸多窗口的?

        Android 是默认单窗口吗?多窗口怎么实现?

        SurfaceFlinger 合成的 Layer 与窗口是一一对应的吗?

        SurfaceFlinger、Layer、Window,以及 WMS 的联系?

        点击的事件分发与双击的事件分发,为什么走的是不一套机制?

下面,就一起去探讨下这些问题吧。有些理解只是抛砖引玉,并未完全解答,还请见谅。

2. 🔱 Window

        首先这样一个场景,下图包含:Activity、Dialog、Toast。 

  • 问:图里有几个窗口?

  • 答:三个,Activity 应用窗口,Dialog 子窗口,Toast 系统窗口。

        我们知道,Android 中的窗口,其实就是由以上三种组成,分别是应用 Window、子 Window、系统 Window。

2.1 认识 Window 的几个阶段

1)阶段一:Window 约等于 Activity

        刚接触 Android 时,认为 Activity 就是一个显示文本、图片的界面,所以 Window 就约等于 Activity。

2)阶段二:Window 约等于 View

        在接触 Window、WMS 概念后,知道 Activity 内部持有 Window 对象,而 Window 实现类 PhoneWindow 内部持有 DecorView 作为根布局,开发人员编写的 ContentView 会添加到 DecorView 中,所以 Window 就约等于 View。

3)阶段三:Window 是个抽象封装概念

        在深入 Window、WMS 源码后,我们知道:

  • 每一个 Window 都对应着一个 View 和 一个 ViewRootImpl;

  • Window 作为 View 对象的容器,以 View 的方式存在,所以 Window 可以称之为 View 的直接管理者;

  • Window 并不是实际存在的,它表示一个窗口的概念,也是一个抽象的概念,封装了对窗口的操作逻辑。

2.2 Android 中的 Window 定义

        接下来,我们看看源码中 Window 类的注释:

/*** Abstract base class for a top-level window look and behavior policy.  An* instance of this class should be used as the top-level view added to the* window manager. It provides standard UI policies such as a background, title* area, default key processing, etc.** <p>The only existing implementation of this abstract class is* android.view.PhoneWindow, which you should instantiate when needing a* Window.*/
public abstract class Window {}

        结合 Window 类的注释,我们知道 Window 就是一层封装,提供通用页面模板,并不是真正的窗口。

2.3 Window 到底是什么?

        Window 是一个抽象的概念,对应屏幕显示图像的一块区域,实际是 View。比如:前言中的图例情景,共包含3个 Window,分为3块区域,对应3个具体 View,当在 Dialog 的 Window 中添加、更新与删除 View,是不会影响到 Activity 和 Toast 的。

        微观角度,Window 就是 View;宏观角度,Window 是 WMS 窗口体系下的最小单位。

        其实,根本就没有具体的 Window,只有具体的 View。

3. 💠 Window 相关

3.1 WindowManager

        WindowManager 是一个接口,继承自 ViewManager 接口,它的实现类为 WindowManagerImpl,WindowManagerImpl 通过桥接模式,将所有操作全部委托给 WindowManagerGlobal 来实现。

        WindowManager 是我们访问 Window 的入口,使用 WindowManager 对 Window 进行添加、更新和删除,具体工作则由 WMS 来处理,WindowManager 和 WMS 通过 Binder 来进行跨进程通信。

        接下来,让我们先从 WindowManager.addView(view, layoutParams) 方法的2个入参开始聊起。

1)入参1:View

        View 表示需要在屏幕显示的内容,它是具体的。

2)入参2:WindowManager.LayoutParams

        LayoutParams 对内容进行约束,包括宽高、位置、类型和 flag 等。

LayoutParams.type

        表示 Window 的类型,Window 共有三种类型,分别是应用 Window、子 Window、系统 Window。

  1. 应用 Window:1 ~ 99,如:Activity 的 type 为2;

  2. 子 Window:1000 ~ 1999,如:PopupWindow 的 type 默认为1000,Dialog 的 type 为1003;

  3. 系统 Window:2000~ 2999,如:状态栏 type 为2000,Toast  type 为2005,悬浮窗口 type 为2038。

        数值越大层级越高,层级高覆盖层级低的,一般通过常量设置,系统 Window 需要申请权限。

LayoutParams.flags

        设置 Window 相关的属性,常用:FLAG_NOT_FOCUSABLE、FLAG_NOT_TOUCH_MODA、FLAG_SHOW_WHEN_LOCKED 等。

  1. FLAG_NOT_FOCUSABLE:表示窗口不需要获取焦点,也不需要接收各种事件,此属性会同时启动FLAG_NOT_TOUCH_MODAL,最终的事件会传递给下层拥有焦点的 Window;

  2. FLAG_NOT_TOUCH_MODAL:将 Window 区域以外的单击事件传递给下层的 Window, 当前 Window 内的单击事件自己处理, 一般都要开启此事件,否则其他 Window 无法收到单击事件;

  3. FLAG_SHOW_WHEN_LOCKED:可以将 Window 显示在锁屏的界面上。

3) 三个方法

        WindowManager 常用的有三个方法,addView、updateView 和removeView,这三个方法定义在 ViewManager 中,而 WindowManager 继承自 ViewManager。

package android.view;public interface ViewManager {public void addView(View view, ViewGroup.LayoutParams params);public void updateViewLayout(View view, ViewGroup.LayoutParams params);public void removeView(View view);
}

3.2 DecorView 与 ViewRootImpl

        DecorView 是 FrameLayout 的子类,被认为是 Android 视图树的根节点视图。

        ViewRootImpl 是一个抽象的概念,相比于 DecorView 这种“实体”根节点,它是一个虚拟的根节点。

        Measure-Layout-Draw 三大 View 绘制流程、触屏和按键等 Input 事件传递,以及 Insets 的更新、大部分需要遍历 View 层级结构的流程,起点都是在 ViewRootImpl。

        简单来说,ViewRootImpl 不仅是 View 和 WM 的桥梁,也是事件分发的桥梁,是视图系统的核心。

3.3 WindowManagerService(WMS)

        WMS 是 Android 系统中的重要服务,管理所有窗口,也是输入事件的中转站。

        对 WMS 来说,一个窗口就是一个可以用来显示的 View 类,一个通过 WindowManagerGlobal.addView() 添加的 View。

        View 本身并不能直接从 WMS 中接收消息,而是通过实现了 IWindow 接口的 ViewRootImpl.W 类来实现。

3.4 Layer

        Layer 是一个比 Window 更底层的实现,代表屏幕上一块显示内容的区域。

        一个 Window 就是一个 Surface,对应一个 Layer。

3.5 SurfaceFlinger

        SurfaceFlinger 作用是合成所有 Layer 并送显。在 App 请求创建 Surface 时,SurfaceFlinger 会创建一个 Layer,在拿到从 WMS 传过来的 Window 宽高、位置,以及 App 提供需要绘制的 View 后合成该 Layer,然后送给屏幕进行显示。

        在 Android 平台上创建的每个 Window 都由 Surface 提供支持,所有被渲染的可见 Surface 都被 SurfaceFlinger 合成到屏幕。

 

4. ⚛️ Window 深入

4.1 Activity/Window/View 的关系

  1. Activity:是最上层的封装,屏蔽复杂的系统实现细节,抽象出 UI 生命周期,方便开发人员专注于界面编写,持有 Window;

  2. Window:也是一层封装,提供通用页面模板,作为视图承载器,负责视图控制,持有 DecorView,DecorView 就是 View 的根布局;

  3. View:就是视图,在 Activity.setContentView 中将 View 视图添加到 DecorView 中,一个页面通用且不变的部分交给 PhoneWindow 实现,变化的部分交给 View,让开发人员能够自由定制。

        Activity 很庞大,通过采用单一职责原则,把 View 相关的代码从 Activity 中剥离到 Window 中去。

4.2 Activity/Window/DecorView/ViewRootImpl 的创建时机

  1. Activity:在 ActivityThread 的 performLaunchActivity 方法中,通过 Instrumentation 在内部使用类加载器创建 Activity 实例;

  2. Window:在 ActivityThread 的 performLaunchActivity 方法中,调用 Activity 的 attach 方法 new 一个 PhoneWindow 然后赋值给 mWindow 成员变量;

  3. DecorView:在 Activity 的 onCreate 方法中调用 setContentView 方法,最后会调用 PhoneWindow 的 generateDecor 方法 new 一个 DecorView 然后赋值给 mDecor 成员变量;

  4. ViewRootImpl

    1. 在 ActivityThread 的 handleResumeActivity 方法中,内部通过 WindowManagerGlobal 的 addView 方法 new 一个 ViewRootImpl 然后赋值给局部变量 root(ViewRootImpl);

    2. 通过 addView 方法,我们也注意到,一个 Window 对应一个 ViewRootImpl;

    3. 此时也会调用 setView 方法与 DecorView 进行绑定:ViewRootImpl 会声明 mView 成员变量,并在 setView 方法中给 mView 对象赋值;

    4. 和 PhoneWindow 一样会持有 DecorView,但 DecorView 的创建还是在 PhoneWindow 的;

4.3 DecorView 什么时候被 WindowManager 添加到 Window 中

  1. 即使 Activity 布局已成功添加到 DecorView 中,但 DecorView 此时还没有添加到 Window 中;

  2. 在 ActivityThread 的 handleResumeActivity 方法中,首先会调用 Activity.onResume 方法,接着调用 Activity.makeVisible 方法,在 makeVisible() 中完成 DecorView 的添加和显示。

4.4 Android 多窗口原理

        简单来说,多窗口框架的核心思想是分栈设置栈边界

  1. 分栈:在 Android 系统中,启动一个 Activity 之后,必定会将此 Activity 存放于某一个 Stack,Android 为了支持多窗口,在运行时创建了多个 Stack;

  2. 栈边界:在多窗口框架中,通过设置 Stack 的边界来控制里面每个 Task 的大小,最终 Task 的大小决定了里面的 Activity 的窗口大小。

4.5 Window 点击与双击事件的区别

  1. 单击走的是 MotionEvent、双击走的是 KeyEvent;

  2. MotionEvent 会返回位置坐标,KeyEvent 不会上报位置坐标。

5. ✅ Window  底层

5.1 Window/View 添加过程

  1. Window/View

  2. WindowManager.addView()

  3. WindowManagerImpl.addView()

  4. WindowManagerGlobal.addView()

  5. ViewRootImpl.setView()

  6. WindowManagerService

        WindowMangerImpl 通过桥接模式,将所有操作全部委托给 WindowManagerGlobal 来实现。

        不管是 Activity 窗口还是非 Activity 窗口,最终都要通过 ViewRootImpl.setView 向 WMS 注册窗口。

5.2 布局加载流程

  1. setContentView 中通过 LayoutInflate.inflate 加载对应布局;

  2. inflate 方法中首先调用 Resources.getLayout 通过 pull 方式 IO 加载 Xml 布局解析器到内存中;

  3. createViewFromTag 根据 xml 的 Tag 标签反射创建 View 到内存;

  4. 递归构建其中子 View,并将子 View 添加到父 ViewGroup 中。

        Android 这套布局加载流程的性能瓶颈:布局文件解析中的 IO 过程;创建 View 对象时的反射过程。

5.3 View 绘制流程

1)addView 流程
  1. ActivityThread.handleResumeActivity;

  2. WindowManagerImpl.addView;

  3. WindowMangerGlobel.addView;

  4. ViewRootImpl.setView;

  5. ViewRootImpl.scheduleTraversals;

  6. ViewRootImpl.doTraveral;

  7. ViewRootImpl.performTraversals。

2)performTraversals 流程
  1. performMeasure—DecorView.measure—DecorView.onMeasure—View.measure;

  2. performLayout—DecorView.layout—DecorView.onLayout—View.layout;

  3. performDraw—DecorView.draw—DecorView.onDraw—View.draw。

3)Measure、Layout、Draw 三大流程
  1. Context.startActivity;

  2. ActivityThread.handleLaunchActivity,执行 onCreate,完成 DecorView 和 Activity 的创建;

  3. ActivityThread.handleResumeActivity,执行 onResume,完成 DecorView 添加到 WindowManager;

  4. ViewRootImpl.performTraversals(),测量、布局、绘制, 从 DecorView 自上而下遍历整个 View 树。

5.4 View 绘制屏幕刷新

  1. CPU:主要负责 Measure、Layout、Record、Execute 数据计算工作;

  2. GPU:负责栅格化(向量图形格式表示的图像转换成位图用于显示器)、渲染,渲染好后放到 buffer(图像缓冲区)里存起来;

  3. Display:屏幕或显示器会以一定的帧率刷新,每次刷新时,就会从缓存区将图像数据读取显示出来,如果缓存区没有新数据,就一直用旧数据,这样屏幕看起来就没有变。

        CPU 准备数据,通过 Driver 层把数据交给 GPU 渲染,Display 负责消费显示内容。

5.5 View 事件分发机制

  1. 定义:将事件(MotionEvent)传递到某个具体的 View & 处理的整个过程;

  2. ViewRootImpl—DecorView—Activity—Window—DecorView(ViewGroup)—View:事件先到 DecorView 然后到 Window。当屏幕被触摸时,input 系统事件从 Native 层分发到 Framework 层,然后到 ViewRootImpl 的 DecorView,进一步到 Activity -> PhoneWindow -> DecorView,最后到 View;

  3. DecorView 怎么将事件分发给 Activity 的?

                a、在 DecorView 的 dispatchKeyEvent 方法内部通过 Window 获取 callback,然后执行 callback 的 dispatchKeyEvent ;

                b、Activity 本身实现了 Window.Callback 接口,并设置给了 Window 的 callback,所以这里的 callback 其实就是 Activity,这样事件就传递到了 Activity;

        为什么 DecorView 走了两遍:主要原因就是解耦。

  1. ViewRootImpl 并不知道 Activity 的存在,它只是持有了 DecorView,所以先传给了 DecorView,而 DecorView 知道有 Activity,所以传给了 Activity;

  2. Activity 也不知道有 DecorView ,它只是持有 PhoneWindow,于是这样一段调用链就形成了。


 

这些疑问,你都解决了吗?

        Android 中窗口的定义是什么?

        Activity、Window、View 之间的关系?

        窗口到 View 的事件分发机制,是怎样的?

        WMS 是如何管理屏幕上显示的诸多窗口的?

        Android 是默认单窗口吗?多窗口怎么实现?

        SurfaceFlinger 合成的 Layer 与窗口是一一对应的吗?

        SurfaceFlinger、Layer、Window,以及 WMS 的联系?

        点击的事件分发与双击的事件分发,为什么走的是不一套机制?


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

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

相关文章

轻松设置Facebook自动隐藏评论和删除评论功能

Facebook作为海外营销的最大流量平台之一&#xff0c;是很多跨境卖家争夺的市场&#xff0c;希望可以通过Facebook这个全球性的平台来推广自己的产品或服务。身处这个竞争激烈的市场&#xff0c;任何一条负面评论或不当言论出现在你的品牌页面上都可能影响到品牌形象&#xff0…

vue + koa + Sequelize + 阿里云部署 + 宝塔:宝塔数据库连接

之前文章已经介绍了宝塔上传前后端代码并部署&#xff0c;不清楚的请看这篇文章&#xff1a; vue koa 阿里云部署 宝塔&#xff1a;宝塔前后端部署 下面是宝塔创建数据库&#xff1a; 我用的 koa Sequelize 连接的数据库&#xff0c;Sequelize 非常适合前端使用&#xf…

爬虫 新闻网站 并存储到CSV文件 以红网为例 V2.0 (控制台版)升级自定义查询关键词、时间段,详细注释

爬虫&#xff1a;红网网站&#xff0c; 获取指定关键词与指定时间范围内的新闻&#xff0c;并存储到CSV文件 V2.0&#xff08;控制台版&#xff09; 爬取目的&#xff1a;为了获取某一地区更全面的在红网已发布的宣传新闻稿&#xff0c;同时也让自己的工作更便捷 对比V1.0升级的…

为何一个网卡需要配置多个IP地址?

在Linux环境中&#xff0c;一个网卡配置多个IP地址是一个常见且强大的网络管理策略&#x1f6e0;️。这种策略不仅增加了网络的灵活性和效率&#xff0c;还能满足特定的网络需求和应用场景&#x1f3af;。让我们一探究竟&#xff0c;看看在哪些情况下&#xff0c;为什么一个网卡…

java内存模型和线程

java内存模型 一个服务端需要同时对多个客户端提供服务&#xff0c;就是并法的具体场景。 衡量一个服务性能的好坏&#xff0c;每秒实务处理数&#xff08;Transactions Per Second,TPS)是重要指标&#xff0c;它代表一秒内服务端平均能响应的请求总数。 硬件的效率和一致性 …

2013年认证杯SPSSPRO杯数学建模C题(第二阶段)公路运输业对于国内生产总值的影响分析全过程文档及程序

2013年认证杯SPSSPRO杯数学建模 C题 公路运输业对于国内生产总值的影响分析 原题再现&#xff1a; 交通运输作为国民经济的载体&#xff0c;沟通生产和消费&#xff0c;在经济发展中扮演着极其重要的角色。纵观几百年来交通运输与经济发展的相互关系&#xff0c;生产水平越高…

开源流程图表库(04):mxGraph,都是可视化编辑,导出使用。

mxGraph是一个用于创建和展示图形的JavaScript库。它提供了丰富的功能和工具&#xff0c;可以用于构建各种类型的图形应用程序&#xff0c;包括流程图、组织结构图、网络拓扑图等。 mxGraph的编辑器 一、mxGraph的特点和功能 以下是一些mxGraph的特点和功能&#xff1a; 强大…

WordPress建站教程:10步快速搭建个人网站

WordPress是一个广泛使用的内容管理系统&#xff08;CMS&#xff09;&#xff0c;凭借其用户友好的界面和大量可定制的主题和插件&#xff0c;为WordPress 提供了多功能性和灵活性&#xff0c;可用于创建各种类型的网站&#xff0c;包括个人博客、B2B企业网站、B2C外贸网站等&a…

jvm基础三——类加载器

类加载器 在Java中&#xff0c;类加载器&#xff08;Class Loader&#xff09;是Java虚拟机&#xff08;JVM&#xff09;的一部分&#xff0c;负责将类文件&#xff08;.class文件&#xff09;加载到JVM中&#xff0c;使得程序能够使用这些类。类加载器在Java中具有重要的作用&…

OSError: Can‘t load tokenizer for ‘bert-base-chinese‘

文章目录 OSError: Cant load tokenizer for bert-base-chinese1.问题描述2.解决办法 OSError: Can’t load tokenizer for ‘bert-base-chinese’ 1.问题描述 使用from_pretrained()函数从预训练的权重中加载模型时报错&#xff1a; OSError: Can’t load tokenizer for ‘…

达梦数据库 优化

谁进行优化&#xff1f;优化什么&#xff1f; 优化不能仅从数据库方面考虑&#xff0c;比如&#xff0c;在存储达到数据库极限、应用涉及人员设计的代码稀巴烂的情况下&#xff0c;进行调优就是杯水车薪的效果。 涉及到优化人员&#xff1a; 数据库管理员应用程序架构师应用…

数据结构|排序总结(1)|直接插入排序

排序分类 插入排序&#xff1a;直接插入排序&#xff0c;希尔排序 选择排序&#xff1a;选择排序&#xff0c;堆排序 交换排序&#xff1a;冒泡排序&#xff0c;快速排序 归并排序 插入排序 直接插入排序 相当于摸牌&#xff0c;例如我们现在手上有{2&#xff0c;4&#xff0…

MySql并发事务问题

事务 事务概念&#xff1a; 事务是一组操作的集合&#xff0c;它是一个不可分割的工作单位&#xff0c;事务会把所有的操作作为一个整体一起向系统提交或撤销操作请求&#xff0c;即这些操作要么同时成功&#xff0c;要么同时失败。 事务的特性&#xff1a;ACID&#xff1a; 小…

Qt元对象系统

第二章Qt元对象系统 文章目录 第二章Qt元对象系统1.什么是元对象&#xff1f;2.元对象系统组成3.信号与槽信号和槽的本质绑定信号与槽自定义槽定义槽函数必须遵循一下规则槽函数的类型自定义槽案例 自定义信号自定义信号需要遵循以下规则信号和槽重载二义性问题 4.内存管理1. 简…

Unity性能优化篇(十四) 其他优化细节以及UPR优化分析器

代码优化&#xff1a; 1. 使用AssetBundle作为资源加载方案。 而且经常一起使用的资源可以打在同一个AssetBundle包中。尽量避免同一个资源被打包进多个AB包中。压缩方式尽量使用LZ4&#xff0c;少用或不要用LZMA的压缩方式。如果确定后续开发不会升级Unity版本&#xff0c;则可…

【Java EE】初识Spring Web MVC

文章目录 &#x1f334;什么是Spring Web MVC&#xff1f;&#x1f338;什么是Servlet呢? &#x1f332;MVC 定义&#x1f338;再理解Spring MVC &#x1f333;如何学习Spring MVC呢&#xff1f;⭕总结 &#x1f334;什么是Spring Web MVC&#xff1f; Spring Web MVC 是基于…

《捕鱼_ue4-5输出带技能的透明通道素材到AE步骤》

《捕鱼_ue4-5输出带技能的透明通道素材到AE步骤》 2022-05-17 11:06 先看下带透明的特效素材效果1、首先在项目设置里搜索alpha&#xff0c;在后期处理标签设置最后一项allow through tonemapper2、在插件管理器中&#xff0c;搜索movie render &#xff0c;加载movie render q…

图论模板详解

目录 Floyd算法 例题&#xff1a;蓝桥公园 Dijkstra算法 例题&#xff1a;蓝桥王国 SPFA算法 例题&#xff1a;随机数据下的最短路问题 总结 最小生成树MST Prim算法 Kruskal算法 例题&#xff1a;聪明的猴子 Floyd算法 最简单的最短路径算法&#xff0c;使用邻接…

win10上一个详细的Django开发入门例子

1.Django概述 Django是一个开放源代码的Web应用框架&#xff0c;由Python写成。采用了MTV的框架模式&#xff0c;即模型M&#xff0c;视图V和模版T。 Django 框架的核心组件有&#xff1a; 用于创建模型的对象关系映射&#xff1b; 为最终用户设计较好的管理界面&#xff1b…

数学矩阵GCD和lCM(详解)

矩阵乘法 知阵乘法是《线性代数》中的基础内容&#xff0c;但在考察数学的算法题中也会出现。 本节我们学习基础的矩阵乘法规则。 每个矩阵会有一个行数和一个列数&#xff0c;只有当相乘的两个矩阵的左矩阵的列数等于右矩阵的行数 时&#xff0c;才能相乘&#xff0c;否则不允…