出现 Choreographer: Skipped xxx frames! 的原因及解决办法
一、问题描述
今天调试停车场控制板,启动的画面有时候会卡顿一下,但是并没有触发ANR错误的程序。仔细研判log发现一条可疑的警告:"Skipped xx frames! The application may be doing too much work on its main thread"
。依照笔者x年的Android开发经验看,卡顿的原因应该隐藏在这条信息背后。
二、出现的原因
scheduleVsync 表示要接受下一次 vSync 信号,等到 vSync 信号到来时会由 SurfaceFlinger 回调通知。直接来看 Choreographer 接受到 vSync 信号后的处理,关键代码如下:
void doFrame(long frameTimeNanos, int frame) {
final long startNanos;
synchronized (mLock) {
long intendedFrameTimeNanos = frameTimeNanos;
startNanos = System.nanoTime();
final long jitterNanos = startNanos - frameTimeNanos;
if (jitterNanos >= mFrameIntervalNanos) {
final long skippedFrames = jitterNanos / mFrameIntervalNanos;
if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {
Log.i(TAG, "Skipped " + skippedFrames + " frames! "
+ "The application may be doing too much work on its main thread.");
}
}
}
...
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
}
当要绘制的图像时间戳晚于一个帧刷新周期时,会去进一步计算异常跳过的帧数,如果跳过的帧数过大,就可以看到非常眼熟的一条日志了:"Skipped xx frames! The application may be doing too much work on its main thread"
随后通过 doCallbacks 回调触发执行 UI 绘制,也就是执行 ViewRootImpl 传过来的 TraversalRunnable、调用 performTraversals 方法,由顶而下的执行界面绘制逻辑。
三、解决办法
- 分析执行的代码块,所有的IO、DB读写都用线程异步完成,使用Handler等异步通知完成逻辑。
- 网络IO中的socket连接也需要线程进行操作。
本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。