Android---如何同view进行渲染

news2024/9/27 5:35:57

ViewRootImpl 在 Activity、window 和 View 三者关系之间起着承上启下的作用。一方面,ViewRootImpl 中通过 Binder 通信机制,远程调用 WindowSession 将 View 添加到 Window 中;另一方面,ViewRootImpl 在添加 View 之前,需要调用  requestLayout 方法,执行完整的 View 树的渲染操作。

ViewRootImpl 执行 View 的渲染

屏幕绘制

ViewRootImpl requestLayout 流程。requestLayout 第一个次被调用是在 setView() 方法中。

从名字也能看出,这个方法的主要目的就是请求布局操作。其中包括 View 的测量、布局和绘制等。具体代码如下

注释1处,检测是否为合法线程。一般情况下,就是检测是否为主线程;注释2处,将请求布局标识符设置为 true。这个参数决定了后续是否执行 measure 和 layout 操作。最后执行 scheduleTraversals() 方法,如下

注释1处向主线程消息队列中插入 SyncBarrierMessage。该方法发送了一个没有 target 的 Message 到 queue 中。在 next 方法获取消息时,如果发现没有 target 的 message,则在一定的时间内跳过同步消息,优先执行异步消息。这里通过调用次方法,保证 UI 绘制操作优先执行。注释2处,调用 postCallback 方法。实际上也是发送一个 Message 到主线程消息队列。

postCallback 的执行流程如下

可以看出,最终通过 Handler 发送 MessageQueue 中的 message ,被设置为异步类型的消息。

 mTraversalRunable 是一个实现 Runable 接口的 TraversalRunable 对象,其 run 方法如下

可以看出,在 run() 方法中,调用了 doTraversal() 方法,并最终调用了 performTraversals() 方法。 这个方法就是真正的开始 View 的绘制流程:measure-->layout-->draw。核心代码如下

这是个比较重的方法,只负责做3件事情,即在自定义 view 中常用到的 3 个主要过程:measue、layout、draw。 下面以测量 performMeasure 实现举例。

ViewRootImpl 的 measureHierarchy

View 的测量是一层递归调用,递归执行子 View 的测量工作之后,最后决定父视图的宽和高。但是,这个递归的起源是在哪里呢?

答案就是 DecorView。因为在 measureHierarchy 方法中,最终是调用 performMeasure 方法来进行测量工作的。performMeasure 方法的实现,如下代码所示

在这个方法中,通过 getRootMeasureSpec() 方法获取了根 View 的 MeasureSpec() 方法。实际上 MeasureSpec 的宽高此处获取的值是 Window 的宽高。

ViewRootImpl 的 performMeasure

这个方法很简单,只是执行了 mView 的 measure 方法。这个 mView 就是 DecorView。其 DecorView 的 measure 方法中会调用 onMeasure 方法,而 DecorView 是继承 FramLayout 的。因此,最终会执行 FramLayout 的 onMeasure 方法,并递归调用子 View 的 onMeasure 方法。

performLayout()  也是类似的过程,这里不再分析。

ViewRootImpl 的 performDraw

从图中可以看出,在 performDraw 方法中调用的 ViewRootImpl 的 draw 方法。在 draw 方法中进行 UI 绘制操作,Android 系统提供了两种绘制方式。图中1处标识 App 开启了硬件加速功能,所以会启动硬件加速绘制;图中2处表示使用软件绘制,

软件和硬件绘制

ViewRootImpl 中有一个非常重要的对象 Suface,之所以说 ViewRootImpl 的一个核心功能是负责 UI 渲染。原因就在于,在 ViewRootImpl 中会将在 draw 方法中绘制的 UI 元素,绑定到这个 Surface 上。如果说 canvas 是画板,那么 surface 就是画板上的画纸。Surface 中的内容最终会被传递给底层的 SurfaceFlinger,最终将 Surface 中的内容进行合成并显示在屏幕上。

软件绘制 drawSoftware

