Android 窗口显示(四)—— Choreographer VSYNC 信号回调

226 阅读7分钟

点击阅读:Android 窗口显示系列文章

1. UI 绘制的调度入口

接上文,当窗口添加到 WMS 并完成尺寸计算后,requestLayout 触发 UI 绘制流程:

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
            int userId) {
    synchronized (this) {
        if (mView == null) {
            // DecorView
            mView = view;
            mWindowAttributes.copyFrom(attrs);
            // 核心流程3
            requestLayout();
            // 核心流程1
            res = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,
                    getHostVisibility(), mDisplay.getDisplayId(), userId,
                    mInsetsController.getRequestedVisibleTypes(), inputChannel, mTempInsets,
                    mTempControls, attachedFrame, compatScale);
            // 核心流程2        
            mWindowLayout.computeFrames(mWindowAttributes, state,
                    displayCutoutSafe, winConfig.getBounds(), winConfig.getWindowingMode(),
                    UNSPECIFIED_LENGTH, UNSPECIFIED_LENGTH,
                    mInsetsController.getRequestedVisibleTypes(), 1f /* compactScale */,
                    mTmpFrames);
                    
            setFrame(mTmpFrames.frame, true /* withinRelayout */);
            
        
        }
    }
}

虽然 requestLayout 在窗口添加前调用,但由于设置了同步屏障和 VSYNC 机制,实际 UI 绘制会在窗口添加完成后执行,确保绘制时窗口已就绪。看一下 requestLayout 方法:

@Override
public void requestLayout() {
    if (!mHandlingLayoutInLayoutRequest) {// 一个标志位,防止重复调用
        checkThread();
        mLayoutRequested = true;// 标志位
        scheduleTraversals();
    }
}
@UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
void scheduleTraversals() {
    if (!mTraversalScheduled) {// 同样避免重复调用
        mTraversalScheduled = true;
        // 1. 设置同步屏障:阻塞同步消息,确保UI绘制优先
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        // 2. 通过Choreographer注册VSYNC回调
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        // 3. 通知渲染线程新帧即将开始
        notifyRendererOfFramePending();
        // 4. 请求Surface绘制锁
        pokeDrawLockIfNeeded();
    }
}

首先设置同步屏障,用于确保 UI 绘制消息(异步消息)能够优先于其他同步消息执行,从而保证绘制的及时性。

这里的关键在于 Choreographer.postCallback  方法,它将 mTraversalRunnable 与 VSYNC 信号绑定。当 VSYNC 信号到来时,系统会优先执行这个 Runnable。

2. Choreographer 对象的创建

mChoreographer 在 ViewRootImpl 构造函数中进行初始化:

mChoreographer = Choreographer.getInstance();

Choreographer 和 Looper 一样 是线程单例的:

private static final ThreadLocal<Choreographer> sThreadInstance =
        new ThreadLocal<Choreographer>() {
    @Override
    protected Choreographer initialValue() {
        Looper looper = Looper.myLooper();
        if (looper == null) {
            throw new IllegalStateException("The current thread must have a looper!");
        }
        Choreographer choreographer = new Choreographer(looper, VSYNC_SOURCE_APP);
        if (looper == Looper.getMainLooper()) {
            mMainInstance = choreographer;
        }
        return choreographer;
    }
};

几个静态变量代表回调消息的类型,优先级由低至高,如下:

// 输入事件
public static final int CALLBACK_INPUT = 0;
// 动画
public static final int CALLBACK_ANIMATION = 1;
// 插入更新的动画
public static final int CALLBACK_INSETS_ANIMATION = 2;
// 绘制
public static final int CALLBACK_TRAVERSAL = 3;
// 提交
public static final int CALLBACK_COMMIT = 4;

3. VSYNC 回调注册

接着看一下 Choreographer 的 postCallback 方法:

public void postCallback(int callbackType, Runnable action, Object token) {
    postCallbackDelayed(callbackType, action, token, 0);
}

public void postCallbackDelayed(int callbackType,
    Runnable action, Object token, long delayMillis) {
    if (action == null) {
        throw new IllegalArgumentException("action must not be null");
    }
    if (callbackType < 0 || callbackType > CALLBACK_LAST) {
        throw new IllegalArgumentException("callbackType is invalid");
    }

    postCallbackDelayedInternal(callbackType, action, token, delayMillis);
}

