Android性能优化(二):布局优化

概念

合理的布局,能够有效地提高性能,加快页面的显示速度,简化逻辑的复杂度。而布局对于Android性能的影响,则主要包含两个方面:测量+绘制。

作用

通过布局的优化,有效的减少页面的卡顿、丢帧等情况,实现应用的流畅。

基础知识

为什么布局复杂的时候就容易卡顿?5.0系统之后增加的硬件加速是什么,为什么开了硬件加速能够提高流畅度?我写了个xml文件,怎么就显示到手机屏幕上了?要想了解这些都是和UI布局优化有关的知识,那么就不得不讲一下布局优化的一些基础知识了,可能会枯燥,但是我相信,当你看完之后,对上面的问题都会有一个明确的答案了。

GPU VS CPU

平时我们说的CPU和GPU是什么?当说到这个问题的时候,我们不得不说一下栅格化。我们屏幕上每一个数据(图片、文字、字母、数字等等)的显示,都是由一个个的小格子组成的。这就是栅格化。这种将文字进行栅格化的功能,费时费力。所以Android系统将这部分功能单独交给了GPU来处理。
CPU只负责将xml布局中的按钮等转化为纹理,然后GPU负责将纹理进行栅格化之后进行渲染。

Open GL

GPU是一个单独的硬件,而且市面上各种GPU型号那么多,CPU将数据传输给GPU的时候,肯定不是说想给就给,必须要遵守一定的接口规则。也就是Open GL。
Open GL是一个跨编程语言、跨平台的编程接口规格的专业的图形程序接口。而CPU和GPU之间的纹理数据的传输,就是通过 Open GL接口来实现的。

渲染原理

CPU和GPU的作用已经清楚了,那么,我们的交给GPU的纹理数据又是如何显示在我们的显示屏上的呢?这时候就涉及到了Android的渲染原理了。

图形消费者-显示屏

显示屏的内容,是从硬件帧缓冲区读取的。会从Buffer的起始位置开始,扫描整个Buffer,然后将内容映射到显示屏。

这里包含了两个缓冲区:
前缓冲区:用来显示内容到屏幕帧缓冲区。
后缓冲区:用于后台合成下一帧图形的帧缓冲区。
很明显,通过两个缓冲区的角色互换能够有效的加快图形的显示。

图形生产者

既然显示屏能够从缓冲区拿到数据并显示,那么肯定是有一个模块的功能是用来生产这些数据的。
为了更好地理解这一部分的功能,我们先来看一下常用的微信页面,并将其进行拆分。

Surface

我们的应用是可以多层显示的,每一层其实都是一个Surface,它就像一个画板。View的onDraw方法,会将相应的布局绘制到Surface内,而每层数据都对应一个Surface。你可以理解为Surface是Android窗口的描述。一个应用最多是可以包含31个窗口的。

GraphicBuffer

Surface内部存在多个缓冲区,形成一个缓冲队列BufferQueue,缓冲则是通过GraphicBuffer来表示。j就像Surface是Android窗口的描述一样,GraphicBuffer则是Android中图形Buffer的描述。

刷新机制

Android系统每隔16ms发出VSYNC信号,触发对UI进行渲染,如果每次渲染都成功,这样就能够达到流畅的画面所需的60FPS。
如果我们想要实现流畅的画面,那么就必须保证从整个渲染过程必须在16ms内完成。如果页面比较复杂,导致了onLayout、onDraw的时间特别长或者复杂的处理占用了主线程较多的时间,导致16ms无法完成后缓冲区的准备工作,那么当前就没办法实现前后缓存区的交换,从而导致页面中仍然显示之前的数据,给人一种卡顿的感觉。
我们来看一个卡顿的具体流程。

  1. 1.Display显示第0帧数据,此时CPU和GPU渲染第1帧画面,而且赶在Display显示下一帧前完成
  2. 2.因为渲染及时,Display在第0帧显示完成后,也就是第1个VSync后,正常显示第1帧
  3. 3.由于某些原因,比如CPU资源被占用,系统没有及时地开始处理第2帧,直到第2个VSync快来前才开始处理
  4. 4.第2个VSync来时,由于第2帧数据还没有准备就绪,显示的还是第1帧。这种情况被Android开发组命名为“Jank”。
  5. 5.当第2帧数据准备完成后,它并不会马上被显示,而是要等待下一个VSync。

    优化工具

    对于UI卡顿的问题,是有很多的诱因的。比如说页面复杂、层级较深、Bitmap过大、一些操作占用了UI线程等等。如果我们能够通过经验就能定位到问题,那当然最好了。但是很多情况下我们无法得知到底是什么原因导致的卡顿,这时候我们就需要通过工具来进行辅助了。

    Systrace

    systrace能够有效的跟踪系统的I/O操作、内核工作队列、CPU负载以及Android各个子系统的运行状况,然后生成HTML报告,可以用来分析我们的卡顿和渲染问题。
  • DDMS在Android Studio3.0以后的版本已经去掉了,需要通过sdk\tools\monitor.bat来启动。
  • 代码方式如下
    systrace没有办法控制Trace的开始和结束。如果我们确定要分析某个部分的代码,我们必须要为这段代码指定具体的Label,以便能够在systrace生成的视图中能够找到我们的trace信息。这时候就通过代码方式,在要监控的代码开始和结果通过添加监控代码来进行处理。

    1
    2
    TraceCompat.beginSection("xxx");
    TraceCompat.endSection();
  • 结果分析
    通过浏览器打开以后,在左侧找到在Frames。可以看到右侧有红、粉、绿三种颜色的圆圈。一般红色表示有问题,需要优化的地方。

