文章目录
1.卡顿介绍及优化工具的选择
1.1背景介绍
很多性能问题不易被发现,但是卡顿却很容易被直观感受,另外一方面,对开发者来说,卡顿的问题的排查和定位有一定的难度。因为卡死产生的原因是错综复杂的,比如代码问题,内存环境、绘制过程,IO操作等等情形都有可能导致卡顿,尤其是线上的卡顿问题,受限于用户具体的运行环境,在线下难以复现,所以最好是能记录卡顿产生时的环境。
1.2工具介绍
Profiler介绍
-
图形的形式展示执行的时间、调用栈等信息
-
信息全面、包含所有线程
-
运行时开销严重,应用整体变慢(可能带偏优化方向)
使用方式
- Debug.startMethodTracing();
- Debug.stopMethodTracing();
- 生成文件在sd卡:Android/data/packagename/files
Systrace介绍
结合Android内核的数据,生成Html报告
API18以上使用,推荐TraceCompat向下兼容
使用方式
-
TraceCompat.beginSection(“xxx”);// 手动埋点起始点
-
TraceCompat.endSection();// 手动埋点结束点
-
python systrace.py -b 32768 -t 5 -a packageName -o trace.html sched gfx view wm am app
优势:
-
轻量级,开销小
-
直观反映cpu利用率
-
根据问题给出建议(比如绘制慢或GC频繁)
Systrace具体使用不是本文重点,有兴趣的可参考: Android应用开发性能优化完全分析
StrictMode介绍
严苛模式,Android提供的一种运行时检测机制,可以用来帮助检测代码中一些不规范的问题,项目中有成千上万行代码,如果通过肉眼对代码进行review,这样不但效率低下,而且还容易遗漏问题。使用StrictMode之后,系统会自动检测出来主线程当中一些违例的情况,同时按照配置给出相应的反应。它主要用来检测两大问题,一个是线程策略,另一个是虚拟机策略,StrictMode方便强大,但是容易因为不被熟悉而忽视。
线程策略的检测内容主要包括一下几个方面
-
自定义的耗时调用,detectCustomSlowCalls()
-
磁盘读取写入操作,detectDiskReads
-
网络操作,detectNetwork
虚拟机策略检测内容主要包括以下几个方面
- Activity泄露,detectActivityLeaks()
- 未关闭的Closable对象泄漏,detectLeakedClosableObjects()
- Sqlite对象泄露,detectLeadedSqlLiteObjects
- 检测某个具体实例数量,setClassInstanceLimit()
StrictMode一般用于线下检测,可以在应用的Application、Activity或者其他应用组件的onCreate方法中加入检测代码
if (BuildConfig.DEBUG) {
//线程策略检测
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectCustomSlowCalls() //API等级11,使用StrictMode.noteSlowCode
.detectDiskReads()
.detectDiskWrites()
.detectNetwork()// or .detectAll() for all detectable problems
.penaltyLog() //在Logcat 中打印违规异常信息
.build());
//虚拟机策略检测
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects()
.setClassInstanceLimit(UserBean.class, 1)
.detectLeakedClosableObjects() //API等级11
.penaltyLog()
.build());
}
可以通过配置peanltyLog(),在Logcat 中打印违规异常信息,或者penaltyDialog(),通过Dialog的方式提示违规异常信息。
2.自动化卡顿检测方案原理
前面简单介绍了Profiler、Systrace系统工具,但是系统工具比较适合线下问题针对性分析,但是卡顿问题和实际使用的场景紧密结合,因此线上环境及测试环节需要自动化检测方案,来帮我们定位卡顿,更重要的是记录卡顿发生时的场景。
2.1自动化卡顿监测原理
它的原理基于Android的消息处理机制,一个线程无论有多少个Handler,都只有一个Looper,主线程中执行的任何代码,都会通过Looper.loop()分发执行,而Looper中有一个mLogging对象,它在每个message处理前后都会被调用,如果主线程发生了卡顿,一定是在dispatchMessage中执行了耗时操作,因此可以通过mLogging对dispatchMessage执行的时间进行监控。
public static void loop() {
...
for (;;) {
Message msg = queue.next(); // might block
if (msg == null) {
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
final Printer logging = me.mLogging;
if (logging != null) {
logging.