图中1处,就是调用 DecorView 的 draw 方法。将 UI 元素绘制到画布 canvas 对象中;图中2处,请求将 canvas 中的内容显示到屏幕上。实际上就是将 canvas 中的内容提交给 SurfaceFlinger 进行合成处理。默认情况下,软件绘制没有采用 GPU 渲染的方式,drawSoftware 工作完全由 CPU 来完成。

DecorView 并没有复写 draw 方法,因此,实际上是调用顶层 View 的 draw 方法,如下代码

图中1处绘制 view 的背景;图中2处绘制 view 的自身内容;图中3处表示对 draw 事件进行分发,在 view 中是空实现,实际调用的是 viewGroup 中的实现,并递归调用子 View 的 draw 事件。

启用硬件加速

可以在 ViewRootImpl 的 draw 方法中,通过如下方法判断是否启用硬件加速

我们可以在 AndroidManifest.xml 清单文件中,指定 Application 或者某一个 Activity 支持硬件加速

此外,还可以进行粒度更小的硬件加速设置,比如设置某个 View 支持硬件加速

之所以会有这么多级的支持区分,主要是因为并不是所有的 2D 绘制操作都支持硬件加速。

硬件加速优势

硬件加速能够提高 UI 渲染的性能。在 ViewRootImpl 的 draw 方法中,mThreadedRenderer 是 ThreadedRenderer 类型

其 darw 方法具体如下

图中1处就是硬件加速的特殊之处,通过 updateRootDisplayList() 方法,将 View 视图抽象成一个 RendererLoad 对象,并构建 View 的 drawOp 树;图中2处通知 renderThread 进行绘制操作。

renderThread 是一个单例线程,每个进程最多只有一个硬件渲染线程,这样就不会存在多线程并发访问冲突问题。updateRootDisplayList() 具体如下

Android 硬件加速过程中,View 视图被抽象成 RenderNode 节点。View 中的绘制操作都会被抽象成一个个 DrawOp。比如,View 中 drawLine,构建过程中就会被抽象成一个 DrawLineOp。drawBitmap 操作会被抽象成 DrawBitmapOp,每个子 View 的绘制被抽象成 DrawRenderNodeOp,每个 DrawOp 有对应的 OpenGL 绘制命令。

上图中1处遍历 view 递归构建 DrawOp;2处根据 canvas 将所有的 operation 进行缓存操作。所有的 DrawOp 对应 OpenGL 命令构建完成后,就需要使用 RenderProxy 向 RenderThread 发送消息。请求 OpenGL 线程进行渲染。整个渲染过程是通过 GPU 并在不同线程绘制渲染图像。因此整个流程会更加流程。

View 的两种刷新方式

Invalidate 轻量级刷新

通过 invalidate 来刷新 View,与 requestLayout 的区别在于它不一定会触发 View 的 measure 和 layout 的操作,多数情况下只会执行 draw 操作。在 View 的 measure 方法中,有如下几行代码

可以看出,如果要触发 onMeasure 方法,需要对 View 设置 PFLAG_FORCE_LAYOUT 的标志位。而这个标志位在 requestLayout 方法中被设置。 invalidate 并没有设置此标志位。

再看一下 onLayout 方法

可以看出当 view 的位置发生改变或者添加 PFLAG_FORCE_LAYOUT 标志位,onLayout 才会被执行。

当调用 invalidate 方法时,如果 View 的位置并没有发生改变,则 View 不会触发重新布局的操作。

postInvalidate

postInvalidate 在面试中经常被问道,实际开发中使用频率也是较高的。invalidate 与 postInvalidate 两者之间的区别是 invalidate 是在 UI 线程调用,postInvalidate 是在非 UI 线程调用。postInvalidate 实现如下

最终还是在 ViewRootImpl 中进行操作。

ViewRootImpl 的 dispatchInvalidateDelayed 在 非 UI 线程中,通过 Handler 发送了一个延时 Message。

因为 Handler 是在主线程中创建的,所以 handleMessage 最终会在主线程中被执行。方法如下

上图中的 msg.obj 就是发生 postInvalidate 的 view 对象。可以看出,最终还是回到了 UI 线程,执行了 View 的 invalidate 方法。

