Android---底层剖析 Window、Activity、View 三者关系

对于一个 Android 工程师来讲,或多或少都听说过 Window 的概念,并且隐约感受到它在 Activity 和 View 之间应该发挥着某种连接的作用。但如果要说出这三者之间的关系,多数 android 工程师都不知道从何下手。

Activity 的 setContentView

Activity 是 Android 开发人员使用最频繁的 API 之一。最初在接触 Android 开发时,很多人都会认为它是负责将 layout 布局中的控件渲染绘制出来的。原因很简单,因为我们认为

\bullet 想显示一个新的界面时,都是通过 start 一个新的 Activity 方式;

\bullet 想显示的内容或者布局,只需要在 Activity 中添加一行 setContentView

剩下的 Activity 都自动的帮我们搞定。但是从来没有去创建一个 window 来绑定 UI 或者 View 元素。

点开 setContentView 的源码,如下

可以看出,Activity 几乎什么都没有做,将操作直接交给了一个 window 来处理。getWindow 返回的是 Activity 中的全局遍历 mWindow,它是 Window 窗口类型。那么这个 mWindow 是什么时候赋值的呢?

在 startActivity 的过程中,最终代码会调用到 ActivityThread 中的 performLaunchActivity 方法通过反射创建 Activity 对象,并执行其 attach 方法。Window 就是在这个方法中被创建

在 attach 方法中,将 mWindow 初始化为一个 PhoneWindow 类型。实际上,整个 Android 系统中,Window 只有一个实现类 PhoneWindow

接下来调用 setWindowManager 方法,将系统 WindowManager 传给 PhoneWindow

最终,在 PhoneWindow 中持有了一个 WindowManagerImpl 的引用。

PhoneWindow 的 setContentView

Activity 将 setContentView 的操作交给了 PhoneWindow,看一下其实现过程

图中1处调用,如果 mContentParent 为 null,则调用 installDecor() 初始化 DecorView 和 mContentParent;图中2处将我们调用的 setContentView 传入的布局添加到 mContentParent 中

 可以看出,在 PhoneWindow 中,默认有一个 DecorView,实际上是一个 FrameLayout。在 decorView 中,默认自带一个 mContentParent,实际上是一个 ViewGroup。我们自己实现的布局是被添加到 mContentParent 中的。因此,经过 setContentView 之后,PhoneWindow 内部的 View 关系,如下图所示:

目前为止,PhoneWindow 中只是创建了一个 DecorView,并在 DecorView 中添加了我们在 Activity 中传入的 layout 布局。但此时,DecorView 还没有与 Activity 建立任何联系,也没有被绘制到界面显示,那么 DecorView 是何时被绘制到屏幕上的呢?

刚接触 Android 学习生命周期时,经常会看到相关文档 Activity 执行到 onCreate() 时 Activity 的内容还并不可见,只有执行完 onResume() 之后,Activity 中的内容才是屏幕可见状态。造成这种现象的原因是:

onCreate 阶段只是初始化了 Activity 需要显示的内容;onResume 阶段会将 PhoneWindow 中的 DecorView 真正的绘制到屏幕上

在 Activity 的 handleResumeActivity 中,会调用 WindowManager 的 addView 方法,将 DecorView 添加到 WMS 上,如下代码所示:

WindowManager 的 addView 结果:

\bullet DecorView 被渲染绘制到屏幕上显示;

\bullet DecorView 可以接收屏幕触摸事件。

WindowManager 的 addView

PhoneWindow 只是负责处理一些应用窗口通用的逻辑,设置标题栏、导航栏等。但真正完成把一个 View 作为窗口添加到 WMS 的过程是由 WindowManager 来完成的。WindowManger 是接口类型,它的真正实现是 WindowManagerImpl 类。它的 addView 方法如下

WindowManagerImpl 也是一个空壳,它调用了 WindowManagerGlobal 的 addView 方法。