Layout Inspector

Layout Inspector是Android Studio自带的视图层次结构分析工具(Android Studio2.2以后),取代了以前经常使用的Hierarchy View工具。它允许在运行时检测应用程序的视图层次结构。
应用启动之后,直接在Android Studio中通过Tools->Layout Inspector。就可以启动Layout Inspector。(该版本是Android Studio4.0。老版本是Tools->Android->Layout Inspector)。

打开之后,能够看到手机当前的页面,左侧则是页面的布局层级。右侧则是选中的控件的属性。

通过这个功能,能够分析布局的层级结构,减少不必要的层级,减少overdraw的问题,从而达到渲染优化的效果。

TraceView

TraceView是一种可视化工具,可以看出代码在运行时的一些具体信息,方法的调用时长,次数,时间比率等等。了解代码运行过程中的效率问题,从而针对性的改善代码。对于systrace中不能够精确定位问题的,TraceView则能够精确地显示出来。

提前监控

既然经常出现布局比较耗时的问题,那么有办法去获取布局耗时的方案么?答案当然是肯定了的了,而且方法还不止一种呢~~

方法前后增加计时

这种方法,相信你应该用过。通过在setContentView()方法前后增加代码,然后计算其耗时,从而监控布局是否出现问题。先不说你是不是个勤劳的cver,这种方法,写起来累是肯定的。少的时候还好,但是一旦页面特别多,估计写着写着就cu死了~

AOP切面编程

面向切面编程,这种在后台开发中,属于已经用烂了的技术,但是在Android端,其实相对来说要少很多。既然是切面编程,那么必然是需要一个切点,能够织入我们自己的代码。这里我们的切入点就是setContentView方法了。通过@Around来进行切面处理

1
2
3
4
5
6
7
8
9
10
11
12
@Around("execution(* android.app.Activity.setContentView(..))")
public void getSetContentViewTime(ProceedingJoinPoint joinPoint){    
Signature signature = joinPoint.getSignature();    
String name = signature.toShortString();    
long time = System.currentTimeMillis();    
try {        
joinPoint.proceed();    
catch (Throwable throwable) {
throwable.printStackTrace();    
}
LogHelper.i(name  + " cost " + (System.currentTimeMillis() - time));
}

重装应用,然后打开几个页面,就可以看到对应的页面布局加载耗时日志信息了

总结

布局优化方向

  • 使用ConstraintLayout降低布局嵌套层级。
  • 避免嵌套使用RelativeLayout
  • 使用ViewStub、Merge,ViewStub是一种高效占位符,用于延迟初始化。
  • onDraw中避免创建大对象,进行耗时操作。
  • 使用异步布局框架Litho
  • 使用Flutter实现高性能的UI布局
  • 避免层级叠加,去掉多余的backgroud

布局优化工具

  • Choreographer:统计线上的FPS
  • LayoutInflaterCompat.setFactory2:可以更细粒度地去检测每一个控件的加载耗时。
  • LayoutInspector:可以很方便地看到每一个界面的布局层级。
  • Systrace:Systrace可以很方便地看到每帧的具体耗时以及这一帧在布局当中它真正做了什么。
您的支持是我原创的动力