个人理解,做过 android 开发的都知道,只有 UI 线程才可以刷新 view 控件。但是,事实却并非如此。在 ViewRootImpl 中对 view 进行刷新时,会检测当前线程的合法性,下图中 mThread 是被赋值为当前线程。而 ViewRootImpl 是在 UI 线程中被创建的,因此只有 UI 线程可以进行 view 刷新。但是,如果我们能在非 UI 线程中创建 ViewRootImpl,并通过这个 ViewRootImpl 进行 view 的添加和绘制操作,那么后续理论上也是可以在非 UI 线程中刷新 view 控件的。只是维护成本较高,很少有人去做这件事情。

总结

\bullet 主要介绍了 ViewRootImpl 是如何执行 View 的渲染操作的。其中核心方法在 performTraversals 方法中会按顺序执行 measure->layout->draw 操作。

\bullet 介绍了软件绘制和硬件绘制的区别

\bullet 介绍了 View 刷新的两种方式 Invalidate 和 postInvalidate。

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

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

相关文章

centos7安装配置及Linux常用命令

目录 前言 一、centos7操作系统 1.centos7简介 2.centos7的安装及配置 3.配置centos的镜像 4.虚拟机开机初始设置 5.查看、设置IP地址 二、MobaXterm工具的使用 1.MobaXterm简介 2.MobaXterm安装 3.MobaXterm的使用 4.切换国内源 三、Linux常用命令 1.查看网络…

合肥中科深谷嵌入式项目实战——人工智能与机械臂(四)

订阅:新手可以订阅我的其他专栏。免费阶段订阅量1000 python项目实战 Python编程基础教程系列(零基础小白搬砖逆袭) 作者:爱吃饼干的小白鼠。Python领域优质创作者,2022年度博客新星top100入围,荣获多家平台专家称号。…

后门程序分析1

临时补充一个内容,这是一个后门程序,通过IDA分析,之后把里面收集的信息点全部整理出来(包括:反虚拟机,系统信息等等)pass:guet 用IDA打开先看看主函数的样子 查阅一些这些API InternetOpenA&…

C++入门06—结构体

1. 结构体基本概念 结构体属于用户自定义的数据类型,允许用户存储不同的数据类型 2. 结构体定义和使用 语法:struct 结构体名 { 结构体成员列表 }; 通过结构体创建变量的方式有三种(相当于python中的实例化对象): struct 结构体…

图像无损放大画质修复工具 Topaz Photo AI「Mac」

Topaz Photo AI是一款适用于Mac的图像处理软件,它使用人工智能技术对照片进行编辑和优化。该软件提供了多种强大的功能,帮助用户轻松地改善图像质量,并实现自定义的效果。 Topaz Photo AI支持多种文件格式,包括JPEG、TIFF、PNG、R…

BLIP2中Q-former详解

简介 Querying Transformer,在冻结的视觉模型和大语言模型间进行视觉-语言对齐。 为了使Q-Former的学习达到两个目标: 学习到和文本最相关的视觉表示。 这种表示能够为大语言模型所解释。 需要在Q-Former结构设计和训练策略上下功夫。具体来说&…

氧化铜纳米线 纳米氧化铜 Cupric oxide 瑞禧

氧化铜纳米线 中文名称:纳米氧化铜 英文名称:Cupric oxide CAS:1317-38-0 保存条件:密封保存于干燥、阴凉的环境中 产品特点 1.氧化铜可溶于稀酸、NH4Cl、(NH4)2CO3、氰化钾溶液,不溶于水,在醇、氨溶液中溶解缓慢。高温遇氢或一氧化碳,可…

如何实现树莓派Raspberry Pi无公网IP环境下远程访问?

文章目录 前言如何通过 SSH 连接到树莓派步骤1. 在 Raspberry Pi 上启用 SSH步骤2. 查找树莓派的 IP 地址步骤3. SSH 到你的树莓派步骤 4. 在任何地点访问家中的树莓派4.1 安装 Cpolar内网穿透4.2 cpolar进行token认证4.3 配置cpolar服务开机自启动4.4 查看映射到公网的隧道地址…

想喝一点汤 - 如何看懂新闻联播