WindowManagerGlobal 是一个单例,每一个进程中只有一个实例对象,如上图红框中所示。在其 addView 方法中,创建了一个最关键ViewRootImpl 对象,然后通过 ViewRootImpl 的 setView 方法,将 View 添加到 WMS 中

ViewRootImpl 的 setView

图中1处的 requestLayout 是刷新布局的操作,调用此方法后 ViewGroup 所关联的 View 也执行 measure、layout、draw 等操作。确保 view 被添加到 Window 上显示到屏幕之前,已经测量和绘制操作。图中2处调用 mWindowSession 的 addToDisplay 方法,将 View 添加到 WMS 中。mWindowSession 是 WindowManagerGlobal 中的单例对象,初始化代码如下

sWindowSession 实际上是 IWindowSession 类型,是一个 Binder 类型。真正的实现类是 System 中的 Session 。图中中,红框中的内容就是用 AIDL 获取 System 进程中的 Session 对象。即 addToDisplay 代码如下

图中的 mService 就是 WMS。至此,Window 已经成功的被传递给了 WMS,剩下的工作就全部转移到系统中的进程 WMS 来完成最终的添加操作。

上面我们提到 addView 成功的另一个标志就是能够接收触屏事件。通过对 setContentView 的流程分析,可以看出:添加 View 的操作实质上是 PhoneWindow 在全盘操作,背后负责人是 WMS。反之,Activity 至始至终没有什么参与感。但是,当触屏事件发生之后 Touch 事件首先被传入到 Activity,然后被下发到布局中的 ViewGroup 或者 View。那么 touch 事件是如何传递到 Activity 上的呢?

ViewRootImpl 中的 setView 方法,除了调用 IWindowSession 执行跨进程添加 View 之外。还有一项重要的操作,就是设置输入事件的处理

上图红框中,设置了一系列的输入通道。一个触屏事件的发生是由屏幕发起,然后经过驱动层一系列的优化计算,通过 socket 跨进程通知 Android 的 Framework 层,实际上就是 WMS。最终,屏幕的触摸事件被发送到上面的输入管道中。

这些输入管道实际上是一个链表结构。当某一个屏幕触摸事件到达其中的 ViewPostImeInputStage时,会经过 onProcess 来处理,如下所示

可以看到,在 onProcess() 方法中,最终调用了一个 mView 的 dispatchPointerEvent() 方法,mView 就是 PhoneWindow 中的 DecorView。而 dispatchPointerEvent 是被 View.java 实现的

最终,调用了 PhoneWindow 中的 callback.dispatchTouchEvent() 方法。那这个 callback 是不是 Activity 呢?

在启动 Activity 阶段,创建 Activity 对象并调用 attach 方法时,有如下一段代码

果然,将 Activity 自身传递给了 PhoneWindow。

Activity 的 dispatchTouchEvent 方法

touch 事件只是在 Activity 中绕了一圈,最终还是回到了 PhoneWindow 中的 DecorView 来处理。剩下的就是从 DecorView 开始,将事件层层传递给子 View 中了。

总结
通过setContentView的流程,分析了Activity、Window和 View 之间的关系。整个过程Activity表面上参与度比较低,大部分View的添加操作都被封装到Window中实现。Activity能够更简单的实现Window和View的操作逻辑。

整个流程需要注意:

1. 一个 Activity 中有一个 Window,也就是 PhoneWindow 对象。在 PhoneWindow 中有一个 DecorView,在 setContentView 中会将 layout 填充到此 DecorView 中。

2. 一个应用程序中只有一个 WindowManagerGlobal对象,因为在 ViewRootImpl 中它是 static 静态类型。

3. 每一个 PhoneWindow 对应一个 ViewRootImplement 对象。

4. WindowManagerGlobal 通过调用 ViewRootImpl 的 setView 方法,完成 window 的添加过程。