private void postCallbackDelayedInternal(int callbackType,
        Object action, Object token, long delayMillis) {
    if (DEBUG_FRAMES) {
        Log.d(TAG, "PostCallback: type=" + callbackType
                + ", action=" + action + ", token=" + token
                + ", delayMillis=" + delayMillis);
    }

    synchronized (mLock) {
        // 当前时间
        final long now = SystemClock.uptimeMillis();
        // 加上延迟时间
        final long dueTime = now + delayMillis;
        // 将回调添加到对应类型的队列(CALLBACK_TRAVERSAL类型)
        mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);

        if (dueTime <= now) {
            // 立即执行
            scheduleFrameLocked(now);
        } else {
            // 发送延迟消息(异步消息,可突破同步屏障)
            Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
            msg.arg1 = callbackType;
            msg.setAsynchronous(true);
            mHandler.sendMessageAtTime(msg, dueTime);
        }
    }
}
    

最终调用到 postCallbackDelayedInternal 方法,传入的第一个参数为 Choreographer.CALLBACK_TRAVERSAL,回调消息类型为 UI 绘制。第二个参数为一个 Runnable 对象。第三个参数 token 为 null。

postCallbackDelayedInternal 中取对应类型的 CallbackQueue 添加任务,也就是在 UI 绘制的 CallbackQueue 中添加了一个任务。addCallbackLocked 方法就是把 dueTime、action、token 封装成 CallbackRecord 对象后存入 CallbackQueue 的下一个节点。

发送的消息没有延迟,直接调用了 scheduleFrameLocked 方法:

private void scheduleFrameLocked(long now) {
    if (!mFrameScheduled) {
        mFrameScheduled = true;
        if (USE_VSYNC) {// 开启了 VSYNC
            //当前执行的线程,是否是mLooper所在线程
            if (isRunningOnLooperThreadLocked()) {
                //申请 VSYNC 信号
                scheduleVsyncLocked();
            } else {
                // 若不在,就用 mHandler 发送消息到原线程,最后还是调用 scheduleVsyncLocked 方法
                Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
                msg.setAsynchronous(true);
                mHandler.sendMessageAtFrontOfQueue(msg);
            }
        } else {
            // 如果未开启 VSYNC 则直接 doFrame 方法(4.1后默认开启)
            final long nextFrameTime = Math.max(
                    mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now);
            Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
            msg.setAsynchronous(true);
            mHandler.sendMessageAtTime(msg, nextFrameTime);
        }
    }
}

若当前执行的线程,是否是 mLooper 所在线程,直接调用 scheduleVsyncLocked 方法申请 VSYNC 信号。若不在,就用 mHandler 发送消息到原线程,最后还是调用 scheduleVsyncLocked 方法:

