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.hqwc.cn/news/155109.html

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

相关文章

网络协议--TCP的保活定时器

23.1 引言 许多TCP/IP的初学者会很惊奇地发现可以没有任何数据流通过一个空闲的TCP连接。也就是说,如果TCP连接的双方都没有向对方发送数据,则在两个TCP模块之间不交换任何信息。例如,没有可以在其他网络协议中发现的轮询。这意味着我们可以…

墨者学院 身份认证失效漏洞实战

一、题目信息 二、漏洞利用 1.通过抓包,抓取test登录信息 发现card_id号以及一些回显的账号密码信息 搜索了一下这个id,发现是测试的头像 2.修改id号 分析请求包的逻辑,发现是请求了头像资源后再去请求头像id的详情包,所以根据…

Android NDK开发详解之Android.mk探秘

Android NDK开发详解之Android.mk探秘 概览基础知识变量和宏NDK 定义的 include 变量CLEAR_VARSBUILD_EXECUTABLEBUILD_SHARED_LIBRARYBUILD_STATIC_LIBRARYPREBUILT_SHARED_LIBRARYPREBUILT_STATIC_LIBRARY 目标信息变量TARGET_ARCHTARGET_PLATFORMTARGET_ABI 模块描述变量LOC…

leetcode第369周赛

2917. 找出数组中的 K-or 值 给你一个下标从 0 开始的整数数组 nums 和一个整数 k 。 nums 中的 K-or 是一个满足以下条件的非负整数: 只有在 nums 中,至少存在 k 个元素的第 i 位值为 1 ,那么 K-or 中的第 i 位的值才是 1 。 返回 nums …

分享大数据分析师前景怎么样? 从事行业有哪些?

数据分析师发展前景和待遇怎么样?有前途吗?好找工作吗?根据某招聘网数据显示,当前市场表现为: 2023年较2022年同期对比增长160%,2022年较2021年下降了46%。 工资待遇:2023年较2022年下降了2…

Java 入门指南:使用 Docker 创建容器化 Spring Boot 应用程序

文章目录 步骤 1: 准备工作步骤 2: 克隆 Spring Boot 应用程序步骤 3: 创建 Dockerfile步骤 4: 构建 Docker 映像步骤 5: 运行容器步骤 6: 链接到本地数据库步骤 7: 使用 Docker Compose 运行多个容器步骤 8: 设置 CI/CD 管道结论 🎈个人主页:程序员 小侯…

Spring更加简单的读取和存储对象

前言:在上篇文章中,小编写了一个Spring的创建和使用的相关博客:Spring的创建和使用-CSDN博客,但是,操作/思路比较麻烦,那么本文主要带领大家走进:Spring更加简单的读取和存储对象! 本…

水果FL Studio21.2体验版下载安装教程(增加云服务功能)

FL Cloud 音效库包含开放版权的Loop和采样,以及来自 FL Studio 著名用户的艺术家独家内容。更新后,现在还可以使用人工智能辅助母带处理和数字发行功能来制作音轨。FL Studio 由最初的 "Fruity Loops" DAW 发展而来,25 年来&#x…

让GPT替我写vue3代码,看的我血压升高

事情是这样子的,最近在写Vue3相关的代码,就想着能不能让GPT辅助我写代码,于是,我就先写了一个中文的prompt Prompt1: 使用vue3写一个用户登录的页面 分割线内部是GPT的回答: 使用 Vue 3 来创建一个用户登录页面需要涉…

反射率检测仪如何检测后视镜

后视镜反射率检测是评估后视镜质量的重要步骤,可以反映后视镜的反射效果是否满足设计要求。一般来说,后视镜的反射率越高,驾驶员观察车后的道路状况就越清晰,从而能够更好地判断与后方车辆的距离和速度差。 后视镜反射率检测的原理…

Agent 应用于提示工程

如果Agent模仿了人类在现实世界中的操作方式,那么,能否应用于提示工程即Prompt Engingeering 呢? 从LLM到Prompt Engineering 大型语言模型(LLM)是一种基于Transformer的模型,已经在一个巨大的语料库或文本数据集上进行了训练&…

单元测试到底测什么,怎么测?我来告诉你

前言: 以国内互联网的开发节奏,在前端业务项目中全面覆盖单元测试有时显得不太可行,主要是因为以下这些绊脚石: UI 交互复杂,路径难以覆盖全面 工期紧,开发对实践 TDD,BDD 所带来的长远效益没有…