5. ViewRootImpl 的 setView 方法中主要完成两件事情:View渲染(requestLayout)以及接收触摸事件。
 

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

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

相关文章

VScode远程连接错误:进程试图写入不存在的管道

使用VScode连接树莓派时,出现远程连接错误:进程试图写入不存在的管道 解决方案: (1)可以进入config所在文件夹,删除文件 (2)无法解决的化尝试下述方法 输入 Remotting-SSH:Settin…

自定义表格的表头根据后端的数据进行筛选是否进行自定义表头添加按钮

自定义表格的表头根据后端的数据进行筛选是否进行自定义表头添加按钮 自定义表格的表头根据后端的数据进行筛选是否进行自定义表头添加按钮 <template><div class"box"><el-table :data"msgMapList" border class"table">&l…

Java入门篇 之 逻辑控制(练习题篇)

博主碎碎念: 练习题是需要大家自己打的请在自己尝试后再看答案哦&#xff1b; 个人认为&#xff0c;只要自己努力在将来的某一天一定会看到回报&#xff0c;在看这篇博客的你&#xff0c;不就是在努力吗&#xff0c;所以啊&#xff0c;不要放弃&#xff0c;路上必定坎坷&#x…

c++ Vector 学习

vevtor 是c 中自带得动态数组&#xff0c;dynamic array array can hold different values/objects of same type 可以装不同得类型或者对象 dynamic size can be changed at runtime 可以运行得时候改变 要使用的话&#xff0c;先引入 #include <vector> std::vector…

Kafka基本原理、生产问题总结及性能优化实践 | 京东云技术团队

Kafka是最初由Linkedin公司开发&#xff0c;是一个分布式、支持分区的&#xff08;partition&#xff09;、多副本的&#xff08;replica&#xff09;&#xff0c;基于zookeeper协调的分布式消息系统&#xff0c;它的最大的特性就是可以实时的处理大量数据以满足各种需求场景&a…

Mac 解决 APP 快捷键冲突

打开 Mac 系统设置键盘->键盘快捷键->App快捷键->添加快捷键&#xff08;加号&#xff09;->标题需要和tab名称完全一致&#xff08;包括中英文、标点符号等&#xff0c;如下图&#xff09;设置快捷键即可 Reference&#xff1a; https://www.cnblogs.com/Questio…

考前须知-11月软考机考

1、11月4-5日考试有几个时间点需注意&#xff1a; 考前40分钟&#xff0c;应试人员应到达准考证上所指定的考点&#xff0c;凭本人准考证和有效证件&#xff08;居民身份证、社会保障卡、港澳台居民身份证明、外籍人员护照、外国人永久居留证明&#xff09;原件进入进入本资格…

分析报告有样板了-奥威BI数据可视化报表模板

述职报告、月度数据分析报告、季度数据分析报告、区域数据分析报告……人在职场&#xff0c;数据分析报告少不了。那么&#xff0c;怎么才能在极短的时间内做出一张既好看又突出重点、分析逻辑在线的数据可视化分析报表&#xff1f;奥威BI软件的建议是采用BI数据可视化报表模板…

成本中心修改后推送其他SAP系统更新成本中心(ALE)

成本中心修改后推送给其他系统 KSH2成本中心组新增成本中心&#xff08;服务器A 400client&#xff09; KAVB 输入成本中心组、控制范围、目标系统 Y 查看&#xff08;服务器B 430client&#xff09;

数字化时代,谈谈数据资产这件事

在全球数字化转型的大趋势下&#xff0c;数据交易市场有望推动数据要素价值的开放共享和流通。据信通院测算&#xff0c;2021年全球47个重要经济体的数字经济增加值规模高达 38.1 万亿美元&#xff0c;我国2021年数字经济规模也已达到7.1万亿美元。数据之于数字经济的价值不言而…

科技云报道:打造生成式AI应用,什么才是关键?

