美文网首页
UI 优化(上)

UI 优化(上)

作者: 凯玲之恋 | 来源:发表于2020-04-23 10:09 被阅读0次

1 UI 渲染的背景知识

1.1 屏幕与适配

通常会比较关注屏幕的尺寸、分辨率以及厚度这些指标。

材质也是屏幕至关重要的一个评判因素。目前智能手机主流的屏幕可分为两大类:一种是 LCD(Liquid Crystal Display),即液晶显示器;另一种是 OLED(Organic Light-Emitting Diode 的)即有机发光二极管。
OLED 和 LCD 什么区别?
手机屏幕的前世今生 可能比你想的还精彩
Android 推荐使用 dp 作为尺寸单位来适配 UI,因此每个 Android 开发都应该很清楚 px、dp、dpi、ppi、density 这些概念

e3094e900dccacb9d9e72063ca3084ce.png

它会存在两个比较大的问题:

1.2 CPU 与 GPU

UI 渲染还依赖两个核心的硬件:CPU 与 GPU。

UI 组件在绘制到屏幕之前,都需要经过 Rasterization(栅格化)操作,而栅格化操作又是一个非常耗时的操作。

GPU(Graphic Processing Unit )也就是图形处理器,它主要用于处理图形运算,可以帮助我们加快栅格化操作。

1c94e50372ff29ef68690da92c6b468d.png

软件绘制使用的是 Skia 库,它是一款能在低端设备如手机上呈现高质量的 2D 跨平台图形框架,类似 Chrome、Flutter 内部使用的都是 Skia 库。

1.3 OpenGL 与 Vulkan

对于硬件绘制,我们通过调用 OpenGL ES 接口利用 GPU 完成绘制。

OpenGL是一个跨平台的图形 API,它为 2D/3D 图形处理硬件指定了标准软件接口。而OpenGL ES 是 OpenGL 的子集,专为嵌入式设备设计。

在官方硬件加速的文档中,可以看到很多 API 都有相应的 Android API level 限制。

d57d364750071b7eb39968fea1a1b15d.png
这是为什么呢?其实这主要是受OpenGL ES版本与系统支持的限制,直到最新的 Android P,有 3 个 API 是仍然没有支持。对于不支持的 API,我们需要使用软件绘制模式,渲染的性能将会大大降低。 cf13332abe87502c7d60ff78b6aeb931.png

Android 7.0 把 OpenGL ES 升级到最新的 3.2 版本同时,还添加了对Vulkan的支持。

Vulkan 是用于高性能 3D 图形的低开销、跨平台 API。相比 OpenGL ES,Vulkan 在改善功耗、多核优化提升绘图调用上有着非常明显的优势。

2 Android 渲染的演进

Android 图形系统的整体架构,以及它包含的主要组件。

7efc5431b860634224f1cd7dda8abd66.png

如果把应用程序图形渲染过程当作一次绘画过程,那么绘画过程中 Android 的各个图形组件的作用是:

  • 画笔:Skia 或者 OpenGL。我们可以用 Skia 画笔绘制 2D 图形,也可以用 OpenGL 来绘制 2D/3D 图形。正如前面所说,前者使用 CPU 绘制,后者使用 GPU 绘制。
  • 画纸:Surface。所有的元素都在 Surface 这张画纸上进行绘制和渲染。在 Android 中,Window 是 View 的容器,每个窗口都会关联一个 Surface。而 WindowManager 则负责管理这些窗口,并且把它们的数据传递给 SurfaceFlinger。
  • 画板:Graphic Buffer。Graphic Buffer 缓冲用于应用程序图形的绘制,在 Android 4.1 之前使用的是双缓冲机制;在 Android 4.1 之后,使用的是三缓冲机制。
  • 显示:SurfaceFlinger。它将 WindowManager 提供的所有 Surface,通过硬件合成器 Hardware Composer 合成并输出到显示屏。

2.1 Android 4.0:开启硬件加速

  • 在 Android 3.0 之前,或者没有启用硬件加速时,系统都会使用软件方式来渲染 UI。
    整个流程如上图所示:


    8f85be65392fd7b575393e5665f49a97.png

