卡顿分析和优化
我们都知道手机目前最合适的显示60fps, 也就是每秒刷新60帧,这也是也是绝大部分Android设备设置的调试频率。那么绘制1帧的时间大概是16.6ms左右。一般情况下只要在16ms内完成界面的刷新就可以展示出流畅的画面. 我们知道Android系统每隔16ms就会发出VSYNC信号,来触发UI的绘制。如果在这个16ms内无法完成本地刷新操作,就会出现掉帧现象,刷新帧率也就下降,这样用户就会明显感到卡顿。
一、导致页面卡顿原因
1、UI线程中耗时操作,阻塞主线程
- UI 线程有IO读写,数据库访问等耗时操作,导致VSYNC信号到的时候,无法完成当前一帧绘制。
2、复杂,嵌套布局以及过度绘制OverDraw
不合理的布局虽然可以完成功能,但随着控件数量越多、布局嵌套层次越深,展开布局花费的时间几乎是线性增长,性能也就越差;
避免OverDraw导致的性能损耗;
3、自定义View中的onDraw,重复创建额外对象
- 避免代码的循环中 new 对象, 或是在 onDraw 中创建对象,因为每重绘一次就需要创建大量的额外对象。
4、内存使用异常导致的卡顿(频繁的GC)
- 内存抖动,内存泄漏都会导致; 内存泄漏导致内存占用过高,分配新的内存时候不足,就会频繁触发GC,由于GC是有停顿的时间,也就是常说的”Stop the world”,需要取当前内存快照分析,GC次数越多,GC消耗时间就越长,相应的剩余绘制的时间就越短。
- 瞬间产生大量的对象会严重占用 Young Generation 的内存区域, 当达到阀值, 剩余空间不够的时候, 也会触发 GC。即使每次分配的对象需要占用很少的内存,但是他们叠加在一起会增加 Heap 的压力, 从而触发更多的 GC。
5、错误的异步方式
- 对线程开启方式的不同选择以及不同配置都可能导致卡顿的发生
二、如何排查卡顿
1、使用StrictMode严格模式
StrictMode类是Android 2.3 (API 9)引入的一个工具类,可以用来帮助开发者发现代码中的一些不规范的问题,以达到提升应用响应能力的目的。可以设置不同的线程检测策略、虚拟机检测策略。
public void onCreate() {
if (DEVELOPER_MODE) {
//线程检测策略
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork() // or .detectAll() for all detectable problems
.penaltyLog()
.build());
//虚拟机检测策略
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects()
.detectLeakedClosableObjects()
.penaltyLog()
.penaltyDeath()
.build());
}
super.onCreate();
}
当检测到违规操作发生时,可以根据自定义的策略记录下Log或者Crash,以便于跟踪改善
2、Profile中的TraceView
与StrictMode相比,TraceView不仅能看出每个方法调用说花的时间、调用的次数、并且可以进行排序,可以直接从耗时最长的方法开始排查和优化。虽然我们在开发阶段可以使用BlockCanary和ANRWatchDog找耗时操作,简单明了,但是无法得到每一个方法的执行时间以及更详细的对比信息。
上部分左边是具体线程,右侧发生的时间轴,下部分左侧是具体调用的方法,右侧是第一列是花费总时间单位微秒,children是内部其他子方法调用的总时间,total - children差值就是当前方法花的时间。
- 部分数据库及IO的操作发生在首屏Activity主线程;
- Application中创建了线程池;
- 首屏Activity网络请求密集;
- 工作线程使用未设置优先级;
- 信息未缓存,重复获取同样信息;
- 流程问题:例如闪屏图每次下载,当次使用;