- 360 ArgusAPM类实现方式: 监测Choreographer两次Vsync时间差
- BlockCanary的实现方式:监测UI线程单条Message执行时间
依赖Choreographer VSYNC回调,具体实现如下:循环添加Choreographer.frameCallback
Choreographer.getInstance().postframeCallback(new Choreographer.frameCallback() {
@Override
public void doframe(long frameTimeNanos) {
mFpsCount++;
mframeTimeNanos = frameTimeNanos;
if (isCanWork()) {
//注册下一帧回调
Choreographer.getInstance().postframeCallback(this);
} else {
mCurrentCount = 0;
}
}
});
这种监听有个问题就是,监听过于频繁,因为在无需界面刷新的时候Choreographer.frameCallback还是不断循环执行,浪费CPU资源,对线上运行采集并不友好,相比之下BlockCanary的监听单个Message执行要友善的多,而且同样能够涵盖UI绘制耗时、两帧之间的耗时,额外执行负担较低
方案二:监控单条Message的执行时间
为Looper设置一个LooperPrinter,根据回传信息头区分消息执行开始于结束,计算Message耗时:原理如下
public static void loop() {
...
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
...
if (logging != null) {
logging.println("<<<<< Finished to " + msg.target + " "+msg.callback);
}
自定义LooperPrinter如下:
class LooperPrinter implements Printer {
@Override
public void println(String x) {
...
if (isValid) {
dispatch(x.charAt(0) == '>', x);
}
利用回调参数">>>>"与"<<<"的 区别即可诊断出Message执行耗时,从而确定是否导致掉帧。以上实现针对所有UI Message,原则上UI线程所有的消息都应该保持轻量级,任何消息超时都应当算作异常行为,所以,直接拿来做掉帧监测没特大问题的。
但是,有些特殊情况可能对FPS计算有一些误判,比如,在touch时间里往UI线程塞了很多消息,单条一般不会影响滚动,但多条聚合可能会带来影响,如果没跳消息执行时间很短,这种方式就可能统计不到,当然这种业务的写法本身就存在问题,所以先不考虑这种场景。



