Android性能优化介绍
性能优化,顾名思义就是对软件的运行性能进行优化。在Android开发中,性能优化可以是对应用耗电的优化、可以是对应用启动速度的优化、也可以是对应用流畅度的优化,等等。这里介绍性能优化的原因、概念以及常用的性能优化工具和做法。
为什么要做性能优化
- 性能优化带来体验的改善,进而帮助业务指标的提升
- 对应用性能优化,比如更加流畅顺滑/更省电,就可能提升用户量
- 缓解机器性能瓶颈问题
- 随着机器性能提升速度的放缓,性能优化可以在相同性能下带来更好的体验
- 缓解移动端电池技术瓶颈
- 用有限的电量做更多的事/获得更好的体验
- ...
什么是性能优化
性能优化的目标主要有三个:快、稳、省。快,就是提升应用速度,比如启动速度、响应速度等,从而让体验更流畅;稳,就是提升应用的稳定性,减少各种原因的crash、无响应;省,就是减少各种资源的使用,比如电池、网络流量等,以期用最小的负载获得最大的收益。下面具体介绍性能优化的分类。
流畅性优化
对于Android应用,每个应用进程都有一个Main Thread(主线程),这个线程负责UI绘制、系统事件处理等任务。主线程的UI绘制会每隔一段时间(这个时间由VSync信号控制,每当收到VSync信号时就应当进行一次绘制)执行一次从而实现屏幕刷新(在60Hz刷新率下,大约每16ms执行一次UI Drawing)。如果应用出现了卡顿现象,就说明UI绘制没能正常执行,一般有下面几种情况:
- 在主线程中放入了较重的业务逻辑,导致主线程忙于执行这部分代码而没能按时进行UI绘制,在业务逻辑执行完成后再进行UI绘制,造成卡顿的现象(等待一段时间后屏幕才响应,出现“划不动”的情况)
- 主线程忙于处理输入事件(或者其他任务)而未能进行UI绘制,导致丢帧(画面跳跃不连贯)
- ...
因此为了保证UI绘制不被各种原因打断、延迟,最好将耗时的业务代码/输入事件放在其他线程,让主线程专注于UI绘制任务。
前面提到UI绘制是由VSync信号控制的,每次收到VSync信号时主线程就应当刷新一次屏幕(进行UI绘制)。如果没有VSync信号,而是由CPU自行决定绘制时机,就可能导致画面撕裂问题(CPU在短时间内进行了两次不同画面的绘制,上一帧尚未渲染完成就绘制下一帧,导致屏幕画面各个部分属于多个不同帧产生撕裂)
资源优化
这里的资源指的是Android设备上的各种软件和硬件资源,比如存储、流量、电池、内存、系统参数等。资源可以粗略分为端侧资源和服务侧资源两个方面,具体如下:
- 端侧资源
- 内存
- CPU
- GPU
- 存储
- 网络
- 亮度
- 声音
- ...
- 服务侧资源
- CDN带宽
- API流量
- ...
这方面的优化包括应用颜色选择(在OLED屏幕上黑色部分不发光,因此黑色系比其他颜色更省电)、算法优化(减轻处理器负担)、资源压缩(降低网络流量消耗)等措施。
稳定性优化
应用稳定性可以从崩溃(Crash)和超时两个方面考虑。崩溃带来的一般就是应用闪退,而超时则通常带来轻则卡顿掉帧、重则ANR(Application Not Responding,应用无响应,当主线程阻塞过长时间时会被触发)。
系统级优化
这方面主要取决于移动操作系统和硬件厂商对系统的优化,比如优化ART虚拟机、引入ZRAM技术、优化处理器调度等等。
性能监控工具
要进行性能优化,首先要找到优化点,一个普遍的做法就是利用性能监控工具找到应用的薄弱点,然后针对性的进行性能优化。除了指导性能优化方向外,性能监控工具还能帮助发现服务提供商的劣化问题(比如发现厂商对应用进行了负优化),及时止损。下面介绍一些常用的性能监控工具。
- GPU呈现模式
- 系统集成的工具,无需二次开发。系统记录每一帧的数据并通过图形方式呈现
- Layertool
- 输出View层级关系,帮助分析overdraw问题
- CPU Profiler
- 基于JVMTI输出完整的方法调用栈,也支持方法的耗时检测
- TraceView
- Systrace
- Rhea
- 字节跳动自研工具,性能损耗小
- Battery Historian
- 应用功耗统计
如何进行性能优化
要对应用进行性能优化,要先进行现状分析,然后根据分析结果制定优化策略。
现状分析
现状分析是为了找出性能优化点,一般可以从耗时成因、运行环境、渲染分析、等方面进行。
- 耗时成因
- CPU Time
- 循环、反射、序列化/反序列化、类解析等
- IO Wait
- IO操作、等待IO操作返回结果
- IPC
- 进程间通信,比如Binder调用
- Lock Wait
- 主线程在等锁,导致超时无响应
- CPU Schedule
- CPU调度策略有问题导致主线程获取不到CPU资源
- CPU Time
- 运行环境
- 根据不同的运行环境(前台/后台/是否是主线程)采用不同的策略
- 渲染分析
- 渲染耗时点
- 渲染频率
优化策略
比如采用预加载来构建UI组件、使用更高效的数据协议(比如json->protobuf)和数据解析器、异步渲染,等等。