Choreographer初识(3)

637 阅读2分钟

「这是我参与11月更文挑战的第14天,活动详情查看:2021最后一次更文挑战

上篇我们介绍到Choreographer去请求Vsync信号,本篇我们接着讲解当收到SurfaceFlinger发来的Vsync信号后是如何去处理的。

当下一个Vsync信号发出的时候SurfaceFlinger会通知我们,会Choreographer中的回调FrameDisplayEventReceiver的onVsync方法。

private final class FrameDisplayEventReceiver extends DisplayEventReceiver
        implements Runnable {
    //...
​
    @Override
    // 接受到Vsync信号的回调
    public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {
​
        long now = System.nanoTime();
        mTimestampNanos = timestampNanos;
        mFrame = frame;
        Message msg = Message.obtain(mHandler, this); //public void run()
        msg.setAsynchronous(true);
        //timestampNanos消息触发时间,时间戳去处理时间
        mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
    }
​
    //执行doFrame
    @Override
    public void run() {
        mHavePendingVsync = false;
        doFrame(mTimestampNanos, mFrame);
    }
}

timestampNanos是Vsync信号的时间戳,在onVsync方法中发了一个异步消息到工作线程中。时间戳去处理时间。到时间后会执行run方法,所以紧接着去分析doFrame方法。

//frameTimeNanos时间戳
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.");
            }
            final long lastFrameOffset = jitterNanos % mFrameIntervalNanos;
            frameTimeNanos = startNanos - lastFrameOffset;
        }
    }
    //第二阶段
}

doFrame主要分为两个阶段,现在分析第一阶段,frameTimeNanos标识这一帧的时间戳,jitterNanos计算当前时间和时间戳的时间间隔,间隔越大表示这一帧的处理已经延时了,如果延时超过一个周期,则计算超过了几个周期skippedFrames。并打印相应的日志。这部分主要就是掉帧计算。

接着看第二部分,处理callback。

void doFrame(long frameTimeNanos, int frame) {
    //...
    doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
    doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
    doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
    doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
}
​

callback有四种类型,每种类型都对应一个CallbackQueues,就是一个单链表。给Vsync分别分发到这四种callback。执行对应的doCallback函数。

void doCallbacks(int callbackType, long frameTimeNanos) {
    CallbackRecord callbacks;
    synchronized (mLock) {
        final long now = System.nanoTime();
        //取出那些到时间的callback
        callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
            now / TimeUtils.NANOS_PER_MS);
        //...
        //执行callback
        for (CallbackRecord c = callbacks; c != null; c = c.next) {
            c.run(frameTimeNanos);
        }
        
        //...
    }
}

主要工作就是取出那些到时间的callback,去执行run方法,在前面传的callback是.CALLBACK_TRAVERSAL,就是去准备回执

void scheduleTraversals() {
        mChoreographer.postCallback(
            Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
    }
}

mTraversalRunnable主要工作是去进行真正的绘制操作。

下面总结一下这个流程:

3.png