介绍

卡顿优化方向

耗时操作:异步、延迟
布局优化:异步Inflate、X2C、重绘解决
内存:降低内存占用,减少GC时间

卡顿监控指标

卡顿率、ANR率、秒开率
交互时间、生命周期
上报环境、场景信息

工具

cpu profiler

systruce

方便看出来CPU使用情况,开销小

TruceView

方便看出来每个线程在特定时间在做什么,开销大容易带偏优化方向

StrictMode

严苛模式,Android提供的一种运行时检测机制
方便强大,容易被忽视
包含:线程策略和虚拟机策略检查
线程策略
自定义的耗时调用,detectCustomSlowCalls()
磁盘读取操作,detectDiskReads
网络操作,detectNetwork
虚拟机策略
Activity泄漏,detectActivityLeaks
Sqlite对象泄漏,detectLeakedSqlLiteObjects
检测实例数量,setClassInstanceLimit()

AndroidPerformanceMonitor

github
介绍:非侵入式的性能监控组件,通知形式弹出卡顿信息,方便精确定位
缺点:
确实卡顿了,但卡顿堆栈可能不准确
和OOM一样,最后的堆栈只是表象,不是真正问题
优化方向:获取监控周期内的多个堆栈,而不仅仅是最后一个

ANR-WatchDog

github
非侵入式的监控ANR组件

Lancet

github
轻量级android aop框架
编译速度快,支持增量编译
API简单没有任何多余代码插入APK

@proxy 通常用于对系统API调用的hook
@insert 常用于操作app与library的类

自动化卡顿检测方案及优化

为什么需要自动化检测方案

系统工具适合线下针对性分析
线上及测试环境需要一套自动化检测方案

方案原理

消息处理机制,一个线程只有一个Looper
mLogging对象在每一个message处理前后被调用
主线程发生卡顿,是在dispatchMessage执行耗时操作

具体实现

Looper.getMainLooper().setMessageLogging();
匹配>>>>>Dispatching,阀值时间后执行任务(获取堆栈)
匹配<<<<<Finished,任务启动之前取消掉

AndroidPerformanceMonitor

github
介绍:非侵入式的性能监控组件,通知形式弹出卡顿信息,方便精确定位
缺点:
确实卡顿了,但卡顿堆栈可能不准确
和OOM一样,最后的堆栈只是表象,不是真正问题
优化方向:获取监控周期内的多个堆栈,而不仅仅是最后一个

海量卡顿堆栈处理

高频卡顿上报量太大,服务端有压力
分析:一个卡顿下多个堆栈大概率有重复
解决:对一个卡顿下堆栈进行hash排重,找出重复的堆栈
效果:极大的减少展示量,同时更高效的找到卡顿堆栈

ANR分析

触发分类

KeyDispatchTimeout(按键或触摸事件),5s
BroadcastTimeout,前台10s,后台60s
ServiceTimeout,前台20s,后台200s

ANR执行流程

发生ANR
进程接收异常终止信号,开始写入RNR信息
弹出ANR提示框

ANR解决套路

adb pull data/anr/traces.txt
详细分析:CPU、IO、锁

线上ANR监控方案

通过FileOberver监控文件变化,高版本权限问题

ANR-WatchDog

github
非侵入式的监控ANR组件

原理
子线程 使用handler向主线程发送一条执行数量+1操作的Message,子线程休眠指定时间后,检查数值是否执行过+1操作,如果+1表示未ANR,如果未+1表示已经ANR

监听ANR异常信息

  1. new ANRWatchDog().setANRListener(new ANRWatchDog.ANRListener() {
  2. @Override
  3. public void onAppNotResponding(ANRError error) {
  4. // Handle the error. For example, log it to HockeyApp:
  5. ExceptionHandler.saveException(error, new CrashManager());
  6. }
  7. }).start();

单点问题监测方案

背景介绍

自动化卡顿监测方案并不够
体系化解决方案务必尽早的暴露问题
单点问题:主线程IPC、DB

IPC问题监测

IPC调用类型
调用耗时、次数
调用堆栈、线程

常规方案

IPC前后加埋点
不优雅、容易忘记
维护成本大

IPC问题监测技巧

adb命令:
adb shell am trace-ipc start
adb shell am trace-ipc stop —dump-file /data/local/tmp/ipc-trace.txt
adb pull /data/local/tmp/ipc-trace.txt

优雅方案
ARTHook 还是 AspectJ ?
ARTHook:可以hook 系统级方法
AspectJ:非系统方法

卡顿问题监测方案

利用ARTHook完善线下工具
开发阶段Hook相关操作,暴露、分析问题

监控纬度

IPC
IO、DB
View 绘制

如何实现界面秒开

界面秒开就是一个小的启动优化
可以借鉴启动优化及布局优化章节

实现

Syctrace,优雅异步+优雅延迟初始化
异步Inflate、X2C、绘制优化
提前获取页面数据

界面秒开监控纬度

总体耗时
生命周期耗时
生命周期间隔耗时

页面秒开率统计

onCreate 到 onWindowFocusChanged 为耗时
使用Lance AOP库, 切入onCreate方法获取开始创建时间,切入onWindowFocusChanged获取第一帧显示时间,相减获取到页面创建耗时

  1. public class ActivityHooker {
  2. private static ActivityRecord mActivityRecord;
  3. static {
  4. mActivityRecord=new ActivityRecord();
  5. }
  6. @TargetClass(value = "android.support.v7.app.AppCompatActivity", scope = Scope.LEAF)
  7. @Insert(value = "onCreate", mayCreateSuper = true)
  8. protected void onCreate(){
  9. mActivityRecord.mOnCreateTime=System.currentTimeMillis();
  10. Origin.callVoid();
  11. }
  12. @TargetClass(value = "android.support.v7.app.AppCompatActivity", scope = Scope.LEAF)
  13. @Insert(value = "onWindowFocusChanged", mayCreateSuper = true)
  14. public void onWindowFocusChanged(){
  15. mActivityRecord.mOnWindowFocusChangedTime=System.currentTimeMillis();
  16. Log.d("codytest","onWindowFocusChanged cost:"+(mActivityRecord.mOnWindowFocusChangedTime-mActivityRecord.mOnCreateTime));
  17. Origin.callVoid();
  18. }
  19. }

耗时盲区监控

线下
TraceView
特别适合一段时间的盲区监控
线程具体时间做了什么,一目了然

线上
思考分析
所有方法都是Msg,mLogging?没有Msg具体堆栈
AOP切Handler方法?不清楚准确执行时间
解决方案:
使用统一的Handler:定制具体方法
定制gradler插件,编译期动态替换Handler

总结:
卡顿监控重要的一环,全面性保障
TraceView试用于线下,可以监控系统Msg
动态替换适合线上,只有应用自身的Msg

模拟问题

你是怎么做卡顿优化的

第一阶段:系统工具定位、解决
第二阶段:自动化卡顿方案及优化
第三阶段:线上监控及线下监测工具建设

怎么自动化的获取卡顿信息

mLogging.println
高频采集,找出重复堆栈

卡顿的一整套方案是怎么做的

线下(线下侧重尽早的发现暴露问题)和线上(线上侧重监控的全面性和自动化和异常感知的灵敏度)相结合
特定难题突破:重点问题、盲区监控
线上监控建设