整个流程如上图所示:

  • Surface。每个 View 都由某一个窗口管理,而每一个窗口都关联有一个 Surface。

  • Canvas。通过 Surface 的 lock 函数获得一个 Canvas,Canvas 可以简单理解为 Skia 底层接口的封装。

  • Graphic Buffer。SurfaceFlinger 会帮我们托管一个BufferQueue,我们从 BufferQueue 中拿到 Graphic Buffer,然后通过 Canvas 以及 Skia 将绘制内容栅格化到上面。

  • SurfaceFlinger。通过 Swap Buffer 把 Front Graphic Buffer 的内容交给 SurfaceFinger,最后硬件合成器 Hardware Composer 合成并输出到显示屏。

CPU 对于图形处理并不是那么高效,这个过程完全没有利用到 GPU 的高性能。

2.2 硬件加速绘制

所以从 Androd 3.0 开始,Android 开始支持硬件加速,到 Android 4.0 时,默认开启硬件加速。


79c315275abac0823971e5d6b9657be8.png

硬件加速绘制与软件绘制整个流程差异非常大
最核心就是我们通过 GPU 完成 Graphic Buffer 的内容绘制。

此外硬件绘制还引入了一个 DisplayList 的概念,每个 View 内部都有一个 DisplayList,当某个 View 需要重绘时,将它标记为 Dirty。

当需要重绘时,仅仅只需要重绘一个 View 的 DisplayList,而不是像软件绘制那样需要向上递归。这样可以大大减少绘图的操作数量,因而提高了渲染效率。


f9da12b7c4d49f47d650cd8e14303c51.png

2.3 Android 4.1:Project Butter

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

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

2.3.1 VSYNC 信号

对于 Android 4.0,CPU 可能会因为在忙别的事情,导致没来得及处理 UI 绘制。

为解决这个问题,Project Buffer 引入了VSYNC,它类似于时钟中断。每收到 VSYNC中断,CPU 会立即准备 Buffer 数据,由于大部分显示设备刷新频率都是 60Hz(一秒刷新 60 次),也就是说一帧数据的准备工作都要在 16ms 内完成。

06753998a26642edd3481f85fc93c8bd.png

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

2.3.2 三缓冲机制 Triple Buffering

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

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


887c5ff4ae381733a95634c115c7a296.png

整个流程如下:

  • 每个 Surface 对应的 BufferQueue 内部都有两个 Graphic Buffer ,一个用于绘制一个用于显示。我们会把内容先绘制到离屏缓冲区(OffScreen Buffer),在需要显示时,才把离屏缓冲区的内容通过 Swap Buffer 复制到 Front Graphic Buffer 中。
  • 这样 SurfaceFlinge 就拿到了某个 Surface 最终要显示的内容,但是同一时间我们可能会有多个 Surface。这里面可能是不同应用的 Surface,也可能是同一个应用里面类似 SurefaceView 和 TextureView,它们都会有自己单独的 Surface。
  • 这个时候 SurfaceFlinger 把所有 Surface 要显示的内容统一交给 Hareware Composer,它会根据位置、Z-Order 顺序等信息合成为最终屏幕需要显示的内容,而这个内容会交给系统的帧缓冲区 Frame Buffer 来显示(Frame Buffer 是非常底层的,可以理解为屏幕显示的抽象)。

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


551fb7b5a8a0bed7d81edde6aff99653.png

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

Android Project Butter分析

2.4 数据测量

Project Butter 在优化 UI 渲染性能的同时,也希望可以帮助我们更好地排查 UI 相关的问题。

在 Android 4.1,新增了 Systrace 性能数据采样和分析工具。

Tracer for OpenGL ES 也是 Android 4.1 新增加的工具,它可逐帧、逐函数的记录 App 用 OpenGL ES 的绘制过程。

它提供了每个 OpenGL 函数调用的消耗时间,所以很多时候用来做性能分析。

但因为其强大的记录功能,在分析渲染问题时,当 Traceview、Systrace 都显得棘手时,还找不到渲染问题所在时,此时这个工具就会派上用场了。

在 Android 4.2,系统增加了检测绘制过度工具
《检查 GPU 渲染速度和绘制过度》

3 Android 5.0:RenderThread

