app渲染优化
一. 渲染优化
1)移除背景
在每一个Activity中,都有一个setContentView()方法,将布局添加到Activity。在这一过程中除了所编写的布局文件外,还有许多其他的视图被添加到视图层级中。

视图层级中最顶层的视图,叫做DecorView。他是视图加载的第一个。在主题(theme)中为Activity 设置背景,就是通过他来显示。然而,这个默认的背景通常会被所编写的布局文件中的背景所遮盖,这意味着会给Gpu带来额外的工作,减慢渲染速度,影响帧率。诀窍就是避免绘制这个背景,从而提供啊性能。
移除这个背景图片的办法,就是Activity的主题中添加这个属性。
<style name="Theme.NoBackground" parent="android:Theme"></style>
以上这一点很有用,因为大部分情况下,Activity都是全屏的,并且整个decorView会被Activity中的非透明子视图所覆盖,一个更好的实践是,将Activity的布局背景移动到窗口的DecorView中,这样做的主要原因是Decorviewd 背景绘制,遭遇其他任何布局。也就是用户可以看到这个背景,无论其他ui加载花费了多少时间,都不会让用户误以为应用程序不在加载中。
<style name="Theme.NoBackground" parent="android:Theme">
<item name="android:windowBackground">@drawable/background</item>
</style>
综合来看,这两点变化不能为性能带来明显改善,但是能为用户带来平滑的体验。
2)过度绘制
布局层级越复杂,ui加载速度越慢——《android 高性能编程》
- 在ui设计阶段就控制好view的数量,全面的推广约束布局
- 使用Hierarchy Viewer 调试Gpu过度绘制(传音控股很强调这个,有点老派了)
- Android对9-patches图进行了优化,被遮盖的部分会被安卓渲染器优化为透明,而透明像素部分不会被系统渲染绘制。所以可一定程度减少过度绘制
- <include/ > <merge>标签,的使用
- 采用ViewStub技术。一旦为ViewStub 变为visible 或者被inflate , 它便不再可用,因为他在布局层级中的位置被实例化出来的布局所替代,因此不能再被访问。该类非常有用,特别是面对i个复杂的布局层级时,可以通过ViewStub延迟部分view的加载,缩短首次加载时间,以及减少一些不必要的内存分配。
3)硬件加速
起初是可选项,在manifest中通过声明来开启,现在Android 4. 0以后,默认开启
每个view的重绘都会耗费大量的cpu性能,但是硬件加速模型的引入,让重绘不会立即执行,view被存储起来了。
4)自定义控件
控件在自定义以后,需要谨慎的对他进行测试。验证自定义控件是否高效的一种方式是,将该控件单独置于布局文件中,观察Gpu性能的变化,然后换到大环境中。
- 检查清楚何时何处调用了View.invalite() 和View.requestLayout()方法,因为这会影响到整个UI,拖慢GPU和它的帧速率。
- 不应该在view.onDraw()方法及其所调用的方法中,进行任何的对象分配。
5)调试Gpu过度绘制
打开手机开发者设置,进入开发者选项页面,打开gpu绘制调试开关。
这时,手机下方会有一些柱状图:
- 蓝色代表绘制:即绘制view的时间。如果在View.onDraw()方法耗费了太多时间,蓝条就会变得更长
- 紫色代表准备:代表将要显示在屏幕上的资源准备好,并转移到渲染线程所花费的时间
- 红色代表处理:处理OpenGL操作所花费的时间
- 橙色代表执行:Gpu负载过重,他会变得更长。
二. 代码逻辑优化
1) 将耗时操作放在用户不注意的位置
比如本文第一章节,渲染优化,移除背景中将的,将Activity的布局背景移动到窗口的DecorView中,造成提前加载的假象。
笔者之间开发过一个相册类应用,中间存在一个A页面跳转到B页面的情况,B页面加载当时非常耗时,老久才能显示文件夹及其封面图(这个文件夹下所有图片的图集的第一张图片)。其实这些数据已经在A页面遍历手机获取了,另外在B页面再行获取实在不恰当,所以A页面中处理自己逻辑的时候,顺带也给B页面的数据处理了。这样B页面第一次打开后的速度就非常快,显示也很快。
2)注意循环
循环过程中,会遍历大量数据,因此循环体的内容应该非常谨慎。如果牵涉到双层循环则更需要注意。
比如自定义控件的onDraw方法,会执行多次,所以这样的循环内部都不能执行分配对象等操作,否则内存和时间问题则是分分钟都会出现。
能不用双层循环的就不用双层循环。
在kotlin代码中,filter内部也是有一个循环的,因此也需要加以注意,比如如下的log输出:
LogUtils.d("vvvv", people.filter { it.age == people.maxBy { person -> person.age }!!.age })
- filter内部其实是有一个循环在进行着,以上写法可能导致性能问题:
推荐:
val maxAge=people.maxBy(Person::age)!!.age
people.filter{it.age==maxAge}
最终代码:
val people = listOf(Person("Alice", 29), Person("Bob", 31),Person("Job", 31))
val maxAge=people.maxBy(Person::age)!!.age
LogUtils.d("vvvv", people.filter{it.age==maxAge})
参考文献:
《Android 高性能变编程》【西班牙】Enrique López Mañas(恩里克·洛佩斯·马尼亚斯),【意】Diego Grancini (迪戈·格兰奇尼)著叶坤 译
《Kotlin实战》【俄】Dmitry Jemerov Svetlana Isakova 著 覃宇 罗丽 李思阳 蒋扬海 译
《重构改善既有代码设计》【美】马丁福勒(MartinFowler)著 熊节 林从羽译
Android app性能调优
安卓包的优化
内存占用优化
app渲染优化
掌握多线程使用