当前位置: 首页 > news >正文

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

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。

相关文章:

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

ViewRootImpl 在 Activity、window 和 View 三者关系之间起着承上启下的作用。一方面,ViewRootImpl 中通过 Binder 通信机制,远程调用 WindowSession 将 View 添加到 Window 中;另一方面,ViewRootImpl 在添加 View 之前&#xff0…...

【LeetCode:26. 删除有序数组中的重复项 | 双指针】

🚀 算法题 🚀 🌲 算法刷题专栏 | 面试必备算法 | 面试高频算法 🍀 🌲 越难的东西,越要努力坚持,因为它具有很高的价值,算法就是这样✨ 🌲 作者简介:硕风和炜,…...

如何判断一个对象是不是一个空对象?

在JavaScript中,可以使用以下方法来判断一个对象是否为空对象: 1:使用Object.keys()方法:Object.keys()方法返回一个包含对象自身可枚举属性的数组。通过判断该数组的长度,可以确定对象是否为空对象。 function isEm…...

C/C++ “variable set but not used“的 警告问题解决方案

在编程的过程中,会有一些预留的变量暂时不用,但是编译过程编译器警告 会报错无法编译通过针对这个问题,采用下面的解决方案比较方便。 错误如下形式: 三种解决方法: 1.可以在变量前加上(void)就…...

JAVA安全入门之反射

反射 对于反射这个概念来说,直白的讲就是: 对象可以通过反射获取他的类,类可以通过反射拿到所有⽅法(包括私有),拿到的⽅法可以调⽤而众所周知 JAVA 是一门静态语言,我们通过反射就可以达到动…...

【c++|opencv】一、基础操作---2.图像信息获取

every blog every motto: You can do more than you think. https://blog.csdn.net/weixin_39190382?typeblog 0. 前言 图像信息获取&#xff0c;roi 1. 图像信息获取 // 获取图像信息#include <iostream> #include <opencv2/opencv.hpp>using namespace cv; …...

HarmonyOS开发:探索组件化模式开发

前言 组件化一直是移动端比较流行的开发方式&#xff0c;有着编译运行快&#xff0c;业务逻辑分明&#xff0c;任务划分清晰等优点&#xff0c;针对Android端的组件化&#xff0c;之前有比较系统的总结过相关文章&#xff0c;感兴趣的朋友&#xff0c;可以查看&#xff0c;点击…...

目标URL启用了不安全的HTTP方法

修复中危web项目漏洞&#xff08;目标URL启用了不安全的HTTP方法&#xff09; 漏洞名 &#xff1a;目标URL启用了不安全的HTTP方法 等级&#xff1a; 中危 漏洞位置&#xff1a; PUT DELETE 描述&#xff1a; Web服务器配置为允许使用危险的HTTP方法&#xff0c;如PUT、MOVE、…...

大数据之LibrA数据库系统告警处理(ALM-12001 审计日志转储失败)

告警解释 根据本地历史数据备份策略&#xff0c;集群的审计日志需要转储到第三方服务器上。如果转储服务器满足配置条件&#xff0c;审计日志可以成功转储。审计日志转储失败&#xff0c;系统产生此告警。如果第三方服务器的转储目录磁盘空间不足&#xff0c;或者用户修改了转…...

大模型该被知道的技术实现-面向垂直领域

一个高度清晰的思维导图截图奉上&#xff08;下载&#xff1a;需3积分&#xff09; 内容截图...

赛灵思产品系列

FPGA概述&#xff1a; FPGA的性能主要划分为以下几个方面&#xff1a; 1. 逻辑单元数量&#xff1a;逻辑单元数量越多&#xff0c;FPGA的处理能力越强。 2. 存储单元数量&#xff1a;存储单元数量越多&#xff0c;FPGA的存储能力越强。 3. 时钟频率&#xff1a;时钟频率越高&a…...

[Linux C] signal 的使用

前言&#xff1a; signal 是一种通信机制&#xff0c;可以跨进程发送&#xff0c;可以同进程跨线程发送&#xff0c;可以不同进程向指定线程发送。 信号的创建有两套api&#xff0c;一个是signal&#xff0c;一个是sigaction&#xff0c;signal缺陷很多&#xff0c;比如没有提…...

AI时代产品经理升级之道:ChatGPT让产品经理插上翅膀

&#x1f482; 个人网站:【工具大全】【游戏大全】【神级源码资源网】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 寻找学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】 AI时代的产品经理面临着…...

计算机网络重点概念整理-第七章 网络安全【期末复习|考研复习】

计算机网络复习系列文章传送门&#xff1a; 第一章 计算机网络概述 第二章 物理层 第三章 数据链路层 第四章 网络层 第五章 传输层 第六章 应用层 第七章 网络安全 计算机网络整理-简称&缩写 文章目录 前言七、网络安全7.1网络安全7.2 网络威胁7.3 加密7.3.1 对称加密7.3.…...

【LeetCode力扣】42. 接雨水

目录 1、题目介绍 2、解题思路 2.1、暴力破解法 2.2、双指针法 1、题目介绍 原题链接&#xff1a; 42. 接雨水 - 力扣&#xff08;LeetCode&#xff09; 示例 1&#xff1a; 输入&#xff1a;height [0,1,0,2,1,0,1,3,2,1,2,1]输出&#xff1a;6解释&#xff1a;上面是由…...