看懂新闻联播 埋头苦干、辛苦劳作是挣不到钱的,要去离钱近的地方。在中国需要靠近政府。 告知性新闻 告知性的新闻往往隐藏着机会. 国际新闻各国领导人来访 潜台词:双边可能达成了某种协议,需要重要领导见证签字。 普通人机会 外贸机会…

Pr2022安装教程(超级好用)附网盘资源(正版教程)

文末资源自提 一.简介 Premiere是由Adobe Systems开发的专业视频编辑软件,被广泛用于电影制作、电视节目制作、广告制作和个人视频编辑等领域。它提供了丰富的工具和功能,使用户能够进行视频剪辑、调色、特效添加和输出等操作。 下面是对Premiere的详…

【Linux】多路IO复用技术①——select详解如何使用select模型在本地主机实现简易的一对多服务器(附图解与代码实现)

这一篇的篇幅可能有点长,但真心希望大家能够静下心来看完,相信一定会有不小的收获。那么话不多说,我们这就开始啦!!! 目录 一对一服务器中的BUG 如何实现简易的一对多服务器 实现简易一对多服务器的大体…

web前端常见开发工具汇总 你用过几个?

搬运旗下公众号的内容~ 目录 1.记事本 2.Visual studio code 3.Hbuilder 4.Eclipse 5.Webstorm 6.Notepad 随着信息时代的不断进步,互联网在人类社会中所占的地位愈发举足轻重。大大小小的网站,构成了如今光怪陆离的网络社会。我们知道&#xff0c…

上海中优城市万豪酒店推出全新国际IP童趣主题房,独特住宿体验中国首秀

2023年10月30日,中国上海 – 近日,上海中优城市万豪酒店正式推出由全球品牌娱乐公司孩之宝官方授权打造的小马宝莉和变形金刚主题客房,以创意客房、新奇体验和丰富礼遇,为童游家庭或年轻的动漫迷们开启沉浸式入住之旅,…

直击电商商城内核!一站式解决方案

作为一家深耕电商运营多年的软件开发公司,我们拥有先进的轻量级电商中台系统,且100%开源,包含B2C、B2B2C、S2B2C、O2O和社区团购等多种商业模式,无论在技术、业务架构、功能、设计还是售后支持上,我们都秉承着追求极致…

函数栈帧的创建和销毁(以C语言代码为例,汇编代码的角度分析)

函数栈帧的创建和销毁[以C语言代码为例,汇编代码的角度分析] 一.前言1.几个问题2.几个说明 二.相关寄存器和汇编命令的简要说明三.从汇编代码调试的角度逐步分析函数栈帧的创建于销毁1.函数栈区的知识:2.逐步调试分析1.保存__tmainCRTStartup这个函数栈帧的栈底地址2.正式进入m…

【Linux】centOS7安装配置及Linux的常用命令---超详细

一,centOS 1.1 centOS的概念 CentOS(Community Enterprise Operating System)是一个由社区支持的企业级操作系统,它是以Red Hat Enterprise Linux(RHEL)源代码为基础构建的。CentOS提供了一个稳定、可靠且…

解决计算机msvcp120.dll文件丢失的5种方法,亲测有效

在计算机使用过程中,我们经常会遇到一些错误提示,其中之一就是“msvcp120.dll丢失”。这个错误提示可能会给我们带来很大的困扰,影响我们的正常使用。本文将详细介绍msvcp120.dll丢失的原因、解决方法以及预防措施,帮助大家更好地…

python读取shadow文件脚本

python读取shadow文件脚本 该脚本源代码为kali中执行的源代码 from dataclasses import fieldswith open(/etc/shadow,r)as file:for line in file:listline.split(:)if list[1]!"*" and list[1]!"!" and list[1]!"!*":paslist[1].split($)sal…

基于FMCW雷达的人体复杂动作识别

基于FMCW雷达的人体复杂动作识别

【Python算法】算法练习(一)

❤️博客主页: iknow181 🔥系列专栏: Python、JavaSE、JavaWeb、CCNP 🎉欢迎大家点赞👍收藏⭐评论✍ 目录 1、输出n以内的质数 2、求n以内最大的m个质数的和,并打印这些质数以及它们的和 方法一 方法二…