阅读 320

Android UI刷新机制

这是我参与更文挑战的第5天,活动详情查看: 更文挑战

ViewRootImpl

Android 中UI的刷新代表着界面的改变,一般有以下两个方法可以调用:

  • requestLayout
  • invalidate

而这两种方法都会执行到ViewRootImpl中,作为负责View树的绘制的顶层结构,上述两个方法都会调用到scheduleTraversals中,scheduleTraversals可以翻译成“执行遍历”。

    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        }
    }
复制代码

scheduleTraversals方法中主要做了三件事:

  • mTraversalScheduled参数置为true。mTraversalScheduled的作用在于防止多次调用绘制。
  • 向消息队列中添加一个同步屏障。这里的mHandler代表主线程,同步屏障的所用在于屏蔽同步消息,优先执行异步消息,那么后面与View相关的消息肯定都是异步的。毕竟要优先保证View的绘制,防止卡顿。
  • 调用Choreographer的postCallback方法。

Choreographer

我们首先来看postCallback方法中的参数mTraversalRunnable,该对象继承自Runnable,run方法中调用了doTraversal方法。

    void doTraversal() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
            performTraversals();
        }
    }
复制代码

doTraversal方法中,恰好也做了三件事:

  • mTraversalScheduled参数置为true。表示可以接受下一次的绘制操作了。
  • 移除同步屏障。放开同步消息
  • 执行View树的绘制。performTraversals方法中会从根布局开始执行View树结构的测量、布局和绘制操作。

通过上述对应的两个方法,我们可以猜测postCallback方法的作用就是执行doTraversal方法, 但是具体什么时候执行呢?

首先我们要知道屏幕的刷新频率FPS,也就是1s内屏幕的刷新次数,目前大部分的手机的刷新频率都在60~120之间,按照刷新频率为60来算的话,大概每16ms就会刷新一次频率,Android中按照这个时间来下发VSync信号,那么可能大家就会有下面的疑问:

  1. View的绘制和Vsync信号之间的关系,View什么时候绘制?
  2. VSync信号如何接收,需要主动订阅吗?

通过以上两个问题,我们引出一个重要的类Choreographer,我将它翻译为起舞者,Android中对该类的说明是协调动画、输入和绘图的时序,接收一个垂直同步信号,计划下一帧的操作。简单就是Choreographer注册一个VSync信号接收器,接收下一帧的到来然后执行指定的操作。

Choreographer中postCallback方法会执行到postCallbackDelayedInternal方法。

    private void postCallbackDelayedInternal(int callbackType,
            Object action, Object token, long delayMillis) {

        synchronized (mLock) {
            final long now = SystemClock.uptimeMillis();
            final long dueTime = now + delayMillis;
            //将传递的参数添加到CallbackQueues中
            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);
            }
        }
    }
复制代码
  • 首先将传递过来的参数callbackType、action、token添加到CallbackQueues中
  • 按照执行时间来调用scheduleFrameLocked方法,时间未到的话就调用Handler来发送一个延迟的异步消息,最终还会执行到scheduleFrameLocked方法。
    private void scheduleFrameLocked(long now) {
        if (!mFrameScheduled) {
            mFrameScheduled = true;
            if (USE_VSYNC) {
             //是否是主线程
             (isRunningOnLooperThreadLocked()) {      //注册VSync接收器
                    scheduleVsyncLocked();
                } else {
                    //切换到主线程注册VSync接收器
                    Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
                    msg.setAsynchronous(true);
                    mHandler.sendMessageAtFrontOfQueue(msg);
                }
            } else {
                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);
            }
        }
    }
复制代码

USE_VSYNC默认为true,表示接收VSync信号,然后调用scheduleVsyncLocked注册信号接收器。反之直接通过主线程的Handler调用doFrame方法执行View的操作。

Choreographer中通过DisplayEventReceiver注册VSync信号接收器,接收到下一帧的VSync信号时,会回调到onVsync中。

    private final class FrameDisplayEventReceiver extends DisplayEventReceiver
            implements Runnable {
        private boolean mHavePendingVsync;
        private long mTimestampNanos;
        private int mFrame;

        public FrameDisplayEventReceiver(Looper looper, int vsyncSource) {
            super(looper, vsyncSource);
        }

        @Override
        public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {

            mTimestampNanos = timestampNanos;
            mFrame = frame;
            Message msg = Message.obtain(mHandler, this);
            msg.setAsynchronous(true);
            mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
        }

        @Override
        public void run() {
            mHavePendingVsync = false;
            doFrame(mTimestampNanos, mFrame);
        }
    }
复制代码

DisplayEventReceiver实现了Runnable方法,在onVsync方法中通过Handler直接调用自身的run方法,然后执行doFrame方法

    void doFrame(long frameTimeNanos, int frame) {
        final long startNanos;
        synchronized (mLock) {
            if (!mFrameScheduled) {
                return; // no work to do
            }
            ......
            //省略的代码用于执行帧的时间校正操作
            ......
        try {

            AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);

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

            mFrameInfo.markAnimationsStart();
            doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);

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

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

    }
复制代码

doCallbacks方法中按照Type来分别执行,大家还记得我们调用postCallback方法传递的type吗,没错就是Choreographer.CALLBACK_TRAVERSAL。 继续看一下doCallbacks中的操作:

    void doCallbacks(int callbackType, long frameTimeNanos) {
        CallbackRecord callbacks;
        synchronized (mLock) {
            
            callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
                    now / TimeUtils.NANOS_PER_MS);
            if (callbacks == null) {
                return;
            }
            
        for (CallbackRecord c = callbacks; c != null; c = c.next) {
              
                c.run(frameTimeNanos);
            }
    }
复制代码

按照callbackType,获取对应的CallbackRecord 对象,然后执行run方法,run方法中会调用action,也就是我们最开始传递的mTraversalRunnable的run方法。

走到这里整个流程就可以通了,在接收到VSync信号后,会调用ViewRootImpl的doTraversal方法,继而执行View树结构的绘制操作。我们也可以回答上面提出的两个问题:

1. View的绘制和Vsync信号之间的关系,View什么时候绘制?

View的绘制首先会在消息队列中插入一条同步屏障,然后注册VSync信号接收器,接收到下一帧的VSync信号才会立即绘制,移除同步屏障。

2. VSync信号如何接收,需要主动订阅吗?

VSync需要注册信号接收器,才会主动下发。同时会根据当前时间进行同步Vsync信号。

在整个View绘制的流程中Choreographer起到的作用:

  1. 接收和执行View相关的动画、输入和绘制。
  2. 注册VSync信号接收器和执行下一帧的操作,同时还负责同步VSync信号的时间。
文章分类
Android
文章标签