03、SpringCloud -- 动态倒计时 及 当前用户的获取(用户未登录提示其登录)

目录 动态倒计时需求思路代码效果优化获取当前登录用户思路代码前端后端controllerservice接口impl实现效果问题修改动态倒计时 需求 根据不同时间展示不同状态,动态显示时间,如原型图: 思...

Mac用户心目中的四款首选原型工具

Wireframe、Mockup和prototype在原型工具中有什么区别&#xff1f; 无论你是刚进入这个行业的UX/UI设计师&#xff0c;还是已经进入这个行业多年的老手&#xff0c;你都必须在制作原型的过程中接触或听到三个非常重要的原型术语&#xff1a;“wireframe(线框图)Mockup”或“pr…...

国内内卷太严重,还不考虑一下在海外接单?那这几个平台你知道吗?

作为一个程序员&#xff0c;在平台上接单赚点外快是再正常不过的事情了&#xff0c;但是现今国内各个平台都内卷比较严重&#xff0c;你是否考虑过去“外面的世界”看看&#xff1f; 如果想过&#xff0c;那么这几个外国的接单平台你都知道吗&#xff1f; 接下来就和我一起来看…...

在excel中如何打出上标、下标

例如&#xff0c;想把A2的2变为下标。 在单元中输入内容&#xff1a; 选中2&#xff1a; 右键单击&#xff0c;然后点击“设置单元格格式”&#xff1a; 在特殊效果的下面勾选“下标”&#xff0c;然后点击下面的“确定”按钮&#xff1a; 就将2变为下标了&#xff1a;…...

LoongArch 五级流水线实现

在单周期的基础上进行拆分成取指、译码、执行、访存、写回五级流水线。 mycpu_top.v include "mycpu.h"module id_stage(input clk ,input reset ,//allowininput …...

Docker 运行 Kafka 带 SASL 认证教程

Docker 运行 Kafka 带 SASL 认证教程 Docker 运行 Kafka 带 SASL 认证教程一、说明二、环境准备三、编写 Docker Compose 和 jaas文件docker-compose.yml代码说明&#xff1a;server_jaas.conf 四、启动服务五、验证服务六、连接kafka服务七、总结 Docker 运行 Kafka 带 SASL 认…...

抖音增长新引擎:品融电商,一站式全案代运营领跑者

抖音增长新引擎&#xff1a;品融电商&#xff0c;一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中&#xff0c;品牌如何破浪前行&#xff1f;自建团队成本高、效果难控&#xff1b;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...

视频字幕质量评估的大规模细粒度基准

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 摘要 视频字幕在文本到视频生成任务中起着至关重要的作用&#xff0c;因为它们的质量直接影响所生成视频的语义连贯性和视觉保真度。尽管大型视觉-语言模型&#xff08;VLMs&#xff09;在字幕生成方面…...

土地利用/土地覆盖遥感解译与基于CLUE模型未来变化情景预测;从基础到高级,涵盖ArcGIS数据处理、ENVI遥感解译与CLUE模型情景模拟等

&#x1f50d; 土地利用/土地覆盖数据是生态、环境和气象等诸多领域模型的关键输入参数。通过遥感影像解译技术&#xff0c;可以精准获取历史或当前任何一个区域的土地利用/土地覆盖情况。这些数据不仅能够用于评估区域生态环境的变化趋势&#xff0c;还能有效评价重大生态工程…...

EtherNet/IP转DeviceNet协议网关详解

一&#xff0c;设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络&#xff0c;本网关连接到EtherNet/IP总线中做为从站使用&#xff0c;连接到DeviceNet总线中做为从站使用。 在自动…...

【HarmonyOS 5 开发速记】如何获取用户信息(头像/昵称/手机号)

1.获取 authorizationCode&#xff1a; 2.利用 authorizationCode 获取 accessToken&#xff1a;文档中心 3.获取手机&#xff1a;文档中心 4.获取昵称头像&#xff1a;文档中心 首先创建 request 若要获取手机号&#xff0c;scope必填 phone&#xff0c;permissions 必填 …...

使用 SymPy 进行向量和矩阵的高级操作

在科学计算和工程领域&#xff0c;向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能&#xff0c;能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作&#xff0c;并通过具体…...

Java 二维码

Java 二维码 **技术&#xff1a;**谷歌 ZXing 实现 首先添加依赖 <!-- 二维码依赖 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.5.1</version></dependency><de…...

VM虚拟机网络配置(ubuntu24桥接模式):配置静态IP

编辑-虚拟网络编辑器-更改设置 选择桥接模式&#xff0c;然后找到相应的网卡&#xff08;可以查看自己本机的网络连接&#xff09; windows连接的网络点击查看属性 编辑虚拟机设置更改网络配置&#xff0c;选择刚才配置的桥接模式 静态ip设置&#xff1a; 我用的ubuntu24桌…...

免费PDF转图片工具

免费PDF转图片工具 一款简单易用的PDF转图片工具&#xff0c;可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件&#xff0c;也不需要在线上传文件&#xff0c;保护您的隐私。 工具截图 主要特点 &#x1f680; 快速转换&#xff1a;本地转换&#xff0c;无需等待上…...