一、问题描述

今天调试停车场控制板,启动的画面有时候会卡顿一下,但是并没有触发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连接也需要线程进行操作。