Android图形系统(十一)-Choreographer

作者: Stan_Z | 来源:发表于2019-02-07 21:28 被阅读58次

    在Android4.1之后增加了Choreographer机制,用于同Vsync机制配合,统一动画、输入和绘制时机。本文以绘制为例来简单学习下Choreographer。

    一、从绘制流程开始

    ViewRootImpl的requestLayout开启绘制流程:

    @Override
        public void requestLayout() {
            if (!mHandlingLayoutInLayoutRequest) {
                checkThread();//检查是否在主线程
                mLayoutRequested = true;//mLayoutRequested 是否measure和layout布局。
                scheduleTraversals();
            }
        }
    
        void scheduleTraversals() {
            if (!mTraversalScheduled) {//同一帧内不会多次调用遍历
                mTraversalScheduled = true;
                mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();//拦截同步Message
                //Choreographer回调,执行绘制操作
                mChoreographer.postCallback(
                        Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            }
        }
    

    这里主要关注两点:

    postSyncBarrier : Handler 的同步屏障。它的作用是可以拦截 Looper 对同步消息的获取和分发,加入同步屏障之后,Looper 只会获取和处理异步消息,如果没有异步消息那么就会进入阻塞状态。也就是说,对View绘制渲染的处理操作可以优先处理(设置为异步消息)。

    Choreographer: 编舞者。统一动画、输入和绘制时机。也是这章需要重点分析的内容。

    二、Choreographer启动
    public ViewRootImpl(Context context, Display display) {
        ...
        //获取Choreographer实例
        mChoreographer = Choreographer.getInstance();
        ...
    }
    

    frameworks\base\core\java\android\view\Choreographer.java

    public static Choreographer getInstance() {
        return sThreadInstance.get();
    }
    
    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!");
            }
            return new Choreographer(looper);
        }
    };
    

    每一个Looper线程都有自己的Choreographer,其他线程发送的回调只能运行在对应Choreographer所属的Looper线程上

    private Choreographer(Looper looper) {
       mLooper = looper;
       mHandler = new FrameHandler(looper);
       // 根据是否使用了VSYNC来创建一个FrameDisplayEventReceiver对象
       mDisplayEventReceiver = USE_VSYNC ? new FrameDisplayEventReceiver(looper) : null;
       mLastFrameTimeNanos = Long.MIN_VALUE;//是指上一次帧绘制时间点
       mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());//帧间时长,一般等于16.7ms
       // CALLBACK_LAST + 1 = 4,创建一个容量为4的CallbackQueue数组,用来存放4种不同的Callback
       mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
       for (int i = 0; i <= CALLBACK_LAST; i++) {
           mCallbackQueues[i] = new CallbackQueue();
       }
    }
    

    Choreographer类中有一个Looper和一个FrameHandler变量。变量USE_VSYNC用于表示系统是否是用了Vsync同步机制,该值是通过读取系统属性debug.choreographer.vsync来获取的。如果系统使用了Vsync同步机制,则创建一个FrameDisplayEventReceiver对象用于请求并接收Vsync事件,最后Choreographer创建了一个大小为3的CallbackQueue队列数组,用于保存不同类型的Callback。

    这里,不同类型的Callback包括如下4种:

    public static final int CALLBACK_INPUT = 0; //输入
    public static final int CALLBACK_ANIMATION = 1; //动画
    public static final int CALLBACK_TRAVERSAL = 2; //视图绘制
    public static final int CALLBACK_COMMIT = 3; //提交 ( 这一类型是在API level=23的时候添加的)
    

    CallbackQueue是一个容量为4的数组,每一个元素作为头指针,引出对应类型的链表,4种事件就是通过这4个链表来维护的。

    而FrameHandler中主要处理三类消息:

    private final class FrameHandler extends Handler {
       public FrameHandler(Looper looper) {
           super(looper);
       }
    
       @Override
       public void handleMessage(Message msg) {
           switch (msg.what) {
               case MSG_DO_FRAME:
                   doFrame(System.nanoTime(), 0);
                   break;
               case MSG_DO_SCHEDULE_VSYNC:
                   doScheduleVsync();   // 请求VSYNC信号
                   break;
               case MSG_DO_SCHEDULE_CALLBACK:
                   doScheduleCallback(msg.arg1);
                   break;
           }
       }
    }
    
    三、Choreographer执行流程
    i

    Choreographer提供了两类添加回调的方式:postCallback 与 postFrameCallback,当然对应类型也包含delay的方法,算上其实有4个方法。

    postCallback对应的:

    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);
    }
    

    postFrameCallback对应的:

    public void postFrameCallbackDelayed(FrameCallback callback, long delayMillis) {
        if (callback == null) {
            throw new IllegalArgumentException("callback must not be null");
        }
        postCallbackDelayedInternal(CALLBACK_ANIMATION,
                callback, FRAME_CALLBACK_TOKEN, delayMillis);
    }
    

    相比之下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;
           // obtainCallbackLocked(long dueTime, Object action, Object token)会将传入的3个参数转换为CallbackRecord(具体请看源码,非主要部分,此处略过),然后CallbackQueue根据回调类型将CallbackRecord添加到链表上。
           mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
           if (dueTime <= now) {
               // 如果delayMillis=0的话,dueTime=now,则会马上执行
               scheduleFrameLocked(now);
           } else {
               // 如果dueTime>now,则发送一个what为MSG_DO_SCHEDULE_CALLBACK类型的定时消息,等时间到了再处理,其最终处理也是执行scheduleFrameLocked(long now)方法
               Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
               msg.arg1 = callbackType;
               msg.setAsynchronous(true);
               mHandler.sendMessageAtTime(msg, dueTime);
           }
       }
    }
    

    mCallbackQueues先把对应的callback添加到链表上来,然后判断是否有延迟,如果没有则会马上执行scheduleFrameLocked,如果有,则发送一个what为MSG_DO_SCHEDULE_CALLBACK类型的定时消息,等时间到了再处理,其最终处理也是执行scheduleFrameLocked(long now)方法。

    private void scheduleFrameLocked(long now) {
       if (!mFrameScheduled) {
           mFrameScheduled = true;
           if (USE_VSYNC) {
               // 如果使用了VSYNC,由系统值确定
               if (DEBUG_FRAMES) {
                   Log.d(TAG, "Scheduling next frame on vsync.");
               }
               if (isRunningOnLooperThreadLocked()) {
                   // 请求VSYNC信号,最终会调到Native层,Native处理完成后触发FrameDisplayEventReceiver的onVsync回调,回调中最后也会调用doFrame(long frameTimeNanos, int frame)方法
                   scheduleVsyncLocked();
               } else {
                   // 在UI线程上直接发送一个what=MSG_DO_SCHEDULE_VSYNC的消息,最终也会调到scheduleVsyncLocked()去请求VSYNC信号
                   Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
                   msg.setAsynchronous(true);
                   mHandler.sendMessageAtFrontOfQueue(msg);
               }
           } else {
               // 没有使用VSYNC
               final long nextFrameTime = Math.max(
                       mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now);
               if (DEBUG_FRAMES) {
                   Log.d(TAG, "Scheduling next frame in " + (nextFrameTime - now) + " ms.");
               }
               // 直接发送一个what=MSG_DO_FRAME的消息,消息处理时调用doFrame(long frameTimeNanos, int frame)方法
               Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
               msg.setAsynchronous(true);
               mHandler.sendMessageAtTime(msg, nextFrameTime);
           }
       }
    }
    

    这里首先判断USE_VSYNC,如果使用了VSYNC:走scheduleVsyncLocked,即请求VSYNC信号,最终调用doFrame,如果没使用VSYNC,则通过消息执行doFrame。

    那么我们先简单了解下请求VSYNC信号的流程:

    private void scheduleVsyncLocked() {
        mDisplayEventReceiver.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 {
            nativeScheduleVsync(mReceiverPtr);
        }
    }
    

    mDisplayEventReceiver 对应的是FrameDisplayEventReceiver,它继承自 DisplayEventReceiver , 主要是用来接收同步脉冲信号 VSYNC。scheduleVsync()方法通过底层nativeScheduleVsync()向SurfaceFlinger 服务注册,即在下一次脉冲接收后会调用 DisplayEventReceiver的dispatchVsync()方法。这里类似于订阅者模式,但是每次调用nativeScheduleVsync()方法都有且只有一次dispatchVsync()方法回调。

    然后再看看接收VSYNC信号:

    底层向应用层发送VSYNC信号,java层通过dispatchVsync()接收,最后回调在FrameDisplayEventReceiver的onVsync

    private final class FrameDisplayEventReceiver extends DisplayEventReceiver implements Runnable {
        private boolean mHavePendingVsync;
        private long mTimestampNanos;
        private int mFrame;
        @Override
        public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
            //忽略来自第二显示屏的Vsync
            if (builtInDisplayId != SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) {
                scheduleVsync();
                return;
            }
            ...
            mTimestampNanos = timestampNanos;
            mFrame = frame;
            //该消息的callback为当前对象FrameDisplayEventReceiver
            Message msg = Message.obtain(mHandler, this);
            msg.setAsynchronous(true);
            //此处mHandler为FrameHandler
            mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
        }
        @Override
        public void run() {
            mHavePendingVsync = false;
            doFrame(mTimestampNanos, mFrame); 
        }
    }
    

    可见onVsync()过程是通过FrameHandler向主线程Looper发送了一个自带callback的消息 callback为FrameDisplayEventReceiver。 当主线程Looper执行到该消息时,则调用FrameDisplayEventReceiver.run()方法,紧接着便是调用doFrame。

    void doFrame(long frameTimeNanos, int frame) {
        final long startNanos;
        synchronized (mLock) {
            if (!mFrameScheduled) {
                return; // mFrameScheduled=false,则直接返回。
            }
            long intendedFrameTimeNanos = frameTimeNanos; //原本计划的绘帧时间点
            startNanos = System.nanoTime();//保存起始时间
            //由于Vsync事件处理采用的是异步方式,因此这里计算消息发送与函数调用开始之间所花费的时间
            final long jitterNanos = startNanos - frameTimeNanos;
            //如果线程处理该消息的时间超过了屏幕刷新周期
            if (jitterNanos >= mFrameIntervalNanos) {
                //计算函数调用期间所错过的帧数
                final long skippedFrames = jitterNanos / mFrameIntervalNanos;
                //当掉帧个数超过30,则输出相应log
                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信号
            if (frameTimeNanos < mLastFrameTimeNanos) {
                scheduleVsyncLocked();
                return;
            }
            mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos);
            mFrameScheduled = false;
            mLastFrameTimeNanos = frameTimeNanos;
        }
        try {
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame");
            //分别回调CALLBACK_INPUT、CALLBACK_ANIMATION、CALLBACK_TRAVERSAL事件
            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 {
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
    }
    

    当Vsync事件到来时,顺序执行4种事件对应CallbackQueue队列中注册的回调。

    void doCallbacks(int callbackType, long frameTimeNanos) {
        CallbackRecord callbacks;
        synchronized (mLock) {
            final long now = SystemClock.uptimeMillis();
            //从指定类型的CallbackQueue队列中查找执行时间到的CallbackRecord
            callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(now);
            if (callbacks == null) {
                return;
            }
            mCallbacksRunning = true;
        }
        try {
            //由于CallbackQueues是按时间先后顺序排序的,因此遍历执行所有时间到的CallbackRecord
            for (CallbackRecord c = callbacks; c != null; c = c.next) {
                c.run(frameTimeNanos);
            }
        } finally {
            synchronized (mLock) {
                mCallbacksRunning = false;
                do {
                    final CallbackRecord next = callbacks.next;
                    recycleCallbackLocked(callbacks);
                    callbacks = next;
                } while (callbacks != null);
            }
        }
    }
    

    按时间顺序先后执行CallbackRecord对应的run方法

    private static final class CallbackRecord {
        public CallbackRecord next;
        public long dueTime;
        public Object action; // Runnable or FrameCallback
        public Object token;
        public void run(long frameTimeNanos) {
            if (token == FRAME_CALLBACK_TOKEN) {
                ((FrameCallback)action).doFrame(frameTimeNanos);
            } else {
                ((Runnable)action).run();
            }
        }
    }
    

    接开篇讲的

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

    mTraversalRunnable对应:

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

    run方法被执行,所以doTraversal()被执行,开启View的绘制流程。

    所以整个绘制过程总的流程如下所示:

    简单总结:

    • Choreographer支持4种类型事件:输入、绘制、动画、提交,并通过postCallback在对应需要同步vsync进行刷新处进行注册,等待回调。
    • Choreographer监听底层Vsync信号,一旦接收到回调信号,则通过doFrame统一对java层4种类型事件进行回调。

    参考
    https://blog.csdn.net/bluewindtalker/article/details/54017569
    https://blog.csdn.net/qian520ao/article/details/80954626
    http://gityuan.com/2017/02/25/choreographer/
    https://www.jianshu.com/p/47c866f6fb67

    相关文章

      网友评论

        本文标题:Android图形系统(十一)-Choreographer

        本文链接:https://www.haomeiwen.com/subject/umbgsqtx.html