private void scheduleVsyncLocked() {
    try {
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#scheduleVsyncLocked");
        mDisplayEventReceiver.scheduleVsync();
    } finally {
        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
}

接着调用 DisplayEventReceiver 的 scheduleVsync 方法:

public void scheduleVsync() {
    if (mReceiverPtr == 0) {
        Log.w(TAG, "Attempted to schedule a vertical sync pulse but the display event "
                + "receiver has already been disposed.");
    } else {
        // 申请 VSYNC 中断信号,会回调 onVsync 方法
        nativeScheduleVsync(mReceiverPtr);
    }
}

在 DisplayEventReceiver 的构造方法会通过 JNI 创建一个 IDisplayEventConnection 的 VSYNC 的监听者:

// 注册VSYNC信号监听者
mReceiverPtr = nativeInit(new WeakReference<DisplayEventReceiver>(this),
        new WeakReference<VsyncEventData>(mVsyncEventData),
        mMessageQueue,
        vsyncSource, eventRegistration, layerHandle);

接着调用 native 方法 nativeScheduleVsync,并将 mReceiverPtr 作为参数传递。

这里我们只需要关注:申请完 VSYNC 信号后,VSYNC 信号的接收回调是 onVsync:

public void onVsync(long timestampNanos, long physicalDisplayId, int frame,
        VsyncEventData vsyncEventData) {
}

4. VSYNC 信号回调处理

看一下 FrameDisplayEventReceiver 中的实现:

private final class FrameDisplayEventReceiver extends android.view.DisplayEventReceiver
        implements Runnable {
    private boolean mHavePendingVsync;
    private long mTimestampNanos;
    private int mFrame;
    private final VsyncEventData mLastVsyncEventData = new VsyncEventData();

    FrameDisplayEventReceiver(Looper looper, int vsyncSource, long layerHandle) {
        super(looper, vsyncSource, /* eventRegistration */ 0, layerHandle);
    }
    @Override
    public void onVsync(long timestampNanos, long physicalDisplayId, int frame,
            VsyncEventData vsyncEventData) {
        try {
            long now = System.nanoTime();
            if (timestampNanos > now) {
                timestampNanos = now;
            }

            if (mHavePendingVsync) {
                Log.w(TAG, "Already have a pending vsync event.  There should only be "
                        + "one at a time.");
            } else {
                mHavePendingVsync = true;
            }

            mTimestampNanos = timestampNanos;
            mFrame = frame;
            mLastVsyncEventData.copyFrom(vsyncEventData);
            // 将本身作为 runnable 传入 msg, 发消息后会走run,异步消息
            Message msg = Message.obtain(mHandler, this);
            msg.setAsynchronous(true);
            mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
    }

    @Override
    public void run() {
        mHavePendingVsync = false;
        // onVsync 方法中只是使用 mHandler 发送消息到 MessageQueue 中,不一定是立刻执行,若 MessageQueue 中前面有较为耗时的操作,那么就要等完成,才会执行本次的 doFrame。
        doFrame(mTimestampNanos, mFrame, mLastVsyncEventData);
    }
}

接着看下 doFrame 方法:

void doFrame(long frameTimeNanos, int frame,
        DisplayEventReceiver.VsyncEventData vsyncEventData) {
    final long startNanos;
    final long frameIntervalNanos = vsyncEventData.frameInterval;
    boolean resynced = false;
    try {
        FrameTimeline timeline = mFrameData.update(frameTimeNanos, vsyncEventData);
        if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
            Trace.traceBegin(
                    Trace.TRACE_TAG_VIEW, "Choreographer#doFrame " + timeline.mVsyncId);
        }
        synchronized (mLock) {
            if (!mFrameScheduled) {
                traceMessage("Frame not scheduled");
                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();
            // 超时时间是否超过一帧的时间(这是因为 MessageQueue 虽然添加了同步屏障,但是还是有正在执行的同步任务,导致 doFrame 延迟执行了)
            final long jitterNanos = startNanos - frameTimeNanos;
            if (jitterNanos >= frameIntervalNanos) {
                // 计算掉帧数
                frameTimeNanos = startNanos;
                if (frameIntervalNanos == 0) {
                    // 掉帧超过30帧打印Log提示
                    Log.i(TAG, "Vsync data empty due to timeout");
                } else {
                    long lastFrameOffset = jitterNanos % frameIntervalNanos;
                    frameTimeNanos = frameTimeNanos - lastFrameOffset;
                    final long skippedFrames = jitterNanos / frameIntervalNanos;
                    if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {
                        Log.i(TAG, "Skipped " + skippedFrames + " frames!  "
                                + "The application may be doing too much work on its main "
                                + "thread.");
                    }
                }
                timeline = mFrameData.update(
                        frameTimeNanos, mDisplayEventReceiver, jitterNanos);
                resynced = true;
            }

            if (frameTimeNanos < mLastFrameTimeNanos) {
                if (DEBUG_JANK) {
                    Log.d(TAG, "Frame time appears to be going backwards.  May be due to a "
                            + "previously skipped frame.  Waiting for next vsync.");
                }
                traceMessage("Frame time goes backward");
                scheduleVsyncLocked();
                return;
            }

            if (mFPSDivisor > 1) {
                long timeSinceVsync = frameTimeNanos - mLastFrameTimeNanos;
                if (timeSinceVsync < (frameIntervalNanos * mFPSDivisor) && timeSinceVsync > 0) {
                    traceMessage("Frame skipped due to FPSDivisor");
                    scheduleVsyncLocked();
                    return;
                }
            }

            mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos,
                    vsyncEventData.preferredFrameTimeline().vsyncId,
                    vsyncEventData.preferredFrameTimeline().deadline, startNanos,
                    vsyncEventData.frameInterval);
            // Frame标志位恢复
            mFrameScheduled = false;
            // 记录最后一帧时间
            mLastFrameTimeNanos = frameTimeNanos;
            mLastFrameIntervalNanos = frameIntervalNanos;
            mLastVsyncEventData.copyFrom(vsyncEventData);
        }

        if (resynced && Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
            String message = String.format("Choreographer#doFrame - resynced to %d in %.1fms",
                    timeline.mVsyncId, (timeline.mDeadlineNanos - startNanos) * 0.000001f);
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, message);
        }
        // 按类型顺序 执行任务
        AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS,
                timeline.mExpectedPresentationTimeNanos);

        mFrameInfo.markInputHandlingStart();
        doCallbacks(Choreographer.CALLBACK_INPUT, frameIntervalNanos);

        mFrameInfo.markAnimationsStart();
        doCallbacks(Choreographer.CALLBACK_ANIMATION, frameIntervalNanos);
        doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameIntervalNanos);

        mFrameInfo.markPerformTraversalsStart();
        doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameIntervalNanos);

        doCallbacks(Choreographer.CALLBACK_COMMIT, frameIntervalNanos);
    } finally {
        AnimationUtils.unlockAnimationClock();
        if (resynced) {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }

    if (DEBUG_FRAMES) {
        final long endNanos = System.nanoTime();
        Log.d(TAG, "Frame " + frame + ": Finished, took "
                + (endNanos - startNanos) * 0.000001f + " ms, latency "
                + (startNanos - frameTimeNanos) * 0.000001f + " ms.");
    }
}