科技云报道原创。 生成式AI作为当前人工智能的前沿领域&#xff0c;全球多家科技企业都在加大生成式AI的研发投入力度。 随着技术、产品及应用等方面不断推出重要成果&#xff0c;如今有更多的行业用户在思考该如何将生成式AI应用落地。 但开发生成式AI应用是一个充满挑战的…

关于嵌入式rtthread系统与单片机芯片

简介 我估计已经有很久没更新了&#xff0c;近一年都在某个国企里工作&#xff0c;我做的就是嵌入式工程师的岗位&#xff0c;最近才刚刚退出来&#xff0c;想来说说自己的工作使用的软件和系统。 本身进公司的时候&#xff0c;其实做的就是写单片机的板子的程序的工作&#x…

破局:如何从产业角度锻造企业不败竞争优势?

引言&#xff1a;究竟是坚守过去的辉煌,还是勇敢拥抱未来的变化?柯达的沉沦与富士胶片的崛起,揭示企业如何通过深刻理解自身的核心竞争力&#xff0c;利用产业战略的转变来实现持续发展。从产品竞争到产业升级,这不仅仅是一场商业竞赛的变革&#xff0c;更是一次企业战略思维的…

第24期 | GPTSecurity周报

GPTSecurity是一个涵盖了前沿学术研究和实践经验分享的社区&#xff0c;集成了生成预训练 Transformer&#xff08;GPT&#xff09;、人工智能生成内容&#xff08;AIGC&#xff09;以及大型语言模型&#xff08;LLM&#xff09;等安全领域应用的知识。在这里&#xff0c;您可以…

Linux0.11内核源码解析-exec.c

主要实现对二进制可执行文件和shell文件的加载和执行&#xff0c;其中主要的函数是do_execve(),它是系统中断调用int 0x80的功能号__NR_execve()调用&#xff0c;是exec()函数的主要实现以下几点功能&#xff1a; 1.执行对参数和环境参数空间页面的初始化操作&#xff0c;初始…

基于51单片机的智能指纹考勤系统设计

**单片机设计介绍&#xff0c;1661【毕设课设】基于51单片机的智能指纹考勤系统设计-原理图-PCB-程序-报告 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于51单片机的智能指纹考勤系统是一种利用51单片机作为主控芯片&#x…

Java生成二维码并打印二维码和文字信息

目录 1、生成二维码&#xff0c;并调用画布进行二维码和文字的描绘。 2、主程序&#xff1a;获取打印机&#xff0c;并打印内容 3、打印效果 参考文献&#xff1a; 前期工作是安装好打印机驱动&#xff0c;可连接打印机。 添加三个依赖jar&#xff1a; 具体见文底的参考文献…

k8s 多网卡方案multus

kubernetes 多网卡方案之 Multus_CNI 部署以及基本使用 一、multus cni 出现的背景 在k8s的环境中启动一个容器&#xff0c;默认情况下只存在两个虚拟网络接口&#xff08;loopback 和 eth0&#xff09;&#xff0c; loopback 的流量始终都会在本容器内或本机循环&#xff0c…

有谁知道怎么下载微信视频号视频吗?

抖音视频下载、某站视频下载都很常见&#xff0c;那你知道怎么下载V信视频号视频吗/今天给大家分享两种简单方便的办法&#xff0c;继续往下看吧&#xff01;一、犀牛视频下载机器人犀牛视频下载器可以直接解析并下载视频号短视频。您只需转发视频到机器人即可下载。此方法也是…

掌握口才与演讲技巧,让你职场中脱颖而出

在职场竞争日趋激烈的今天&#xff0c;口才和演讲能力已经成为一个人成功的重要标志之一。掌握了优秀的口才与演讲技巧&#xff0c;不仅可以帮助你在工作中更好地表达自己和传达信息&#xff0c;同时也可以让你在同事和上级心中留下深刻印象&#xff0c;从而在职场中脱颖而出&a…