Android App卡顿分析,以及使用Choreographer进行帧率统计监测

1、背景:

卡顿是最影响App用户体验的原因之一。卡顿造成的原因多种多样,简单列举一下

1、布局层级过多,设置无用的背景色,布局中添加了多种不必要的背景色,导致view绘制的时候多次绘制,引起卡顿
2、布局太复杂,嵌套过多,例如RelativeLayout 相比LinearLayout,可以有效减少布局层级,层级太复杂,会影响view的measure时间,进而造成卡顿。
3、内存使用不当,主线程做了大量计算或者IO操作等,这样就不举例说明了。

2、性能问题常用检测方法

检测app绘制性能的方法多种多样:

如针对对于布局层级过多,可以使用开发者选项-->调用GPU过度绘制 方式查看。

针对布局复杂,可以用Hierarchy Viewer

也可以使用腾讯提供的性能检测工具:GT(GitHub地址:https://github.com/FanGuangcheng/GT)

然而,这所有的检测方法都是开发阶段进行的,当我们的app投放到市场之后,我们不知道用户那边使用的真实场景是怎样的。

我们开发、测试进行的性能检测,都是针对我们的开发机器,没法真实模拟用户场景,比如用户多种多样的机型、内存大小、app使用状态、SD卡使用状态等等。
因此我们需要一种线上的监测机制,实时统计用户的卡顿数据,获得用户使用的第一手资料,并有针对性的对某些功能页面进行优化,更好的提升用户体验。

3、android绘制原理

** Android 16ms原理**:Android系统每隔16ms会发出VSYNC信号重绘我们的界面(Activity).为什么是16ms, 因为Android设定的刷新率是60FPS(Frame Per Second), 也就是每秒60帧的刷新率, 约合16ms刷新一次.
如下图所示:

《Android App卡顿分析,以及使用Choreographer进行帧率统计监测》 16ms

如图中所示,每隔16ms,系统都会发出VSYNC信号,如果这时候我们的画面(view)准备好了,我们的view绘制就会很流畅。如果我们在这个16ms间隔内,没有准备好画面(view),那么这一次绘制,就不会展示在屏幕上,就相当于少绘制了一帧,画面就会出现卡顿,断断续续。 如下图所示

《Android App卡顿分析,以及使用Choreographer进行帧率统计监测》 卡顿的原因

4、Choreographer 监测掉帧原理

在Choreographer中有个回调接口,FrameCallback。

public interface FrameCallback { public void doFrame(long frameTimeNanos); }
doFrame 的注释如下:* Called when a new display frame is being rendered.
就是说,当新的一帧被绘制的时候被调用。
因此我们利用这个特性,可以统计两帧绘制的时间间隔。
主要流程如下:

  • 1、实现Choreographer.FrameCallback接口,比如实现类是FrameSkipMonitor

  • 2、在doFrame中统计两帧绘制的时间,代码与注释如下:
    @Override
    public void doFrame(long frameTimeNanos) {
    if (mLastFrameNanoTime != 0) {//mLastFrameNanoTime 上一次绘制的时间
    long frameInterval = frameTimeNanos – mLastFrameNanoTime;//计算两帧的时间间隔
    //如果时间间隔大于最小时间间隔,316ms,小于最大的时间间隔,6016ms,就认为是掉帧,累加统计该时间
    //此处解释一下原因: 因为正常情况下,两帧的间隔都是在16ms以内 ,如果我们统计到的两帧间隔时间大于三倍的普通绘制时间,我们就认为是出现了卡顿,之所以设置最大时间间隔,是为了有时候页面不刷新绘制的时候,不做统计处理
    if (frameInterval > MIN_FRAME_TIME && frameInterval < MAX_FRAME_TIME) {
    long time = 0;
    if (mSkipRecordMap.containsKey(mActivityName)) {
    time = mSkipRecordMap.get(mActivityName);
    }
    mSkipRecordMap.put(mActivityName, time + frameInterval);//统计时间
    }
    }
    mLastFrameNanoTime = frameTimeNanos;
    Choreographer.getInstance().postFrameCallback(this);
    Runtime.getRuntime().maxMemory();
    }

  • 3、启动监测
    public void start() {
    Choreographer.getInstance().postFrameCallback(FrameSkipMonitor.getInstance());
    }

  • 4、上报处理
    在doFrame选中,我们已经统计到了想要的数据,然后我们将time的值除以16600000(因为我们统计的时间是纳秒),就是掉帧的数字。

demo源码地址:https://github.com/FanGuangcheng/FrameSkipMonitor(github代码由于涉及公司部分源码已删除)
新链接地址,大家感兴趣可以下载demo看看:
链接: https://pan.baidu.com/s/1hrG2sDY 密码: u9gp
源码简要解读:
最重要的两个类
FrameSkipMonitor:统计绘制间隔的时间处理操作。
MyActivityLifeCycle:activity相关的跳转纪录操作,通过纪录,就可以统计每个页面的掉帧卡顿时间。

参考资料:

1、http://www.jianshu.com/p/1fb065c806e6
2 、https://mp.weixin.qq.com/s?__biz=MzAxMzYyNDkyNA==&mid=2651332439&idx=1&sn=ba542ffeb494d827b9009d4e2128ed5c&scene=1&srcid=0818mFLqkkIMoS7vzjxTOEq2&key=8dcebf9e179c9f3a440c75502cc03ba23f09db161f5a3a4bd15acbdaa6b2ac0e641552d3b40a09b753739924009c9bfb&ascene=0&uin=Mjc3OTU3Nzk1&devicetype=iMac+MacBookPro10%2C1+OSX+OSX+10.10.5+build%2814F1909%29&version=11020201&pass_ticket=jzkG1H7kXvY27npmMnFUaa1eMShQT4c0PYd9r8wDanabk59MHU0ynkDd6oxen8jH
    原文作者:梦飞成2012
    原文地址: https://www.jianshu.com/p/6a680186b95f
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