Choreographer初识(1)

271 阅读2分钟

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

Choreographer初识(1)

上次介绍了初始化的过程以及两个重要的类FrameHandler和FrameDisplayEventReceiver

下面我们继续进行分析

Choreographer 处理每一帧

doFrame()是处理每一帧操作的核心

这里主要做了两部分处理,一部分是掉帧计算,另一部分是处理回调。

掉帧计算

//frameTimeNanos时间戳
void doFrame(long frameTimeNanos, int frame) {
    final long startNanos;
    synchronized (mLock) {
        if (!mFrameScheduled) {
            return; // no work to do
        }
​
        if (DEBUG_JANK && mDebugPrintNextFrameTimeDelta) {
            mDebugPrintNextFrameTimeDelta = false;
            Log.d(TAG, "Frame time delta: "
                  + ((frameTimeNanos - mLastFrameTimeNanos) * 0.000001f) + " ms");
        }
​
        //计算掉帧逻辑
        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;
            }
    
    //...
}

frameTimeNanos是Vsync信号到达时的时间戳,startNanos指的是真正去处理Vsync的时间戳,这两个时间差值就是 Vsync 信号的处理时延,即掉帧时间。,根据延迟的时间间隔获取延迟周期跳过的帧数。

回调的响应

//frameTimeNanos时间戳
void doFrame(long frameTimeNanos, int frame) {
    //第二阶段
        try {
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame");
            AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);
            // 处理 CALLBACK_INPUT Callbacks响应输入
            mFrameInfo.markInputHandlingStart();
            doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
            //响应动画
            // 处理 CALLBACK_ANIMATION Callbacks
            // 处理 CALLBACK_INSETS_ANIMATION Callbacks
            mFrameInfo.markAnimationsStart();
            doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
            doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos);
            //响应绘制流程
            // 处理 CALLBACK_TRAVERSAL Callbacks
            
            mFrameInfo.markPerformTraversalsStart();
            doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
            //处理commit回调,修正最后一帧的时间戳。
            // 处理 CALLBACK_COMMIT Callbacks
            doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
        } finally {
            AnimationUtils.unlockAnimationClock();
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
}

相面分别对着几个响应回调进行分析:

Input调用栈

用户进行input交互后,doCallBacks方法内最终执行的是ConsumeBatchedInputRunnable,之后会走到DecorView.dispatchTouchEvent(),后续进行一系列touch event事件分发。

animation调用栈

动画执行时,Choreographer.doFrame() 会响应 AnimationHandler 内部的 Choreographer.FrameCallback.doFrame(),然后执行到 ValueAnimator.onAnimationUpdate(),这个方法也就是业务测实现动画逻辑的地方。

动画完成后,执行 Choreographer.scheduleVsyncLocked() 调度下一个 Vsync(“调度 Vsync” 部分会再讲到),再次执行下一帧的动画流程。

traversal 调用栈

traversal 过程主要是指 View 的 测绘,布局,绘制视图。invalidate()和requestLayout() 都会触发重绘流程。