造成性能差异原因分析:
- 国内大部分用户用的 Android 手机系是各大厂商定制过的版本,往往不是最新的原生系统内核,可能绝大多数还停留在 Android 5.0 系统上,甚至 Android 6.0 以上所占比例还偏小,更新存在延迟性。
- 由于 Android 系统源码是开放的,那么国内各个厂商就把基于 Android 源码改造成自己对外发布的系统,这里面就会引发一个问题,那就是著名的Android 碎片化问题,本质就是不同 Android 系统的应用兼容性不同,达不到一致性。
一个性能好的app:
- 流畅——避免出现卡顿,响应速度快,减少用户等待的时间,满足用户期望。
- 稳定——内存泄漏,减低 crash 率和 ANR 率,不要在用户使用过程中崩溃和无响应
- 省电、省流量——代码质量、逻辑问题
- 省空间——安装包小
流畅优化
常见的卡顿场景如下:
这4种卡顿场景的根本原因可以分为两大类:
- 界面绘制。主要原因是绘制的层级深、页面复杂、刷新不合理,由于这些原因导致卡顿的场景更多出现在 UI 和启动后的初始界面以及跳转到页面的绘制上。
- 数据处理。导致这种卡顿场景的原因是数据处理量太大,一般分为三种情况
- 数据处理 在UI 线程
- 数据处理占用 CPU 高,导致主线程拿不到时间片
- 内存增加导致 GC 频繁,从而引起卡顿
引起卡顿的原因很多,但不管怎么样的原因和场景,最终都是通过设备屏幕上显示来达到用户,归根到底就是显示有问题。
卡顿根本原因
根据Android 系统显示原理可以看到,影响绘制的根本原因有以下两个方面:
- 绘制任务太重,绘制一帧内容耗时太长。
- 主线程太忙,根据系统传递过来的 VSYNC 信号来时还没准备好数据导致丢帧。
绘制耗时太长,有一些工具可以帮助我们定位问题。主线程太忙则需要注意了,主线程关键职责是处理用户交互,在屏幕上绘制像素,并进行加载显示相关的数据,所以特别需要避免任何主线程的事情,这样应用程序才能保持对用户操作的即时响应。总结起来,主线程主要做以下几个方面工作:
- UI 生命周期控制
- 系统事件处理
- 消息处理
- 界面布局
- 界面绘制
- 界面刷新
除此之外,应该尽量避免将其他处理放在主线程中,特别是复杂的数据计算和网络请求等。
布局优化
布局是否合理主要影响的是页面测量时间的多少,我们知道一个页面的显示测量和绘制过程都是通过递归来完成的,多叉树遍历的时间与树的高度h有关,其时间复杂度 O(h),如果层级太深,每增加一层则会增加更多的页面显示时间,所以布局的合理性就显得很重要。
减少层级、减少测量和绘制时间、提高复用性:
- 减少层级。合理的多使用Merge、include等标签,十层布局就会直接异常。
- 提高显示速度。使用 ViewStub,它是一个看不见的、不占布局位置、占用资源非常小的视图对象。尽量使用GONE替换INVISIBLE。
- 布局复用。可以通过标签来提高复用。
- 尽可能少用wrap_content。wrap_content 会增加布局 measure 时计算成本,在已知宽高为固定值时,不用wrap_content 。使用weight后尽量将width和heigh设置为0dp减少运算。
避免过度绘制
过度绘制是指在屏幕上的某个像素在同一帧的时间内被绘制了多次。在多层次重叠的 UI 结构中,如果不可见的 UI 也在做绘制的操作,就会导致某些像素区域被绘制了多次,从而浪费了多余的 CPU 以及 GPU 资源。
如何避免过度绘制呢,如下:
- 移除 XML 中非必须的背景,移除 Window 默认的背景、按需显示占位背景图片
- 使用 canvas.clipRect()来帮助系统识别那些可见的区域,只有在这个区域内才会被绘制。
启动优化
优化启动逻辑,提高应用的启动速度。启动主要完成三件事:UI 布局、绘制和数据准备。因此启动速度优化就是需要优化这三个过程:
- UI 布局。应用一般都有闪屏页,优化闪屏页的 UI 布局,可以通过 Profile GPU Rendering 检测丢帧情况。
- 启动加载逻辑优化。可以采用分布加载、异步加载、延期加载策略来提高应用启动速度。
- 数据准备。数据初始化分析,加载数据可以考虑用线程初始化等策略。
合理的刷新机制
在应用开发过程中,因为数据的变化,需要刷新页面来展示新的数据,但频繁刷新会增加资源开销,并且可能导致卡顿发生,因此,需要一个合理的刷新机制来提高整体的 UI 流畅度。合理的刷新需要注意以下几点:
- 尽量减少刷新次数。
- 尽量避免后台有高的 CPU 线程运行。
- 缩小刷新区域。
在实现动画效果时,需要根据不同场景选择合适的动画框架来实现。有些情况下,可以用硬件加速方式来提供流畅度。