课程名称:Top团队大牛带你玩转Android性能分析与优化
课程章节:App性能概览与平台化实践
主讲老师:随风绽放
在计算机硬件中,通常 CPU 用来处理数据,GPU 用来渲染数据,Android 系统也不例外,绘制过程首先是 CPU 准备数据,通过 Driver 层把数据交给 GPU 渲染,其中 CPU 主要负责 Measure、Layout、Record、Execute 的数据计算工作,GPU 负责 Rasterization(栅格化)、渲染。
无论是 CPU 准备的数据,还是 GPU 要渲染的数据,都是一帧一帧的形式。我们看到的界面也是由一帧一帧的图像连续显示而来。对于人眼来说,每秒看到 60 帧是比较流畅的,即 FPS(Frame Per Second)为 60,1/60 = 0.0167,即每 16 ms进行一次准备、渲染工作。
常用的布局优化工具有以下几种:
Android 的布局加载时 IO 操作,比如 xml 文件要通过 xml 解析器才能加载到内存里。在加载 xml 文件的时候,还会用到反射,而反射本来性能就不高。
在对布局优化前,我们需要找到一个指标,作为衡量布局优化效果的标准。常用有下面几种方式来获取页面加载的耗时。
通过上面的方式可以获取页面加载的耗时,为了更加精细化的优化,如何获取每个控件加载的耗时呢? 可以通过 LayoutInflater.Factory(考虑兼容性可以使用 LayoutInflaterCompat.setFactory2) 来实现,这种方式代码侵入性小。通过自定义 Factory,在 onCreateView() 方法的中实现对每个控件加载耗时的计算。使用时需要注意,这个设置应该在super.onCreate()之前,否则无效。
public interface Factory { /** * Hook you can supply that is called when inflating from a LayoutInflater. * You can use this to customize the tag names available in your XML * layout files. * * <p> * Note that it is good practice to prefix these custom names with your * package (i.e., com.coolcompany.apps) to avoid conflicts with system * names. * * @param name Tag name to be inflated. * @param context The context the view is being created in. * @param attrs Inflation attributes as specified in XML file. * * @return View Newly created view. Return null for the default * behavior. */ public View onCreateView(String name, Context context, AttributeSet attrs); }
前面介绍到布局文件加载的过程是一个 IO 过程,创建对象时还使用到反射,使用反射创建对象的速度比通过 new 关键字创建要慢 3 倍左右。所以布局优化的方向是尽量减少 IO 过程,减少通过反射来创建对象。
这里介绍一种侧面优化布局的方式即异步实现布局的加载。AsyncLayoutInflater 是一个异步初始化布局的 Helper 类。它的本质就是把对布局文件的 inflate 放入到了子线程里面,等到初始化成功后,在通过接口抛回到主线程。
有没有一种方法从根本上来解决io操作慢、反射慢的问题呢?答案是直接用 Java 代码写布局文件,这样会引入新问题,不便于开发、可维护性差。用 xml 来写布局,虽然IO慢、反射慢,因为方便维护,可读性好,还方便写UI时进行预览。那有没有一个方案既能保留XML优点,还能解决其性能问题呢?X2C 就是这样一个方案。
X2C 的原理是在 APT 编译期将需要翻译的布局 xml 文件翻译生成对应的 java 文件,这样对于开发人员来说写布局还是写原来的 xml ,但对于程序来说,运行时加载的是对应的 java 文件。
不论是异步加载 AsyncLayoutInflater,还是 X2C 方案,都要考虑下面的问题:
前面对布局优化的方案都是针对布局加载进行优化的,这里介绍一下在视图绘制时的优化方案。Android 中的视图绘制要经历三个过程:
三个过程的实现都会涉及 View 树自顶向下的遍历,有时甚至还会触发多次,所以每个阶段都会出现耗时的情况,都是我们去优化的方向。布局优化在绘制阶段的准则是尽量减少 View 树的层级,页面布局要做到宽而浅、避免窄而深。
ConstraintLayout 是在 Android Studio 2.2 中主要的新增功能之一,它可以有效地解决布局嵌套过多的问题,实现几乎完全扁平化布局,构建复杂布局的性能更高,它有点类似于RelativeLayout,但远比RelativeLayout要更强大。除了使用 ConstraintLayout,还可以考虑以下的优化方式:
过度绘制就是在同一个区域中叠加了多个控件,重复的叠加就是过度绘制,很可能会造成刷新率下降,造成卡顿的现象。对于过度绘制的测试主要通过人工进行测试,也是发现应用过渡绘制的首选途径,通过打开开发者选项中的 显示GPU过度绘制。过渡绘制可以通过颜色标识,按照从好到差依次是:蓝、绿、淡红、红。
避免过度绘制的方法:
布局优化的其它技巧:
课程从布局加载、布局绘制的角度介绍了布局优化的方案,涵盖了布局优化的所有手段。既提供了获取优化指标的方式,又提供了优化的手段,非常实用。