美文网首页Android
Android 绘制与 16ms 不得不说的故事

Android 绘制与 16ms 不得不说的故事

作者: tandeneck | 来源:发表于2020-08-26 22:04 被阅读0次

刷新率、帧率

刷新率:每秒屏幕刷新次数。
帧率:GPU 在一秒内绘制的帧数。
虽然现在有的厂商推出了高刷新率的手机,但是主流的还是 60Hz,即1秒显示60帧,1000ms / 60 frames ≈ 16.67 ms/frames,为了保证 App 的流畅度,我们应该尽量让每帧的绘制时间不超过 16ms。

Android 系统显示原理

Android 的显示过程可以简单概括为:应用程序把经过 measure(测量)、layout(布局)、draw(绘制)后的 surface 缓存数据,通过 SurfaceFlinger 把数据渲染到显示屏幕上,通过 Android 的刷新机制来刷新数据。换言之,应用层负责绘制,系统层负责渲染,通过进程间通信把应用层需要绘制的数据传递到系统层服务,系统层通过刷新机制把数据更新到屏幕上。

以下是有关概念的解释:

  • Surface,surface持有用于最终显示在屏幕上的像素数据,一个 window 对应着一个 Surface,Android 中 每个 view 都会绘制到 Surface 上,其中包含了两个(小于4.1版本)或者三个(4.1及以上版本)缓冲区中。
  • Window,window 实际上是 View 的直接管理者,每个 window 拥有一个 surface,它的具体实现是 PhoneWindow,WindowManager 是外界访问 window 的入口。
  • Canvas ,Canvas 是 Surface 绘图时返回的一个接口,并提供丰富的绘图的 API,用来进行实际的绘图操作。
应用层

在 Android 中每个 view 都会经过 measure 和 layout 来确定其所在的大小和位置,然后绘制到 surface (缓冲区上),绘制是由 ViewRootImpl 类中 performTraversals() 方法发起的。

Android支持两种绘制方式:\color{red}{软件绘制}\color{red}{硬件绘制}。硬件极速从 Android 3.0 开始支持,它在 UI 显示和绘制效率方面远高于软件绘制,但是它的也有缺点:

  • 耗电,GPU 功耗高于 CPU。
  • 兼容性,不兼容某些接口和方法。
  • 内存占用大。
系统层

经过多次绘制后,要显示的 view 相关的数据存储(如大小和位置)在 Surface 的缓冲区中,接下来渲染操作交由系统进程中的 SurfaceFlinger 服务来完成,这是一个 IPC(进程间通信)过程。SurfaceFlinger 的主要工作流程如下:

  • 响应客户端事件,创建 Layer 与客户端的 Surface 建立连接。
  • 接收客户端数据和属性,修改 Layer 属性,如尺寸、颜色、透明度等。
  • 将创建的 Layer 内容刷新到屏幕上。
  • 维持 Layer 的序列,并对 Layer 最终输出做出裁剪计算。

当 Android 应用层在图形缓冲区中绘制好 View 层次结后,应用层通过 Binder 机制与 SurfaceFlinger 通信并借助一块匿名共享内存把图形缓冲区交给 SurfaceFlinger 服务。由于单纯的匿名共享服务在传递多个窗口数据时缺乏有效的管理,所以匿名共享内存就被抽象为一个更上层的数据结构——SharedClient,在 SharedClient 中,最多有 31 个 SharedBufferStack,每个 SharedBufferStack 都对应一个 Surface 即一个 Window。这表明一个 Android 应用程序最多可以包含 31 个 window

绘制的过程首先是 CPU 准备数据(measure、layout等),GPU 负责栅格化、渲染。因为图像 API 不允许 CPU 直接与 GPU 通信,所以要通过一个图形驱动的中间层来进行连接。图形驱动里面维护了一个队列,CPU 把 display list(待显示的数据列表)添加到队列中,GPU 从这个队列中取出数据进行绘制,最终在屏幕上显示出来,如下图所示:


Android 系统每隔 16ms 会发出 VSYNC 信号,触发对 UI 进行渲染,如果每次都渲染成功,就能够达到流畅画面所需的 60PS。

垂直同步、双缓冲和三级缓冲

双缓冲

双缓冲顾名思义是有两个缓冲区(上文提到的 SharedBufferStack),分别是 FontBuffer(又叫作 FrameBuffer) 和 BackBuffer。UI 总是先在 Back Buffer 中绘制,然后再和 Font Buffer 交换,渲染到显示设备中,即只有当另一个 buffer 的数据准备好后,才会通过系统调用来通知显示设备切换 Buffer。

双缓冲机制在大部分情况下是适用的,但是如果某个环节出现了问题,CPU 资源就有可能存在浪费,如下图所示:

VSYNC 类似与时钟中断。竖线分割的部分代表 16ms 的时间段。正常情况下,在每一时间段内,Display 显示一帧数据(即每秒60帧)。

上图中在第二个 16ms 时间段内,Display 本应显示 B 帧,但是因为 GPU 还在处理 B 帧,导致 A 帧被重复显示。与此同时,在第二个时间段内,处于 CPU 处于空闲状态,造成了浪费。因为 A Buffer 被 Diaplay 在使用(SufaceFlinger 用完后不会释放当前的 Buffer,只会释放旧的 Buffer),B Buffer 被 GPU 在使用,这就是 双缓冲机制的局限性。

三级缓冲

Android 4.1 版本中对 Android Display 系统进行了重构,引入了三个核心元素:

  • \color{red}{VSYNC(垂直同步)} 。VSYNC 是 Vertical Synchronization 的缩写,可以认为是一种定时中断。
  • \color{red}{Choreography(编舞者)} 。 Choreographer 起调度的作用,将绘制工作统一到 VSYNC 的某个时间点上,使应用的绘制工作有序,具体来说是当收到 VSYNC 信号时,调用用户设置的回调函数,回调类型的优先级从高到低为 ALLBACK_INPUT、CALLBACK_ANIMATION、CALLBACK_TRAVERSAL。
  • \color{red}{TripleBuffer} 。Triple Buffer 的引入是为了解决上文提到的 CPU 没有 buffer 可用的窘境。如下图所示:

在第二个 16ms 时间内,CPU 使用 C Buffer 绘图,虽然还是会多显示 A 帧一次,但是后续的显示相对双缓冲机制就顺滑多了。但是 Buffer 并不是越多越好,从上图可知,在第二个时间内,CPU 绘制的第 C 帧数据要到第四个 16ms 才能显示,这比双 Buffer 多了 16ms 的延迟。由此可见,双缓冲保证低时延,三缓冲保证稳定性。

整个流程简单来说就是 CPU/GPU 会接收到 VSYNC 信号,触发对 UI 进行渲染(每 16ms 显示一帧)。在 16ms 内需要完成两项任务:将 UI 对象转换为一系列多边形和纹理(栅格化)和 CPU 传递处理数据到 GPU,更详细的内容可以看这篇文章Android的16ms和垂直同步以及三重缓存

拓展

了解 Android 绘制流程后,我们不难反推 Android 应用程序卡顿的原因:

  • 绘制任务太重,绘制一帧的时间耗时太长,超过了 16ms。常见的场景是布局层级嵌套过多、过度绘制等。
  • 主线程阻塞,导致 VSYNC 信号到来时还没有准备好数据导致丢帧。

参考

相关文章

  • Android 绘制与 16ms 不得不说的故事

    刷新率、帧率 刷新率:每秒屏幕刷新次数。帧率:GPU 在一秒内绘制的帧数。虽然现在有的厂商推出了高刷新率的手机,但...

  • Android界面性能优化必读

    一. Android渲染知识 1.1 绘制原理 Android系统要求每一帧都要在 16ms 内绘制完成,平滑的完...

  • Android性能优化

    基础原理 绘制原理(16ms原则):Android系统每隔16ms发出VSync信号,触发对UI进行渲染,这就意味...

  • Android 绘图渲染原理学习

    一、Android渲染机制 Android的渲染机制,我们要知道Android系统每隔16ms就重新绘制一次Act...

  • Android 性能优化: Render + Hierarchy

    Android 什么情况下会出现卡顿现象: android 绘制的机制是每60帧(16ms)绘制一次;正常来讲程序...

  • 性能优化之渲染优化

    一、卡顿现象 Android系统每隔16ms就重新绘制一次Activity,也就是说,我们的应用必须在16ms内完...

  • android编舞者ChoreoGrapher

    Choreographer的作用 结合上篇Android 绘制原理可知道,屏幕每16ms 显示frame buff...

  • 性能优化实践(二)-布局优化

    一、简介 众所周知的Android系统每隔16ms重新绘制一次视图,也就是说你的app必须在16ms内完成屏幕刷新...

  • 过度绘制分析及解决方案

    过度绘制 绘制原理 Android系统要求每一帧都要在 16ms 内绘制完成,平滑的完成一帧意味着任何特殊的帧需要...

  • android view 渲染

    Android系统每隔16ms就重新绘制一次,也就是说,我们的应用必须在16ms内完成屏幕刷新的全部逻辑操作,即每...

网友评论

    本文标题:Android 绘制与 16ms 不得不说的故事

    本文链接:https://www.haomeiwen.com/subject/hestsktx.html