Android性能优化之UI篇

概要

  1. 我们开发中的布局文件如何显示到屏幕上的?
  2. 为什么是60FPS?
  3. 卡顿是怎么产生的?

一、Pipeline流程:

pipeline流程

当更新可视化物品的时候,
android在设备上绘制图案前 需将xml转化成GPU可执行的文件 然后进行屏幕渲染。这要借助一个内部对象,它叫做显示列表(Display List),显示列表基本上包含了所有用于GPU渲染的信息。我们通过提交绘图命令至GPU来执行显示列表。
pipeline流程

如果将来还想再次渲染这个视图,比如变换button在屏幕中的位置,我们只需再次执行显示列表;
但是,在将来视图的某些部分发生改变,之前的显示列表可能不在有效,因此我们需要重新生成显示列表,并再次执行,从而在屏幕上更新。

但是请记住:每次视图的绘制内容更改时,它都会重复显示列表的生成过程,并且在屏幕上再次执行,它的性能各不相同,要根据视图的具体复杂度来定。根据视图的可视性变化,对渲染途径也会产生影响。

pipeline流程

例如,一个文字框突然增大了一倍,导致父容器必须在更新自己的大小前重新摆放兄弟视图。在这种情况下,我们更新了一个视图,它将其它需要改动的工作进行了串联。这些类型的可视化更改要求出现渲染途径的额外阶段。当你的视图的大小更改时,measure阶段会开始,它会浏览你视图的层级,要求每一个视图提供新的大小。如果你改变了物体的位置,要进行layout。或者如果一个viewgroup重置了它的子View,layout阶段也会启动,并穿过层级,计算出每个图形应该被放置在何处。

这些阶段都是需要时间来允许的,独立运行时问题不大,但当大量视图变无效或更改时就会造成巨大的性能损耗。也就是说:要想提升渲染系统的整体性能,布局失效最小化是一个不错的开端。

pipeline tool

CPU:

Measure –> Layout –> Record –> Execute

GPU:

Rasterization

在CPU方面,最常见的性能问题来自于不必要的或失效的布局,这是视图层次结构的一部分,必须进行测量,拆除和重建。 这通常源自两方面。 1.显示列表在你的帧时间内重建得太多了;2.花费了太多的时间用于视图层次结构中不必要的或失效的部分。

在GPU方面,最常见的问题是过度绘制。 重复绘制像素会浪费GPU。

二、Android VSYNC

显卡的生成图像的频率与显示器的刷新频率是相互独立的。因此,就涉及到了一个配合的问题。最理想的情况是两者之间的频率是相同且协同进行工作的,在这样的理想条件下,达到了最优解。但实际中显卡的生成图像的频率是变化的,如果没有有效的技术手段进行保证,两者之间很容易出现这样的情况:当GPU还在渲染下一帧图像时,显示器却已经开始进行绘制,这样就会导致屏幕撕裂(Tear)。这会使得屏幕的一部分显示的是前一帧的内容,而另一部分却在显示下一帧的内容。

屏幕撕裂(Tear)的问题,早在 PC 游戏时代就被发现, 并不停的在尝试进行解决。 其中最知名可能也是最古老的解决方案就是 V-Sync 技术。

V-Sync 的原理简单而直观:产生屏幕撕裂的原因是显卡在屏幕刷新时进行了渲染,而 V-Sync 通过同步渲染/刷新时间的方式来解决这个问题。显示器的刷新频率为 60 Hz,若此时开启 V-Sync,将控制显卡渲染速度在 60 Hz 以内以匹配显示器刷新频率。这也意味着,在 V-Sync 的限制下,显卡显示性能的极限就限制为 60 Hz 以内。(VSYNC信号来自显示模块)

Test UI performance

  • Rendering Performance 101
  • Why 60fps?
  • Android, UI, and the GPU
  • Invalidations, Layouts, and Performance
  • Analyzing UI Performance with Systrace

drawing_with_vsync

double_buffering

triple_buffering

后续了解android Double Buffer和Triple Buffer机制

三、如何优化

3.1 针对CPU工作过程进行优化

1)删除多余的嵌套布局

2)使用 merge/include

3)使用性能更好的布局Understanding the performance benefits of ConstraintLayout)

3.2 针对GPU工作过程进行优化

减少过度绘制

1)在布局中删除不需要的背景。

2)减少视图层次结构。

3)降低透明度。

实例:
  • getWindow().setBackgroundDrawable(null);
  • android:background:”@null” // xml
  • Canvas.clipRect();该方法用于裁剪画布,也就是设置画布的显示区域,调用clipRect()方法后,只会显示被裁剪的区域,之外的区域将不会显示
  • Canvas.quickReject();可以用来测试指定区域是否在裁剪范围之外,如果要绘制的元素位于裁剪范围之外,就可以直接跳过绘制步骤。
  • Be flat
  • Avoid unnecessary layouts

四、优化常用工具

  1. Systrace
  2. Profile GPU rendering
  3. Lint
  4. Layout InspectorHierarchy Viewer

参考:

how the rendering pipeline works

Android Application Performance — Step 1: Rendering

猜你喜欢

转载自blog.csdn.net/zclongwork/article/details/80339593