Android 渲染机制——Display List

2024-06-12 10:18

本文主要是介绍Android 渲染机制——Display List,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Display List


Android 需要把 XML 布局文件转换成 GPU 能够识别并绘制的对象。这个操作是在 DisplayList 的帮助下完成的。DisplayList 持有所有将要交给 GPU 绘制到屏幕上的数据信息。

Display List 是什么?

Display List 是一个缓存绘制命令的 Buffer,Display List 的本质是一个缓冲区,它里面记录了即将要执行的绘制命令序列。

Display List 是视图的基本绘制元素,包含元素原始属性(位置、尺寸、角度、透明度等),对应 Canvas 的 drawXxx()方法。

视图信息传递流程:Canvas(Java API) —> OpenGL(C/C++ Lib) —> 驱动程序 —> GPU。

在硬件加速渲染环境中,Android 应用程序窗口的 UI 渲染是分两步进行的:
  1. 第一步是构建 Display List,发生在应用程序进程的 Main Thread 中。
  2. 第二步是渲染 Display List,发生在应用程序进程的 Render Thread 中。增加 Render Thread 线程,也是为了避免 UI 线程任务过重,用于提高渲染性能。

这些绘制命令最终会转化为 Open GL 命令由 GPU 执行。这意味着我们在调用 Canvas API 绘制 UI 时,实际上只是将 Canvas API 调用及其参数记录在 Display List 中,然后等到下一个 VSYNC 信号到来时,记录在 Display List 里面的绘制命令才会转化为 Open GL 命令由 GPU 执行。

Display List 的构建

Display List 是以视图为单位进行构建的,因此每一个视图都对应有一个 Display List。

Android 应用程序窗口视图是树形结构的,因此它们的 Display List 是从根视图开始构建的,并且子视图的 Display List 包含在父视图的 Display List 中。这意味着根视图的 Display List 包含了 Android 应用程序窗口 UI 所有的绘制命令,因此最后我们只需要对根视图的 Display List 进行渲染即可得到 Android 应用程序窗口的 UI。

Android 应用程序窗口的根视图是虚拟的,抽象为一个 Root Render Node。此外,一个视图如果设置有 Background,那么这个 Background 也会抽象为一个 Background Render Node。Root Render Node、Background Render Node 和其它真实的子视图,除了 TextureView 和软件渲染的子视图之外,都具有 Display List,并且是通过一个称为 Display List Renderer 的对象进行构建的。最后,Root Render Node 的 Display List 被一个称为 Open GL Renderer 的对象进行渲染,就得到 Android 应用程序窗口的UI了。

TextureView 不具有 Display List,它们是通过一个称为 Layer Renderer 的对象以 Open GL 纹理的形式来绘制的,不过这个纹理也不是直接就进行渲染的,而是先记录在父视图的 Display List 中以后再进行渲染的。同样,软件渲染的子视图也不具有 Display List,它们先绘制在一个 Bitmap 上,然后这个 Bitmap 再记录在父视图的 Display List 中以后再进行渲染的。

Display List 的使用和优势

在某个 View 第一次需要被渲染时,Display List 会因此被创建,当这个 View 要显示到屏幕上时,我们会执行 GPU 的绘制指令来进行渲染。

如果 View 的属性发生了改变(例如移动位置),我们就仅仅需要 Execute Display List 就够了。

在这里插入图片描述

但是,如果我们修改了 View 中的某些可见组件的内容,那么之前的 DisplayList 就无法继续使用了,我们需要重新创建一个 DisplayList 并重新执行渲染指令更新到屏幕上。

在这里插入图片描述

注意:任何时候 View 中的绘制内容发生变化时,都会需要重新创建 DisplayList,渲染 DisplayList,更新到屏幕上等一系列操作。这个流程的表现性能取决于你的 View 的复杂程度,View 的状态变化以及渲染管道的执行性能。举个例子,假设某个 Button 的大小需要增大到目前的两倍,在增大 Button 大小之前,需要通过父 View 重新计算并摆放其他子 View 的位置。修改 View 的大小会触发整个视图树的重新计算大小的操作。如果是修改 View 的位置则会触发视图树重新计算其他 View 的位置。如果布局很复杂,这就会很容易导致严重的性能问题。

完整过程如下:

在这里插入图片描述

这里总结下,使用 DisplayList 的优势:

  1. 第一个好处是在绘制窗口的下一帧时,若某一个视图的 UI 没有发生变化,那么就不必执行与它相关的 Canvas API,即不用执行它的成员函数 onDraw,而是直接复用上次构建的 Display List 即可。
  2. 第二个好处是在绘制窗口的下一帧时,若某一个视图的 UI 发生了变化,但是只是一些简单属性发生了变化,例如位置和透明度等简单属性,那么也不必重建它的 Display List,而是直接修改上次构建的 Display List 的相关属性即可,这样也可以省去执行它的成员函数 onDraw。

硬件加速条件下,CPU 用于控制复杂绘制逻辑、构建或更新 DisplayList;GPU 用于完成图形计算、渲染 DisplayList。在硬件加速条件下,刷新界面尤其是播放动画时,CPU 只重建或更新必要的 DisplayList,进一步提高渲染效率。实现同样效果,应尽量使用更简单的 DisplayList,从而达到更好的性能(例如:Shape 代替 Bitmap 等)。

