Android 高质量开发 — UI 优化

Android 之理解 VSYNC 信号

2019-11-19  本文已影响0人  godliness

UI 优化系列专题,来聊一聊 Android 渲染相关知识,主要涉及 UI 渲染背景知识如何优化 UI 渲染两部分内容。


UI 优化系列专题

View 绘制流程之 setContentView() 到底做了什么?
View 绘制流程之 DecorView 添加至窗口的过程
深入 Activity 三部曲(3)View 绘制流程
Android 之 LayoutInflater 全面解析
关于渲染,你需要了解什么?
Android 之 Choreographer 详细分析

Android 之如何优化 UI 渲染(上)
Android 之如何优化 UI 渲染(下)


优化是无止境的,Google 在 2012 年的 I/O 大会上宣布了 Project Butter 黄油计划,并且在 Android 4.1 中正式开启了这个机制。

Project Butter 主要包含两个组成部分:一个是 VSYNC,另一个是 Triple Buffering。

也正是从这时候开始,作为严重影响 Android 口碑的 UI 流畅性问题便得到了有效解决。今天我们就来聊一聊这两个组成部分的工作原理,不过在理解它们之前,需要先看下所涉及的另外两个概念:

1. 刷新率

表示屏幕在一秒内刷新画面的次数, 刷新率取决于硬件的固定参数,单位 HZ(Hz)。例如常见的 60 Hz,即每秒钟刷新 60 次。

逐行扫描

显示器并不是一次性将画面显示到屏幕上,而是从左到右边,从上到下逐行扫描显示,不过这一过程快到人眼无法察觉到变化,以 60 Hz 刷新率的屏幕为例,即 1000 / 60 ≈ 16ms。

2. 帧速率

表示 GPU 在一秒内绘制操作的帧数。例如电影采用 24 fps、Android 系统采用 60 fps,即一秒钟绘制 30 / 60 帧画面。更多内容参考《Why 60 fps》。


屏幕撕裂

现在,刷新频率和帧速率需要一起合作,才能使图形内容呈现在屏幕上,GPU 会获取图形数据进行绘制, 然后硬件负责把图像内容呈现到屏幕上,这一过程在应用程序的生命周期内一遍又一遍的发生。

不幸的是,刷新频率和帧率并不总是能够保持相对同步,如果你的帧速率实际比刷新率快,例如帧速率是 120 fps,显示器的刷新频率为 60 Hz。此时将会发生一些视觉上的问题。

由于显示器提取画面是从左到右,从上到下逐行扫描提取图像显示,这一过程需要 16ms(1000 / 60),当 GPU 利用一块内存区域写入帧数据时,从顶部开始新一帧覆盖前一帧,并立刻输出一行内容。当屏幕刷新时,它并不知道图像缓冲区的状态,因此它从 GPU 抓取的帧并不是完整的数据。也就是它有一半的前一帧和一半的当前帧,这种情况被称之为屏幕撕裂

解决该问题的方案是双缓冲,即 GPU 和显示器都有各自的工作缓冲区。GPU 始终将完成的一帧绘制数据写入到 Back Buffer,而显示器使用 Frame Buffer。当屏幕刷新时,Frame Buffer 并不会发生变化。Back Buffer 根据屏幕的刷新将数据 copy 到 Frame Buffer,这便是 VSYNC 的用武之地。

在 Android 4.1 之前,Android 使用双缓冲机制。怎么理解呢?一般来说,同一个 View Hierarchy 内的 View 都会共用一个 Window,也就是共用一个 Surface。

而每个 Surface 都会有一个 BufferQueue 缓存队列,但是这个队列会由 SurfaceFlinger 管理,通过匿名共享内存与 App 应用层交互。

整个流程如下:

Android 一直使用 VSYNC 来阻止屏幕撕裂,对于 Android 4.0,CPU 可能会因为在忙其他的事情,导致没来得及处理 UI 绘制。所以从 4.1 开始 VSYNC 则更进一步,VSYNC 脉冲现在用于开始下一帧的所有处理。

VSYNC 类似于时钟中断,每收到 VSYNC 中断,CPU 会立即准备 Buffer 数据,由于大部分显示设备刷新频率都是 60Hz(一秒刷新 60 次),也就是说一帧数据的准备工作都要在 16ms(1000/60≈16)内完成。

这样应用总是在 VSYNC 边界上开始绘制,而 SurfaceFlinger 总是在 VSYNC 边界上进行合成。这样便可以消除卡顿,并提升图形的视觉表现。

Triple Buffering

如果理解了双缓冲机制的原理,那就非常容易理解什么是三缓冲区了。如果只有两个 Graphic Buffer 缓存区 A 和 B,如果 CPU/GPU 绘制过程较长,超过了一个 VSYNC 信号周期,因为缓冲区 B 中的数据还没有准备完成,所以只能继续展示 A 缓冲区的内容,这样缓冲区 A 和 B 都分别被显示设备和 GPU 占用,CPU 则无法准备下一帧的数据。

如果再提供一个缓冲区,CPU、GPU 和显示设备都能使用各自的缓冲区工作,互不影响。简单来说,三缓冲机制就是在双缓冲机制基础上增加了一个 Graphic Buffer 缓冲区,这样可以最大限度利用空闲时间,带来的坏处是多使用了一个 Graphic Buffer 所占用的内存。

从图中可以看出,缓冲区 B 花费的时间太长,并且正在使用 A 来显示当前帧。不过这次,系统不是在重复的缓冲区中浪费时间,而是创建一个 C 缓冲区,并开始处理下一帧。三重缓冲降低了 jank 的进一步加剧。

三缓冲并不总是存在,通常情况下仅运行一个双缓冲区,但是当发生延迟等情况时,第三个缓冲区就会出现,以便降低延迟的加剧。对于 VSYNC 信号和 Triple Buffering 更详细的介绍,可以参考《Project Butter - How it works and What it added?》。

Android 渲染框架非常庞大,而且演进的非常快。感兴趣的朋友可以进一步阅读下面的参考资料。


扩展阅读
上一篇下一篇

猜你喜欢

热点阅读