最近写了一个开源项目叫framemonitor, 相当于SDK版的Systrace,可以监控App的帧绘制时间,接入后可以通过小圆球看到App的实时帧绘制时间,当发生严重掉帧,小圆球颜色变红,并且会保存日志到本地,以供分析。
其功能如下图,今天给大家分析卡顿定位模块,流量检测功能已完成,但还未发布依赖,将跟随2.0版本一起发布,有兴趣的童鞋可以在github fork,继续关注该项目卡顿检测原理-Choregrapher
Android的屏幕刷新是依赖Vsync信号,而Choregrapher可以通知系统产生vsync信号,也可以做为vsync信号的接收者。当Choregrapher接收到vsync信号时,会处理View的Input、Animation、traversal(view的measure、layout、draw)等操作。相关代码在choregrapher的doFrame中,详细的流程可以去了解Choregrapher的源码分析当某一帧的处理时间超过16ms,Android屏幕仍会显示上一帧,即发生了掉帧,掉帧严重时,给到用户的感觉是卡顿,不流畅。为什么是16ms?Android屏幕设备的刷新频率为60Hz,通过做一道小学计算题 1÷60≈16.67 (ms)得到16ms
通过Choregrapher的源码可以知道View的相关操作是以Callback形式来进行的,所以我们可以向Choreographer类中加入自己的Callback来冒充View的Callback,通过此Callback我们可以获得View绘制的时间、可以统计一秒内帧绘制的能力(FPS)。之所以不用FPS来检测卡顿,是因为Android系统默认在前台页面静止时,FPS可能为0,FPS低无法直接代表当前处于卡顿。
framemonitor的相关代码如下:
掉帧程度定义-依据systrace
用systrace去分析ui性能时我们总可以以看到一些连续的小圆球,上面可以看到每一帧的绘制时间,有红绿蓝三种颜色,代表帧绘制时间的长度,通过对其分析可以得到:绿色 -(<=16ms),黄色-(16-32ms),红色(>32ms),framemonitor与systrace保持一致,当小圆球颜色变红,会保存堆栈信息到本地,以供分析。
实际测试中由于机器性能差异同一份代码在不同机器上跑出来的帧绘制时间可能相差很远,所以提供IConfig接口来定义掉帧的严重程度,使帧绘制时间可以反映主观上的卡顿
堆栈收集
framemonitor调用完start方法后会开启线程每隔一定的时间去收集主线程堆栈,用一个栈去存储,当卡顿发生时,从栈中取出前几个堆栈信息保存到日志文件中,同时也会将主线程的MessageQueue中的所有Message执行情况和当时的所有线程的堆栈保存到日志文件中
悬浮窗显示(无需悬浮窗权限)
Android的页面是一个View Tree,具体结构如下图
可以发现Android系统会给我们的布局套上一个id为content的framelayout,而framelayout可以用来做图层管理,悬浮窗可以理解为最上层的View,所以通过对每个Activity在这个id为content的framelayout添加view可以实现悬浮窗,这其实也是google snackbar的原理,而要对App内的所有Activity进行操作,可通过ActivityLifecycleCallbacks接口,对于界面内的悬浮窗可以采用这种方案而不必去走申请悬浮窗权限的流程
framemonitor的悬浮窗做得比较简单,是一个不限时间的悬浮窗,没有入场动画和出场动画,后面会把这块内容做成一个sdk,其任务分解如下图,欢迎继续关注对比blockcanary
定位卡顿sdk还有blockcanary,具体对比过程可以看我另一篇博客