一、卡顿产生的原因
卡顿产生的原因错综复杂,代码,内存,绘制,IO 等都有可能导致卡顿
线上问题不易复现,和当时的场景强相关
二、卡顿优化的工具
CPU profiler
- 图形的形式展示执行时间、调用栈等
- 信息全面,包含所有线程
- 运行时开销严重,整体都会变慢
使用方式
- Debug.startMethodTracing ("");
- Debug.stopMethodTracing();
- 生成文件在sd卡:Android/data/packagename/files
Systrace
- 监控和跟踪Api调用、线程运行情况,生成Htr l报告
- API 18以上使用,推荐TraceCompat
使用方式
- python systrace.py -t 10 [other-options] [categories]
- https://developer.android.com/studio/command-line/systrace#command_options
Systrace优点
StrictMode
- 严苛模式,Android提供的一种运行时检测机制
- 方便强大,容易被忽视
- 包含:线程策略和虚拟机策略检测
三、线程策略
- 自定义的耗时调用:detectCustomSlowcallsQ
- 磁盘读取操作:detectDiskReads
- 网络操作:detectNetwork
四、虛拟机策略
- Activity泄露,detectActivityLeaks0
- Sqlite对象泄露,detectLeakedsqlLiteobjects
- 检测实例数量,setClassinstanceLimito
五、自动化卡顿检测方案及优化
方案原理
- 消息处理机制 ,一个线程只有一个Looper
- mLogging对象在每个message处理前后被调用
- 主线程发生卡顿,是在dispatchMessage执行耗时操作
具体实现
- Looper.getMainLooper().setMessageLogging();
- 匹配>>>>>Dispatching,國值时间后执行任务(获取堆栈)
- 匹配<<<<
AndroidPerformanceMonitor
- 非侵入式的性能监控组件 ,通知形式弹出卡顿信息
- com.github.markzhai:blockcanary-android
- https://github.com/markzhai/AndroidPerformanceMonitor
方案总结
问题
- 确实卡顿了,但卡顿堆栈可能不太准确
- 和OOM 一样最后的堆栈只是表象,不是真正问题
优化
获取监控周期内的多个堆栈,而不仅是最后一个
- startMonitor
- 高频采集堆栈
- endMonitor
- 记录多个堆栈
- 上报
海量卡顿堆处理
- 高频卡顿上报量太大,服务端有压力
- 分析:一个卡顿下多个堆栈大概率有重复
- 解决:对一个卡顿下堆栈进行hash排重,找出重复的堆栈
- 效果:极大的减少展示量同时更高效找到卡顿堆栈
ANR介绍
- KeyDispatch Timeout, 5s. 按键或者触摸事件在特定的内没有完成
- BroadcastTimeout ,前台10s,后台60s
- Service Timeout,前台20s,后台200s
具体的定义在ActivityServiceManager
ANR执行流程
- 进程接收异常终止信号,开始写入进程ANR信息,当时的场景,包含了所有的堆栈信息以及CPU IO的使用情况等
- 弹出ANR提示框 (Rom表现不一)提示用户是选择关闭还是继续等待,这个提示框也是不一定会弹的,有些手机厂商会默认取消掉这个提示框,以免有个不好的用户体验
ANR解决套路
- adb pull data/anr/traces.txt 存储路径
- 详细分析:CPU、死锁
ANR 的提示框的弹出肯定要比特定的时间早就,写入信息也需要时间
举例:
wait to lock 等待一把锁
held by thread xxx 被这个线程所持有
一个线程在等一把锁,而这个锁被另一个线程持有
线上ANR监控方案
- 通过Fileoberver监控文件变化,但是高版本权限问题,监控不到这个文件的变化
ANR-WatchDog
- 描述:非侵入式的ANR监控组件
- 依赖方式:com.github.anrwatchdog:anrwatchdog:1.3.0
- 项目地址:https://github.com/SalomonBrys/ANR-WatchDog
原理
- start 这个线程就开始工作
- post 消息改值,通过主线程的handler post 一个消息,做的操作是在主线程中对一个值做一个加1的操作
- sleep post 完成之后这个线程会sleep 一段时间检测是否修改,如果被修改,证明在主线程中执行了这个Message ,也就是主线程没有发生卡顿,没有发生修改证明主线程被卡顿着判断ANR 发生,然后抛出一个异常如果每次ANR以后App 都崩溃掉了,用户体验就更差,可以自己开始实现一个ANRwatchListener 可以对ANR 事件进行一个处理,复写listener 拿到堆栈信息,上报到云端