接着看一下 doCallbacks 方法,

void doCallbacks(int callbackType, long frameIntervalNanos) {
    CallbackRecord callbacks;
    long frameTimeNanos = mFrameData.mFrameTimeNanos;
    synchronized (mLock) {
        final long now = System.nanoTime();
        // 根据指定的类型 CallbackQueue 中查找到达执行时间的 CallbackRecord
        callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
                now / TimeUtils.NANOS_PER_MS);
        if (callbacks == null) {
            return;
        }
        mCallbacksRunning = true;

        // 提交任务类型
        if (callbackType == Choreographer.CALLBACK_COMMIT) {
            final long jitterNanos = now - frameTimeNanos;
            Trace.traceCounter(Trace.TRACE_TAG_VIEW, "jitterNanos", (int) jitterNanos);
            if (frameIntervalNanos > 0 && jitterNanos >= 2 * frameIntervalNanos) {
                final long lastFrameOffset = jitterNanos % frameIntervalNanos
                        + frameIntervalNanos;
                if (DEBUG_JANK) {
                    Log.d(TAG, "Commit callback delayed by " + (jitterNanos * 0.000001f)
                            + " ms which is more than twice the frame interval of "
                            + (frameIntervalNanos * 0.000001f) + " ms!  "
                            + "Setting frame time to " + (lastFrameOffset * 0.000001f)
                            + " ms in the past.");
                    mDebugPrintNextFrameTimeDelta = true;
                }
                frameTimeNanos = now - lastFrameOffset;
                mLastFrameTimeNanos = frameTimeNanos;
                mFrameData.update(frameTimeNanos, mDisplayEventReceiver, jitterNanos);
            }
        }
    }
    try {
        // 迭代执行队列所有任务
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, CALLBACK_TRACE_TITLES[callbackType]);
        for (CallbackRecord c = callbacks; c != null; c = c.next) {
            if (DEBUG_FRAMES) {
                Log.d(TAG, "RunCallback: type=" + callbackType
                        + ", action=" + c.action + ", token=" + c.token
                        + ", latencyMillis=" + (SystemClock.uptimeMillis() - c.dueTime));
            }
            // 回调 CallbackRecord 的 run,其内部回调 Callback 的 run
            c.run(mFrameData);
        }
    } finally {
        synchronized (mLock) {
            mCallbacksRunning = false;
            do {
                final CallbackRecord next = callbacks.next;
                //回收CallbackRecord
                recycleCallbackLocked(callbacks);
                callbacks = next;
            } while (callbacks != null);
        }
        Trace.traceEnd(Trace.TRACE_TAG_VIEW);
    }
}
private static final class CallbackRecord {
    public CallbackRecord next;
    public long dueTime;
    /** Runnable or FrameCallback or VsyncCallback object. */
    public Object action;
    /** Denotes the action type. */
    public Object token;

    @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
    public void run(long frameTimeNanos) {
        if (token == FRAME_CALLBACK_TOKEN) {
            // 通过postFrameCallback 或 postFrameCallbackDelayed,会执行这里,postFrameCallback
            ((FrameCallback)action).doFrame(frameTimeNanos);
        } else {
            //取出Runnable执行run()
            ((android.telecom.Logging.Runnable)action).run();
        }
    }

    void run(FrameData frameData) {
        frameData.setInCallback(true);
        if (token == VSYNC_CALLBACK_TOKEN) {
            ((VsyncCallback) action).onVsync(frameData);
        } else {
            run(frameData.getFrameTimeNanos());
        }
        frameData.setInCallback(false);
    }
}

最后会调用到 action 的 run 方法,即 TraversalRunnable 的 run 方法:

final class TraversalRunnable implements Runnable {
    @Override
    public void run() {
        doTraversal();
    }
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();

在 doTraversal 中,移除了同步屏障,并且开始了 performTraversals 流程:

void doTraversal() {
    if (mTraversalScheduled) {
        mTraversalScheduled = false;
        // 移除同步屏障
        mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

        if (mProfile) {
            Debug.startMethodTracing("ViewAncestor");
        }

        // 核心。主线程(main)的 Looper 回调到 TraversalRunnable 回调
        performTraversals();

        if (mProfile) {
            Debug.stopMethodTracing();
            mProfile = false;
        }
    }
}