摘要:描述Android基于Hook机制检测并统计应用FPS的一种方法,通过该方法能够量化应用“不流畅”、“卡”等问题描述。
1.问题
描述Android基于Hook机制检测并统计应用FPS的一种方法,通过该方法能够量化应用“不流畅”、“卡”等问题描述。
2.原理
每个Android应用都存在一个主线程,每个主线程关联了一个Looper对象。Looper中存在一个消息队列,用来处理主线程消息,如果无法及时处理主线程消息,那么可以认为“卡”(UI界面处理一般在主线程中)。
每个线程存在一个Choreographer,用来同步显示,通过主线程的Choreographer处理耗时便可以大致计算出当前应用的FPS。
2.1 Looper.loop等待消息
当应用启动后,会进入Looper.loop()一直等待消息,当消息到来将通过消息目标Message.target对象处理消息,Message.target类型为Handler。
2.2 Handler.dispatchMessage处理消息
|
|
具体处理是在子类中实现,我们需要跟踪的是Choreographer消息处理。
2.3 FrameHandler帧同步消息处理
子类android.view.Choreographer$FrameHandler继承Handler用来处理帧同步消息:
因此计算出doFrame的耗时就能计算出当前的大致帧率。
3. doFrame与帧率
首先直观了解下doFrame耗时与主线程加载数据之间的关系,以今日头条为例:
“位置1”:今日头条首先加载欢迎界面做广告,加载数据等原因导致耗时比较长。
“位置2”、“位置3”、“位置4”:加载主界面时,仍然有很长的耗时。
“位置5”:当主线程数据加载完成,耗时恢复正常。
现编写ListView测试,将5000条数据添加到,每次获取数据都直接去数据库查询,结果如下:
“位置6”:Activity刚创建完需要显示时数据加载时的帧率,已经达到了2000ms的耗时,测试10000条数据时,耗时为12000ms,此时界面会黑屏一段时间才显示。
“位置7”:为加载完后帧率正常情况。
“位置8”:当尝试滑动ListView时,由于获取数据时间过长,表现上已经明显卡顿,此时看到耗时已经几百毫秒,如果按帧率计算不到10帧。
4. Hook实现简单描述
经过以上分析,我们可以首先hook掉Handler.dispatchMessage函数,然后对Message.target发送目标进行判断,如果为android.view.Choreographer$FrameHandler,那么就可以记录并统计出耗时。
5. 总结
这里简单描述了主线程中Looper、Choreographer之间的关系以及卡顿的原因,接下来将描述通过Hook方式检测FPS的具体实现。