总结


  1. Android 需要把 XML 布局文件转换成 GPU 能够识别并绘制的对象。这个操作是在 DisplayList 的帮助下完成的。DisplayList 持有所有将要交给 GPU 绘制到屏幕上的数据信息。

  2. Display List 是一个缓存绘制命令的 Buffer,Display List 的本质是一个缓冲区,它里面记录了即将要执行的绘制命令序列。

  3. Display List 是视图的基本绘制元素,包含元素原始属性(位置、尺寸、角度、透明度等),对应 Canvas 的 drawXxx()方法。

  4. 渲染 Display List,发生在应用程序进程的 Render Thread 中。增加 Render Thread 线程,也是为了避免 UI 线程任务过重,用于提高渲染性能。

  5. Display List 是以视图为单位进行构建的,因此每一个视图都对应有一个 Display List。

  6. 在某个 View 第一次需要被渲染时,Display List 会因此被创建,当这个 View 要显示到屏幕上时,我们会执行 GPU 的绘制指令来进行渲染。

  7. 在绘制窗口的下一帧时,若某一个视图的 UI 没有发生变化,那么就不必执行与它相关的 Canvas API,即不用执行它的成员函数 onDraw,而是直接复用上次构建的 Display List 即可。

  8. 在绘制窗口的下一帧时,若某一个视图的 UI 发生了变化,但是只是一些简单属性发生了变化,例如位置和透明度等简单属性,那么也不必重建它的 Display List,而是直接修改上次构建的 Display List 的相关属性即可,这样也可以省去执行它的成员函数 onDraw。

  9. 硬件加速条件下,CPU 用于控制复杂绘制逻辑、构建或更新 DisplayList;GPU 用于完成图形计算、渲染 DisplayList。


**PS:更多精彩内容,请查看 --> 《Android 性能优化》
**PS:更多精彩内容,请查看 --> 《Android 性能优化》
**PS:更多精彩内容,请查看 --> 《Android 性能优化》

这篇关于Android 渲染机制——Display List的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1053934

相关文章

java中反射(Reflection)机制举例详解

《java中反射(Reflection)机制举例详解》Java中的反射机制是指Java程序在运行期间可以获取到一个对象的全部信息,:本文主要介绍java中反射(Reflection)机制的相关资料... 目录一、什么是反射?二、反射的用途三、获取Class对象四、Class类型的对象使用场景1五、Class

Android Kotlin 高阶函数详解及其在协程中的应用小结

《AndroidKotlin高阶函数详解及其在协程中的应用小结》高阶函数是Kotlin中的一个重要特性,它能够将函数作为一等公民(First-ClassCitizen),使得代码更加简洁、灵活和可... 目录1. 引言2. 什么是高阶函数?3. 高阶函数的基础用法3.1 传递函数作为参数3.2 Lambda

Android自定义Scrollbar的两种实现方式

《Android自定义Scrollbar的两种实现方式》本文介绍两种实现自定义滚动条的方法,分别通过ItemDecoration方案和独立View方案实现滚动条定制化,文章通过代码示例讲解的非常详细,... 目录方案一:ItemDecoration实现(推荐用于RecyclerView)实现原理完整代码实现

java streamfilter list 过滤的实现

《javastreamfilterlist过滤的实现》JavaStreamAPI中的filter方法是过滤List集合中元素的一个强大工具,可以轻松地根据自定义条件筛选出符合要求的元素,本文就来... 目录1. 创建一个示例List2. 使用Stream的filter方法进行过滤3. 自定义过滤条件1. 定

Android App安装列表获取方法(实践方案)

《AndroidApp安装列表获取方法(实践方案)》文章介绍了Android11及以上版本获取应用列表的方案调整,包括权限配置、白名单配置和action配置三种方式,并提供了相应的Java和Kotl... 目录前言实现方案         方案概述一、 androidManifest 三种配置方式

Android WebView无法加载H5页面的常见问题和解决方法

《AndroidWebView无法加载H5页面的常见问题和解决方法》AndroidWebView是一种视图组件,使得Android应用能够显示网页内容,它基于Chromium,具备现代浏览器的许多功... 目录1. WebView 简介2. 常见问题3. 网络权限设置4. 启用 JavaScript5. D

Android如何获取当前CPU频率和占用率

《Android如何获取当前CPU频率和占用率》最近在优化App的性能,需要获取当前CPU视频频率和占用率,所以本文小编就来和大家总结一下如何在Android中获取当前CPU频率和占用率吧... 最近在优化 App 的性能,需要获取当前 CPU视频频率和占用率,通过查询资料,大致思路如下:目前没有标准的

如何通过Golang的container/list实现LRU缓存算法

《如何通过Golang的container/list实现LRU缓存算法》文章介绍了Go语言中container/list包实现的双向链表,并探讨了如何使用链表实现LRU缓存,LRU缓存通过维护一个双向... 目录力扣:146. LRU 缓存主要结构 List 和 Element常用方法1. 初始化链表2.

python中列表list切分的实现

《python中列表list切分的实现》列表是Python中最常用的数据结构之一,经常需要对列表进行切分操作,本文主要介绍了python中列表list切分的实现,文中通过示例代码介绍的非常详细,对大家... 目录一、列表切片的基本用法1.1 基本切片操作1.2 切片的负索引1.3 切片的省略二、列表切分的高

java两个List的交集,并集方式

《java两个List的交集,并集方式》文章主要介绍了Java中两个List的交集和并集的处理方法,推荐使用Apache的CollectionUtils工具类,因为它简单且不会改变原有集合,同时,文章... 目录Java两个List的交集,并集方法一方法二方法三总结java两个List的交集,并集方法一