虽然我们利用了 GPU 的图形高性能运算,但是从计算 DisplayList,到通过 GPU 绘制到 Frame Buffer,整个计算和绘制都在 UI 主线程中完成。

778a18e6f9f9c1d08a5f5e12645c21b1.png

UI 主线程“既当爹又当妈”,任务过于繁重。如果整个渲染过程比较耗时,可能造成无法响应用户的操作,进而出现卡顿。

GPU 对图形的绘制渲染能力更胜一筹,如果使用 GPU 并在不同线程绘制渲染图形,那么整个流程会更加顺畅。

在 Android 5.0 引入了两个比较大的改变。一个是引入了 RenderNode 的概念,它对 DisplayList 及一些 View 显示属性做了进一步封装。
另一个是引入了 RenderThread,所有的 GL 命令执行都放到这个线程上,渲染线程在 RenderNode 中存有渲染帧的所有信息,可以做一些属性动画,这样即便主线程有耗时操作的时候也可以保证动画流畅。

我们还可以开启 Profile GPU Rendering 检查。在 Android 6.0 之后,会输出下面的计算和绘制每个阶段的耗时:

5e61bfdec7dabd49b082bbbebb497cf2.png

如果我们把上面的步骤转化线程模型,可以得到下面的流水线模型。CPU 将数据同步(sync)给 GPU 之后,一般不会阻塞等待 GPU 渲染完毕,而是通知结束后就返回。

CPU 将数据同步(sync)给 GPU 之后,一般不会阻塞等待 GPU 渲染完毕,而是通知结束后就返回。而 RenderThread 承担了比较多的绘制工作,分担了主线程很多压力,提高了 UI 线程的响应速度。

7f349aefe7a081259218af30b9a9fc7d.png

在 Android 6.0 的时候,Android 在 gxinfo 添加了更详细的信息;
在 Android 7.0 又对 HWUI 进行了一些重构,而且支持了 Vulkan;
在 Android P 支持了 Vulkun 1.1

但是凡事都要两面看,硬件加速绘制虽然极大地提高了 Android 系统显示和刷新的速度,但它也存在那么一些问题。
一方面是内存消耗,OpenGL API 调用以及 Graphic Buffer 缓冲区会占用至少几 MB 的内存,而实际上会占用更多一些。

灰度 Inline Hook、GOT Hook

参考

20 | UI 优化(上):UI 渲染的几个关键概念

相关文章

  • UI 优化(上)

    1 UI 渲染的背景知识 1.1 屏幕与适配 通常会比较关注屏幕的尺寸、分辨率以及厚度这些指标。 材质也是屏幕至关...

  • Android UI性能优化

    Ui性能优化 参考博客:Android UI性能优化实战 识别绘制中的性能问题Android UI性能优化详解 1...

  • 日常学习

    1:日常中优化有哪些: 优化主要分为UI优化、内存优化、相应优化 UI优化 1、布局控件的选择:主要布局有Rela...

  • Android App优化

    本篇文章来简单介绍一下App优化的方式。 1、UI优化 UI优化主要是提高UI的绘制效率,包括减少UI层次,提高初...

  • Android性能优化大纲

    1.内存优化 内存泄漏 优化分析 内存优化工具 2.UI优化 UI卡顿分析 渲染优化 计算性能优化 3.电量优化 ...

  • UI优化

    UI优化,也就是体验优化 常见优化场景 1) 过渡绘制 2) 布局复杂度 3) 逻辑优化 4) 内存使用优化 UI...

  • Android UI优化

    Android性能优化 - UI篇Android性能优化 - CPU/GPU篇 一、UI层级优化 借助工具:Hie...

  • Android进阶篇:深度解析UI原理和高级的UI优化方式

    不知道UI原理如何做UI优化? 本文内容分为三个部分,UI原理、LayoutInflater原理、UI优化,篇幅有...

  • 性能优化

    Android UI性能优化实战 识别绘制中的性能问题性能优化(二) UI 绘制优化 通过Hierarchy Vi...

  • 无标题文章

    APP性能优化 UI卡顿优化 View的绘制原理 UI卡顿原理分析 UI卡顿检测分析 BlockCanary原理分...

网友评论

      本文标题:UI 优化(上)

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