精华内容
参与话题
问答
  • Android系统的编舞者Choreographer

    千次阅读 2018-04-26 19:59:20
    前言 上一篇文章 Android的16ms和垂直同步以及三重缓存 解释...Choreographer机制,用于同Vsync机制配合,实现统一调度界面绘图。 Choreographer的构造 Choreographer是线程级别的单例,并且具有处理当前线程...

    个人博客地址 http://dandanlove.com/

    前言

    上一篇文章 Android的16ms和垂直同步以及三重缓存 解释了手机流畅性的问题,并在文章中提到了在Android4.1中添加的VsyncChoreographer机制,用于同Vsync机制配合,实现统一调度界面绘图。

    Choreographer的构造

    Choreographer是线程级别的单例,并且具有处理当前线程消息循环队列的功能。

    public final class Choreographer {
        // Enable/disable vsync for animations and drawing.
        private static final boolean USE_VSYNC = SystemProperties.getBoolean(
                "debug.choreographer.vsync", true);
        
    	//单例
    	public static Choreographer getInstance() {
            return sThreadInstance.get();
        }
    
        //每个线程一个Choreographer实例
        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);
            }
        };
    
        private Choreographer(Looper looper) {
            mLooper = looper;
            //创建handle对象,用于处理消息,其looper为当前的线程的消息队列
            mHandler = new FrameHandler(looper);
            //创建VSYNC的信号接受对象
            mDisplayEventReceiver = USE_VSYNC ? new FrameDisplayEventReceiver(looper) : null;
            //初始化上一次frame渲染的时间点
            mLastFrameTimeNanos = Long.MIN_VALUE;
            //计算帧率,也就是一帧所需的渲染时间,getRefreshRate是刷新率,一般是60
            mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());
            //创建消息处理队列
            mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
            for (int i = 0; i <= CALLBACK_LAST; i++) {
                mCallbackQueues[i] = new CallbackQueue();
            }
        }
    }
    

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

    Choreographer的使用

    注册Runnable对象

    作者之前写过一篇关于ViewRootImpl的文章:ViewRootImpl的独白,我不是一个View(布局篇)里面有涉及使用Choreographer进行View的绘制,这次我们从ViewRootImpl的绘制出发来看看Choreographer的使用。

    public final class ViewRootImpl implements ViewParent,
            View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
        Choreographer mChoreographer;
        /***部分代码省略***/
        public ViewRootImpl(Context context, Display display) {
            /***部分代码省略***/
            mChoreographer = Choreographer.getInstance();
            /***部分代码省略***/
        }
        /***部分代码省略***/
        void scheduleTraversals() {
            if (!mTraversalScheduled) {
                mTraversalScheduled = true;
                mTraversalBarrier = mHandler.getLooper().postSyncBarrier();
                mChoreographer.postCallback(
                        Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
                if (!mUnbufferedInputDispatch) {
                    scheduleConsumeBatchedInput();
                }
                notifyRendererOfFramePending();
            }
        }
    }
    

    注册FrameCallback对象

    无论是注册Runnable还是注册FrameCallback对象最终都会调用postCallbackDelayedInternal方法往mCallbackQueues添加回调,区别在于FrameCallback的token为FRAME_CALLBACK_TOKEN,两者在回调的时候不相同。

    public final class Choreographer {
        // All frame callbacks posted by applications have this token.
        private static final Object FRAME_CALLBACK_TOKEN = new Object() {
            public String toString() { return "FRAME_CALLBACK_TOKEN"; }
        };
    
        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();
                }
            }
        }
    }
    

    Choreographer的消息处理

    Choreographer接受消息

    public final class Choreographer {
        //Input callback.  Runs first.
        public static final int CALLBACK_INPUT = 0;
        //Animation callback.  Runs before traversals.
        public static final int CALLBACK_ANIMATION = 1;
        // Traversal callback.  Handles layout and draw.  
        //Runs last after all other asynchronous messages have been handled.
        public static final int CALLBACK_TRAVERSAL = 2;
        private static final int CALLBACK_LAST = CALLBACK_TRAVERSAL;
    
        //长度为3(CALLBACK_LAST+1)的CallbackQueue类型的数组
        private final CallbackQueue[] mCallbackQueues;
    
        //发送回调事件
        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) {
            /***部分代码省略***/
            synchronized (mLock) {
                //从开机到现在的毫秒数(手机睡眠的时间不包括在内); 
                final long now = SystemClock.uptimeMillis();
                final long dueTime = now + delayMillis;
                //添加类型为callbackType的CallbackQueue(将要执行的回调封装而成)
                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);
                }
            }
        }
        /**
         * @param dueTime 任务开始时间
         * @param action 任务
         * @param token 标识
         */
        private CallbackRecord obtainCallbackLocked(long dueTime, Object action, Object token) {
            CallbackRecord callback = mCallbackPool;
            if (callback == null) {
                callback = new CallbackRecord();
            } else {
                mCallbackPool = callback.next;
                callback.next = null;
            }
            callback.dueTime = dueTime;
            callback.action = action;
            callback.token = token;
            return callback;
        }
        private final class CallbackQueue {
            private CallbackRecord mHead;
            public void addCallbackLocked(long dueTime, Object action, Object token) {
                CallbackRecord callback = obtainCallbackLocked(dueTime, action, token);
                CallbackRecord entry = mHead;
                //判断当前的是否不头节点
                if (entry == null) {
                    mHead = callback;
                    return;
                }
                //判断当前任务出发起始时间是不是当前所有任务的最开始时间
                if (dueTime < entry.dueTime) {
                    callback.next = entry;
                    mHead = callback;
                    return;
                }
                //根据任务开始时间由小到大插入到链表当中
                while (entry.next != null) {
                    if (dueTime < entry.next.dueTime) {
                        callback.next = entry.next;
                        break;
                    }
                    entry = entry.next;
                }
                entry.next = callback;
            }
        }
    }
    

    CallbackQueue

    public final class Choreographer {
        /**
         * Callback type: Input callback.  Runs first.
         * @hide
         */
        public static final int CALLBACK_INPUT = 0;
    
        /**
         * Callback type: Animation callback.  Runs before traversals.
         * @hide
         */
        public static final int CALLBACK_ANIMATION = 1;
    
        /**
         * Callback type: Traversal callback.  Handles layout and draw.  Runs
         * after all other asynchronous messages have been handled.
         * @hide
         */
        public static final int CALLBACK_TRAVERSAL = 2;
    }
    

    三种类型不同的CallbackRecord链表,按照任务触发时间由小到大排列。

    CallbackQueue.png

    FrameHandler异步处理

    public final class Choreographer {
    
        private static final int MSG_DO_FRAME = 0;
        private static final int MSG_DO_SCHEDULE_VSYNC = 1;
        private static final int MSG_DO_SCHEDULE_CALLBACK = 2;
        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:
                        //做VSYNC的信号同步
                        doScheduleVsync();
                        break;
                    case MSG_DO_SCHEDULE_CALLBACK:
                        //将当前任务加入执行队列
                        doScheduleCallback(msg.arg1);
                        break;
                }
            }
        }
    }
    

    doFrame

    public final class Choreographer {
        void doFrame(long frameTimeNanos, int frame) {
            final long startNanos;
            synchronized (mLock) {
                if (!mFrameScheduled) {
                    return; // no work to do
                }
                //当前时间
                startNanos = System.nanoTime();
                //抖动间隔
                final long jitterNanos = startNanos - frameTimeNanos;
                //抖动间隔大于屏幕刷新时间间隔(16ms)
                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.");
                    }
                    //最后一次的屏幕刷是lastFrameOffset之前开始的
                    final long lastFrameOffset = jitterNanos % mFrameIntervalNanos;
                    if (DEBUG) {
                        Log.d(TAG, "Missed vsync by " + (jitterNanos * 0.000001f) + " ms "
                                + "which is more than the frame interval of "
                                + (mFrameIntervalNanos * 0.000001f) + " ms!  "
                                + "Skipping " + skippedFrames + " frames and setting frame "
                                + "time to " + (lastFrameOffset * 0.000001f) + " ms in the past.");
                    }
                    //最后一帧的刷新开始时间
                    frameTimeNanos = startNanos - lastFrameOffset;
                }
                //由于跳帧可能造成了当前展现的是之前的帧,这样需要等待下一个vsync信号
                if (frameTimeNanos < mLastFrameTimeNanos) {
                    if (DEBUG) {
                        Log.d(TAG, "Frame time appears to be going backwards.  May be due to a "
                                + "previously skipped frame.  Waiting for next vsync.");
                    }
                    scheduleVsyncLocked();
                    return;
                }
                //当前画面刷新的状态置false
                mFrameScheduled = false;
                //更新最后一帧的刷新时间
                mLastFrameTimeNanos = frameTimeNanos;
            }
            //按照优先级策略进行画面刷新时间处理
            doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
            doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
            doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
            
            if (DEBUG) {
                final long endNanos = System.nanoTime();
                Log.d(TAG, "Frame " + frame + ": Finished, took "
                        + (endNanos - startNanos) * 0.000001f + " ms, latency "
                        + (startNanos - frameTimeNanos) * 0.000001f + " ms.");
            }
        }
    }
    

    doScheduleVsync

    public final class Choreographer {
        //等待vsync信号
        void doScheduleVsync() {
            synchronized (mLock) {
                if (mFrameScheduled) {
                    scheduleVsyncLocked();
                }
            }
        }
        //当运行在Looper线程,则立刻调度vsync
        private void scheduleVsyncLocked() {
            mDisplayEventReceiver.scheduleVsync();
        }
    }
    

    doScheduleCallback

    public final class Choreographer {
        // Enable/disable vsync for animations and drawing.
        private static final boolean USE_VSYNC = SystemProperties.getBoolean(
                "debug.choreographer.vsync", true);
        private final class CallbackQueue {
            //判断是否有能执行的任务
            public boolean hasDueCallbacksLocked(long now) {
                return mHead != null && mHead.dueTime <= now;
            }
            /***部分代码省略***/
        }
        /***部分代码省略***/
        //执行任务回调
        void doScheduleCallback(int callbackType) {
            synchronized (mLock) {
                if (!mFrameScheduled) {
                    final long now = SystemClock.uptimeMillis();
                    //有能执行的任务
                    if (mCallbackQueues[callbackType].hasDueCallbacksLocked(now)) {
                        scheduleFrameLocked(now);
                    }
                }
            }
        }
    
        private void scheduleFrameLocked(long now) {
            if (!mFrameScheduled) {
                mFrameScheduled = true;
                if (USE_VSYNC) {
                    if (DEBUG) {
                        Log.d(TAG, "Scheduling next frame on vsync.");
                    }
    
                    // If running on the Looper thread, then schedule the vsync immediately,
                    // otherwise post a message to schedule the vsync from the UI thread
                    // as soon as possible.
                    if (isRunningOnLooperThreadLocked()) {
                        //当运行在Looper线程,则立刻调度vsync
                        scheduleVsyncLocked();
                    } else {
                        //切换到主线程,调度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) {
                        Log.d(TAG, "Scheduling next frame in " + (nextFrameTime - now) + " ms.");
                    }
                    Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
                    msg.setAsynchronous(true);
                    mHandler.sendMessageAtTime(msg, nextFrameTime);
                }
            }
        }
        //检测当前的Looper线程是不是主线程
        private boolean isRunningOnLooperThreadLocked() {
            return Looper.myLooper() == mLooper;
        }
    }
    
    public final class Choreographer {
        // The display event receiver can only be accessed by the looper thread to which
        // it is attached.  We take care to ensure that we post message to the looper
        // if appropriate when interacting with the display event receiver.
        private final FrameDisplayEventReceiver mDisplayEventReceiver;
    
        private Choreographer(Looper looper) {
            /***部分代码省略***/
            //在Choreographer的构造函数中,我们使用USE_VSYNC则会有FrameDisplayEventReceiver做为与显示器时间进行交互
            mDisplayEventReceiver = USE_VSYNC ? new FrameDisplayEventReceiver(looper) : null;
        }
        /***部分代码省略***/
        private final class FrameDisplayEventReceiver extends DisplayEventReceiver
                implements Runnable {
            //构造函数需要传入当前的looper队列  
            public FrameDisplayEventReceiver(Looper looper) {
                super(looper);
            }
            /***部分代码省略***/  
        }
    }
    
    public abstract class DisplayEventReceiver {
        private static native void nativeScheduleVsync(long receiverPtr);
        /**
         * Creates a display event receiver.
         *
         * @param looper The looper to use when invoking callbacks.
         */
        public DisplayEventReceiver(Looper looper) {
            if (looper == null) {
                throw new IllegalArgumentException("looper must not be null");
            }
            mMessageQueue = looper.getQueue();
            //接受数量多少等于looper中消息的多少
            mReceiverPtr = nativeInit(this, mMessageQueue);
            mCloseGuard.open("dispose");
        }
        /**
         * Schedules a single vertical sync pulse to be delivered when the next
         * display frame begins.
         */
        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);
            }
        }
    }
    

    Choreographer流程汇总

    choreographer.png

    native端的消息处理

    文件路径:frameworks/base/core/jni/android_view_DisplayEventReceiver.cpp

    NativeDisplayEventReceiver类结构

    //NativeDisplayEventReceiver类的定义
    class NativeDisplayEventReceiver : public LooperCallback {
    public://对象公共方法
        //构造函数
        NativeDisplayEventReceiver(JNIEnv* env,
                jobject receiverObj, const sp<MessageQueue>& messageQueue);
        status_t initialize();  //初始化方法
        void dispose();
        status_t scheduleVsync();//获取下一个VSYNC信号
    
    protected:
        virtual ~NativeDisplayEventReceiver();//析构函数
    
    private:
        jobject mReceiverObjGlobal;//java层的DisplayEventReceiver的全局引用
        sp<MessageQueue> mMessageQueue;//looper的消息队列
        DisplayEventReceiver mReceiver;//frameworks/nivate/libs/gui/DisplayEventReceiver.cpp
        bool mWaitingForVsync;//默认为false
    
        virtual int handleEvent(int receiveFd, int events, void* data);
        bool processPendingEvents(nsecs_t* outTimestamp, int32_t* id, uint32_t* outCount);
        void dispatchVsync(nsecs_t timestamp, int32_t id, uint32_t count);
        void dispatchHotplug(nsecs_t timestamp, int32_t id, bool connected);
    };
    
    //ststem/core/include/utils/Looper.h
    /**
     * A looper callback.
     */
    //NativeDisplayEventReceiver的父类,用与looper中消息的回调
    class LooperCallback : public virtual RefBase {
    protected:
        virtual ~LooperCallback() { }
    
    public:
        virtual int handleEvent(int fd, int events, void* data) = 0;
    };
    

    NativeDisplayEventReceiver初始化

    //初始化native的消息队列
    static jlong nativeInit(JNIEnv* env, jclass clazz, jobject receiverObj,
            jobject messageQueueObj) {
        sp<MessageQueue> messageQueue = android_os_MessageQueue_getMessageQueue(env, messageQueueObj);
        if (messageQueue == NULL) {
            jniThrowRuntimeException(env, "MessageQueue is not initialized.");
            return 0;
        }
        //构造NativeDisplayEventReceiver对象
        sp<NativeDisplayEventReceiver> receiver = new NativeDisplayEventReceiver(env,
                receiverObj, messageQueue);
        status_t status = receiver->initialize();
        if (status) {
            String8 message;
            message.appendFormat("Failed to initialize display event receiver.  status=%d", status);
            jniThrowRuntimeException(env, message.string());
            return 0;
        }
    
        receiver->incStrong(gDisplayEventReceiverClassInfo.clazz); // retain a reference for the object
        return reinterpret_cast<jlong>(receiver.get());
    }
    //NativeDisplayEventReceiver的构造函数
    NativeDisplayEventReceiver::NativeDisplayEventReceiver(JNIEnv* env,
            jobject receiverObj, const sp<MessageQueue>& messageQueue) :
            mReceiverObjGlobal(env->NewGlobalRef(receiverObj)),
            mMessageQueue(messageQueue), mWaitingForVsync(false) {
        ALOGV("receiver %p ~ Initializing input event receiver.", this);
    }
    //receiver内部数据的初始化
    status_t NativeDisplayEventReceiver::initialize() {
        status_t result = mReceiver.initCheck();
        if (result) {
            ALOGW("Failed to initialize display event receiver, status=%d", result);
            return result;
        }
        //监听mReceiver的所获取的文件句柄。
        int rc = mMessageQueue->getLooper()->addFd(mReceiver.getFd(), 0, Looper::EVENT_INPUT,
                this, NULL);
        if (rc < 0) {
            return UNKNOWN_ERROR;
        }
        return OK;
    }
    

    NativeDisplayEventReceiver请求VSYNC的同步

    //java层调用DisplayEventReceiver的scheduleVsync请求VSYNC的同步
    static void nativeScheduleVsync(JNIEnv* env, jclass clazz, jlong receiverPtr) {
        sp<NativeDisplayEventReceiver> receiver =
                reinterpret_cast<NativeDisplayEventReceiver*>(receiverPtr);
        status_t status = receiver->scheduleVsync();
        if (status) {
            String8 message;
            message.appendFormat("Failed to schedule next vertical sync pulse.  status=%d", status);
            jniThrowRuntimeException(env, message.string());
        }
    }
    
    status_t NativeDisplayEventReceiver::scheduleVsync() {
        if (!mWaitingForVsync) {
            ALOGV("receiver %p ~ Scheduling vsync.", this);
    
            // Drain all pending events.
            nsecs_t vsyncTimestamp;
            int32_t vsyncDisplayId;
            uint32_t vsyncCount;
            processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount);
            //请求下一次Vsync信息处理
            status_t status = mReceiver.requestNextVsync();
            if (status) {
                ALOGW("Failed to request next vsync, status=%d", status);
                return status;
            }
    
            mWaitingForVsync = true;
        }
        return OK;
    }
    
    //frameworks/native/libs/gui/DisplayEventReceiver.cpp
    //通过IDisplayEventConnection接口来请求Vsync信号,IDisplayEventConnection实现了Binder通信框架,可以跨进程调用。
    //因为Vsync信号请求进程和Vsync产生进程有可能不在同一个进程空间,因此这里就借助IDisplayEventConnection接口来实现。
    status_t DisplayEventReceiver::requestNextVsync() {
        if (mEventConnection != NULL) {
            mEventConnection->requestNextVsync();
            return NO_ERROR;
        }
        return NO_INIT;
    }
    

    NativeDisplayEventReceiver处理消息

    //NativeDisplayEventReceiver处理消息
    int NativeDisplayEventReceiver::handleEvent(int receiveFd, int events, void* data) {
        ...
        nsecs_t vsyncTimestamp;
        int32_t vsyncDisplayId;
        uint32_t vsyncCount;
        //过滤出最后一次的vsync
        if (processPendingEvents(&vsyncTimestamp, &vsyncDisplayId, &vsyncCount)) {
            mWaitingForVsync = false;
            //分发Vsync,调用到native的android/view/DisplayEventReceiver.class的dispatchVsync方法
            dispatchVsync(vsyncTimestamp, vsyncDisplayId, vsyncCount);
        }
        return 1;
    }
    

    DisplayEventReceiver分发VSYNC信号

    public abstract class DisplayEventReceiver {
        /***部分代码省略***/
        public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
        }
        // Called from native code.
        @SuppressWarnings("unused")
        private void dispatchVsync(long timestampNanos, int builtInDisplayId, int frame) {
            onVsync(timestampNanos, builtInDisplayId, frame);
        }
    }
    
    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.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
        }
    
        @Override
        public void run() {
            mHavePendingVsync = false;
            //DisplayEventReceiver消息处理
            doFrame(mTimestampNanos, mFrame);
        }
    }
    

    DisplayEventReceiver消息处理

    参见4.2.1、doFrame介绍

    Choreographer处理回调

    Choreographer触发可执行任务的回调

    这里为什么说可执行任务呢?因为每个任务都有自己的触发时间,Choreographer只选择它能触发的任务。

    public final class Choreographer {
        //进行回调的标识
        private boolean mCallbacksRunning;
        /***部分代码省略***/
        void doCallbacks(int callbackType, long frameTimeNanos) {
            CallbackRecord callbacks;
            synchronized (mLock) {
                final long now = SystemClock.uptimeMillis();
                //找到当前能触发的回调链表
                callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(now);
                if (callbacks == null) {
                    return;
                }
                mCallbacksRunning = true;
            }
            try {
                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);
                }
            }
        }
        //回收回调任务资源
        private void recycleCallbackLocked(CallbackRecord callback) {
            callback.action = null;
            callback.token = null;
            callback.next = mCallbackPool;
            mCallbackPool = callback;
        }
        private final class CallbackQueue {
            public CallbackRecord extractDueCallbacksLocked(long now) {
                CallbackRecord callbacks = mHead;
                //当链表头部的任务触发事件都比当前时间晚,那么整个链表则没有任务需要触发
                if (callbacks == null || callbacks.dueTime > now) {
                    return null;
                }
    
                CallbackRecord last = callbacks;
                CallbackRecord next = last.next;
                //找到当前时间之前需要触发任务链表,将该链表截断并返回
                while (next != null) {
                    if (next.dueTime > now) {
                        last.next = null;
                        break;
                    }
                    last = next;
                    next = next.next;
                }
                //mHead重置为原始链表截断的头部
                mHead = next;
                return callbacks;
            }
        }
    }
    

    处理Choreographer回调

    3、Choreographer的使用部分讲述了ViewRootImpl使用Choreographer的使用,那么我们现在来看一下ViewRootImplChoreographer回调时间的处理。

    public final class ViewRootImpl implements ViewParent,
            View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks {
        Choreographer mChoreographer;
        /***部分代码省略***/
        public ViewRootImpl(Context context, Display display) {
            /***部分代码省略***/
            mChoreographer = Choreographer.getInstance();
            /***部分代码省略***/
        }
        /***部分代码省略***/
        void scheduleTraversals() {
            if (!mTraversalScheduled) {
                mTraversalScheduled = true;
                mTraversalBarrier = mHandler.getLooper().postSyncBarrier();
                mChoreographer.postCallback(
                        Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
                if (!mUnbufferedInputDispatch) {
                    scheduleConsumeBatchedInput();
                }
                notifyRendererOfFramePending();
            }
        }
        final class TraversalRunnable implements Runnable {
            @Override
            public void run() {
                //开始View的测量、布局、绘制
                doTraversal();
            }
        }
        final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
    }
    

    总结

    整片文章单独看起来留下的印象不是很深刻,以前阅读过 Android的16ms和垂直同步以及三重缓存 这篇文章之后就会知道本文章是对 Android的16ms和垂直同步以及三重缓存 这篇文章其中的一些疑问进行解答。从代码的角度讲述了android的屏幕绘制部分知识。

    文章到这里就全部讲述完啦,若有其他需要交流的可以留言哦

    想阅读作者的更多文章,可以查看我 个人博客 和公共号:
    振兴书城

    展开全文
  • Choreographer 解析

    2019-03-06 17:52:43
    在看Animator源码的过程中,发现书上源码和实际源码不同,其...在 Logcat 中使用关键词 Choreographer 进行过滤,对 App 做一些操作,一般能看到控制台输出这样的日志 I/Choreographer: Skipped 55 frames! T...

    在看Animator源码的过程中,发现书上源码和实际源码不同,其handler的使用封装在了这个类中。后发现这个类是4.1之后才出现的,特此先找几篇文章记录一下其源代码及使用方式

    一、概述

    在 Logcat 中使用关键词 Choreographer 进行过滤,对 App 做一些操作,一般能看到控制台输出这样的日志

    I/Choreographer: Skipped 55 frames!  The application may be doing too much work on its main thread.
    I/Choreographer: Skipped 43 frames!  The application may be doing too much work on its main thread.
    

    这是观察 App 掉帧情况的一种手段
    默认只会打印掉帧 >=30 帧的信息,已 ROOT 的手机可以修改此条件

    getprop debug.choreographer.skipwarning      //读取
    setprop debug.choreographer.skipwarning 5    //修改
    setprop ctl.restart surfaceflinger; setprop ctl.restart zygote    //重启
    

    理解 Choreographer 工作过程,有助于分析代码调用过程、进行帧率统计、分析 Systrace

    二、Choreographer

    硬件每 16 毫秒产一个 VSync 信号,App 要想实现垂直同步,就是要收到 VSync
    只有调用 DisplayEventReceiver 的 nativeScheduleVsync 方法后,才能收到下一个 VSync
    请求 VSync(即调用 nativeScheduleVsync)只是单次有效,并不是一劳永逸
    请求 VSync → 收到 VSync → 请求 VSync → 收到 VSync
    如果没有再次请求 VSync,则无法收到 VSync

    我们将 Choreographer 工作过程分为 2 部分来分析:请求 VSync 和 收到 VSync

    1、Choreographer 请求 VSync

    1.1 ViewRootImpl

    [ViewRootImpl.java→]

    void scheduleTraversals() {
        ...
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        ...
    }
    
    @Override
    public void requestLayout() {
        if (!mHandlingLayoutInLayoutRequest) {
            checkThread();
            mLayoutRequested = true;
            scheduleTraversals();
        }
    }
    
    void invalidate() {
        mDirty.set(0, 0, mWidth, mHeight);
        if (!mWillDrawSoon) {
            scheduleTraversals();
        }
    }
    

    熟悉 View 绘制过程的话,应该知道会一直递归向上到 ViewRootImpl 最终会调用到 scheduleTraversals()
    在 ViewRootImpl 里也能找到

    mChoreographer.postCallback(Choreographer.CALLBACK_INPUT, ...
    
    mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, ...
    

    1.2 mChoreographer.postCallback

    [Choreographer.java→]

    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) {
    
        synchronized (mLock) {
            final long now = SystemClock.uptimeMillis();
            final long dueTime = now + delayMillis;
            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);
            }
        }
    }
    

    把任务都放在 mCallbackQueues[callbackType] 队列中

    1.3 scheduleFrameLocked

    private void scheduleFrameLocked(long now) {
        if (!mFrameScheduled) {
            mFrameScheduled = true;
            if (USE_VSYNC) {
    
                // If running on the Looper thread, then schedule the vsync immediately,
                // otherwise post a message to schedule the vsync from the UI thread
                // as soon as possible.
                if (isRunningOnLooperThreadLocked()) {
                    scheduleVsyncLocked();
                } else {
                    Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
                    msg.setAsynchronous(true);
                    mHandler.sendMessageAtFrontOfQueue(msg);
                }
            } else {
                // mLastFrameTimeNanos 主要作用于当 USE_VSYNC = false 的时候,设置 doFrame 的时间
                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,也就是代表开启垂直同步

    private static final boolean USE_VSYNC = SystemProperties.getBoolean(
            "debug.choreographer.vsync", true);
    
    // If running on the Looper thread, then schedule the vsync immediately,
    // otherwise post a message to schedule the vsync from the UI thread
    // as soon as possible.
    

    这句注释意思就是如果当前是在主线程,则立即执行 scheduleVsyncLocked(),如果不是主线程,则通过 mHandler 发消息给主线程,最终也是执行 scheduleVsyncLocked()

    1.4 scheduleVsyncLocked

    mDisplayEventReceiver = FrameDisplayEventReceiver(looper) 
    
    private void scheduleVsyncLocked() {
        mDisplayEventReceiver.scheduleVsync();
    }
    
    class FrameDisplayEventReceiver extends DisplayEventReceiver implements Runnable
    

    [DisplayEventReceiver→]

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

    2、Choreographer 收到 VSync

    2.1 dispatchVsync

    Java 层接收 VSync 的入口是 dispatchVsync(),看注释就知道是从 native 调用的

    [DisplayEventReceiver.java→]

    // Called from native code.
    private void dispatchVsync(long timestampNanos, int builtInDisplayId, int frame) {
        onVsync(timestampNanos, builtInDisplayId, frame);
    }
    

    断点下就很清晰了,显然收到 VSync 时会往 UI Looper 中插入一个 msg,所以 onVsync 也是在主线程执行的

    2.2 onVsync

    [Choreographer.java→]

    private final class FrameDisplayEventReceiver extends DisplayEventReceiver
            implements Runnable {
        ...
        private long mTimestampNanos;
    
        public FrameDisplayEventReceiver(Looper looper) {
            super(looper);
        }
    
        @Override
        public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
            ...
            mTimestampNanos = timestampNanos;
            Message msg = Message.obtain(mHandler, this);
            msg.setAsynchronous(true);
            mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
        }
    
        @Override
        public void run() {
            ...
            doFrame(mTimestampNanos, mFrame);
        }
    }
    

    Message.obtain(mHandler, this) 所以 msg.callback 是 this,只要清楚 Handler 工作原理就知道最后会调用到 msg.callback.run(),也就是 FrameDisplayEventReceiver run(),进入 doFrame()
    关注 mTimestampNanos,它是来自 onVsync 的 timestampNanos 参数,代表产生 VSync 的时间

    2.3 doFrame

    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
                frameTimeNanos = startNanos - lastFrameOffset;
            }
            
            if (frameTimeNanos < mLastFrameTimeNanos) {
                // 请求 VSync
                scheduleVsyncLocked();
                return;
            }
    
            mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos);
            mFrameScheduled = false;
            mLastFrameTimeNanos = frameTimeNanos;
        }
    
        try {
            ...
            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 {
            ...
        }
        ...
    }
    

    当出现掉帧时,会对 frameTimeNanos 进行修正,修正到最后一次 VSync 的时间
    当 frameTimeNanos < mLastFrameTimeNanos 时,请求 VSync 然后 return,相当于忽略本次信号,等待下一个信号。但这个条件不太可能通过,有可能的情况比如 System.nanoTime() 发生倒退(比如修改手机时间)。反正这个条件通过就代表出现异常情况。

    2.4 doCallbacks

    void doCallbacks(int callbackType, long frameTimeNanos) {
        CallbackRecord callbacks;
        synchronized (mLock) {
            // We use "now" to determine when callbacks become due because it's possible
            // for earlier processing phases in a frame to post callbacks that should run
            // in a following phase, such as an input event that causes an animation to start.
            final long now = System.nanoTime();
            callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
                    now / TimeUtils.NANOS_PER_MS);
            if (callbacks == null) {
                return;
            }
            mCallbacksRunning = true;
    
            // Update the frame time if necessary when committing the frame.
            // We only update the frame time if we are more than 2 frames late reaching
            // the commit phase.  This ensures that the frame time which is observed by the
            // callbacks will always increase from one frame to the next and never repeat.
            // We never want the next frame's starting frame time to end up being less than
            // or equal to the previous frame's commit frame time.  Keep in mind that the
            // next frame has most likely already been scheduled by now so we play it
            // safe by ensuring the commit time is always at least one frame behind.
            if (callbackType == Choreographer.CALLBACK_COMMIT) {
                final long jitterNanos = now - frameTimeNanos;
                Trace.traceCounter(Trace.TRACE_TAG_VIEW, "jitterNanos", (int) jitterNanos);
                if (jitterNanos >= 2 * mFrameIntervalNanos) {
                    final long lastFrameOffset = jitterNanos % mFrameIntervalNanos
                            + mFrameIntervalNanos;
                    if (DEBUG_JANK) {
                        Log.d(TAG, "Commit callback delayed by " + (jitterNanos * 0.000001f)
                                + " ms which is more than twice the frame interval of "
                                + (mFrameIntervalNanos * 0.000001f) + " ms!  "
                                + "Setting frame time to " + (lastFrameOffset * 0.000001f)
                                + " ms in the past.");
                        mDebugPrintNextFrameTimeDelta = true;
                    }
                    frameTimeNanos = now - lastFrameOffset;
                    mLastFrameTimeNanos = frameTimeNanos;
                }
            }
        }
        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));
                }
                c.run(frameTimeNanos);
            }
        } finally {
            synchronized (mLock) {
                mCallbacksRunning = false;
                do {
                    final CallbackRecord next = callbacks.next;
                    recycleCallbackLocked(callbacks);
                    callbacks = next;
                } while (callbacks != null);
            }
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
    }
    

    extractDueCallbacksLocked 是取出执行时间在当前时间之前的所有 CallbackRecord,callbacks 是一个链表,然后遍历 callbacks 执行 run 方法

    public void run(long frameTimeNanos) {
        if (token == FRAME_CALLBACK_TOKEN) {
            ((FrameCallback)action).doFrame(frameTimeNanos);
        } else {
            ((Runnable)action).run();
        }
    }
    

    看到上面代码中有 FrameCallback.doFrame,只要 postFrameCallback(FrameCallback),下一次 Choreographer doFrame 时就会调用 FrameCallback.doFrame

    下面的代码可以实现监听每一帧

    Choreographer.getInstance().postFrameCallback(new Choreographer.FrameCallback() {
        @Override
        public void doFrame(long frameTimeNanos) {
            // do something
            Choreographer.getInstance().postFrameCallback(this);
        }
    });
    

    doCallbacks 最后一步是 CALLBACK_COMMIT,如果 doCallbacks 耗时 >= 32 毫秒,就修正 frameTimeNanos

    图中 doCallbacks 从 frameTimeNanos2 开始执行,执行到进入 CALLBACK_COMMIT 时,判断
    now - frameTimeNanos >= 2 * mFrameIntervalNanos,于是把 frameTimeNanos 修正到倒数第二个信号时间

    3、Choreographer 与 Looper

    Choreographer 中涉及到 Looper 的有

    1. postCallback → scheduleFrameLocked,dueTime > now 时经过 Looper
    2. scheduleFrameLocked → scheduleVsyncLocked,非 UI 线程时经过 Looper
    3. 收到 VSync → dispatchVsync,必定经过 Looper
    4. onVsync → doFrame,必定经过 Looper

    这些 msg isAsynchronous() == true

    三、msg.setAsynchronous(true)

    msg.isAsynchronous() == true 表示是一个异步消息
    当有 MessageQueue 设置 barrier 时,只有异步消息可以被处理,同步消息无法被处理,只有移除 barrier 后,同步消息才会被处理
    如果没有设置 barrier,异步消息与同步消息没有区别

    1、postSyncBarrier 和 removeSyncBarrier

    如何设置 barrier 和 移除 barrier,在 ViewRootImpl 里就可以看到

    [ViewRootImpl.java→]

    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            ...
        }
    }
    
    void doTraversal() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
            ...
            performTraversals();
            ...
        }
    }
    

    2、example

    写个 example 看下 msg 的调用顺序就知道 barrier 和异步消息是如何使用的

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            Log.d("TEST", "" + msg.what);
            if (msg.what == 3) {
                removeSyncBarrier(mHandler, mBarrier);
                mBarrier = -1;
            }
    
        }
    };
    
    private int mBarrier = -1;
    
    @Override
    public void onClick(View v) {
        mBarrier = postSyncBarrier(mHandler);
        //------------------------------------------------------------
        mHandler.post(new Runnable() {
            @Override
            public void run() {
                Log.d("TEST", "1");
    
            }
        });
        //------------------------------------------------------------
        Message msg = Message.obtain(mHandler, new Runnable() {
            @Override
            public void run() {
                Log.d("TEST", "2");
            }
        });
        mHandler.sendMessageDelayed(msg, 1000);
        //------------------------------------------------------------
        Message msg3 = Message.obtain();
        msg3.setAsynchronous(true);
        msg3.what = 3;
        mHandler.sendMessageDelayed(msg3, 2000);
        //------------------------------------------------------------
        Message msg4 = Message.obtain();
        msg4.setAsynchronous(true);
        msg4.what = 4;
        mHandler.sendMessageDelayed(msg4, 1500);
        //------------------------------------------------------------
        Message msg5 = Message.obtain();
        msg5.what = 5;
        mHandler.sendMessage(msg5);
    }
    
    public static int postSyncBarrier(Handler handler) {
        MessageQueue messageQueue = handler.getLooper().getQueue();
        try {
            Method postSyncBarrier = messageQueue.getClass().getDeclaredMethod("postSyncBarrier");
            postSyncBarrier.setAccessible(true);
            return (int) postSyncBarrier.invoke(messageQueue);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        return -1;
    }
    
    public static void removeSyncBarrier(Handler handler, int barrier) {
        MessageQueue messageQueue = handler.getLooper().getQueue();
        try {
            Method removeSyncBarrier = messageQueue.getClass().getDeclaredMethod("removeSyncBarrier", int.class);
            removeSyncBarrier.setAccessible(true);
            removeSyncBarrier.invoke(messageQueue, barrier);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
    

    输出顺序是 4 3 1 5 2

    3、scheduleTraversals 和 doTraversal

    1. scheduleTraversals
    2. postSyncBarrier
    3. mChoreographer.postCallback
    4. Choreographer ... → doCallback
    5. mTraversalRunnable.run → doTraversal
    6. removeSyncBarrier

    这整个过程只有异步消息可以被处理,Choreographer 过程中的动作也都是异步消息,这样可以确保 Choreographer 的顺利运转,也确保了第一时间执行 doTraversal(doTraversal → performTraversals 就是执行 view 的 layout、measure、draw),这个过程中如果有其他同步消息,也无法得到处理,都要等到 doTraversal 之后

    下面这 2 行代码就没有 postSyncBarrier,显然没有 View 绘制的优先级高

    [ViewRootImpl.java→]

    mChoreographer.postCallback(Choreographer.CALLBACK_INPUT, ...
    
    mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, ...
    

    四、举例分析

    以下图为例,分析执行过程

    1. postCallback(runnable1) → scheduleFrameLocked() → scheduleVsync()
    2. frameTimeNanos1 时刻收到 VSync,往 Looper 队列插入一个 msg
    3. Looper 取出 2 的 msg,调用 onVsync(frameTimeNanos1) → doFrame(frameTimeNanos1)
        3.1) 判断没有掉帧
        3.2) doCallbacks 调用 runnable1.run()
    4. postCallback(runnable1) → scheduleFrameLocked() → scheduleVsync()
    5. postCallback(runnable3)
      scheduleFrameLocked() 判断 mFrameScheduled == true 直接 return,所以并不会调用 scheduleVsync()
    6. frameTimeNanos2 时刻收到 VSync,往 Looper 队列插入一个 msg
    7. Looper 取出 6 的 msg,调用 onVsync(frameTimeNanos2) → doFrame(frameTimeNanos2)
      7.1 虽然有延迟,但是延迟没有超过 16 毫秒,判断没有掉帧
      7.2 doCallbacks 调用 runnable2.run() 和 runnable3.run()
    8. 非 UI 线程 postCallback(runnable4) 往 Looper 队列插入一个 msg
    9. frameTimeNanos3 时刻接收不到 VSync,因为自上次收到 VSync 后,未调用过 scheduleVsync()
    10. Looper 取出 8 的 msg,调用 scheduleFrameLocked → scheduleVsync()
    11. frameTimeNanos4 时刻收到 VSync,往 Looper 队列插入一个 msg
    12. Looper 取出 11 的 msg,调用 onVsync(frameTimeNanos4) → doFrame(frameTimeNanos4)
      12.1 延迟超过 16 毫秒,判断掉帧
      12.2 修正 frameTimeNanos,frameTimeNanos = frameTimeNanos5
      12.3 doCallbacks 调用 runnable4.run()



    作者:风风风筝
    链接:https://www.jianshu.com/p/dd32ec35db1d
    来源:简书
    简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

     

    Github上有一个利用Choreographer原理实现的开源库TinyDancer,感兴趣的可以去学习一下。

    展开全文
  • Choreographer

    2019-10-21 14:55:46
    Choreographer Choreographer(编舞者)用来控制同步处理输入(Input)、动画(Animation)、绘制(Draw)三个UI操作。 初始化Choreographer 在View的绘制流程的WindowManagerGlobal.java类中,addView()方法的root = new ...

    Choreographer

    Choreographer(编舞者)用来控制同步处理输入(Input)、动画(Animation)、绘制(Draw)三个UI操作。

    初始化Choreographer

    在View的绘制流程的WindowManagerGlobal.java类中,addView()方法的root = new ViewRootImpl(view.getContext(), display)进行初始化操作,其中的构造方法中mChoreographer = Choreographer.getInstance();

    public static Choreographer getInstance() {
            return sThreadInstance.get();
        }
    

    其中进入sThreadInstance

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

    其中Choreographer choreographer = new Choreographer(looper, VSYNC_SOURCE_APP)为初始化Choreographer。

    Vsync信号

    在构造方法中,mDisplayEventReceiver = USE_VSYNC ? new FrameDisplayEventReceiver(looper, vsyncSource) : null代码进行了FrameDisplayEventReceiver初始化,其中开启了onVsync回调,进行vsync信号的接收。根据屏幕的刷新速率,进行接收vsync信号,然后通过handler进行更新。

    运行流程

    在这里插入图片描述
    通过流程图过程,进入到Choreographer的postCallbackDelayedInternal方法中,将其通过类型mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token)添加到CallbackRecord的单向链表中,其中通过obtainCallbackLocked方法对

    private CallbackRecord obtainCallbackLocked(long dueTime, Object action, Object token) {
            CallbackRecord callback = mCallbackPool;
            if (callback == null) {
                callback = new CallbackRecord();
            } else {
                mCallbackPool = callback.next;
                callback.next = null;
            }
            callback.dueTime = dueTime;
            callback.action = action;
            callback.token = token;
            return callback;
        }
    

    对dueTime,action,token进行赋值。
    其中从ViewRootImplscheduleTraversals方法中,mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null)看到类型为Choreographer.CALLBACK_TRAVERSAL,action为mTraversalRunnable,token为null

    在这里插入图片描述
    其中继续通过scheduleFrameLocked()进入handler的doScheduleVsync()-》scheduleVsyncLocked()-》mDisplayEventReceiver.scheduleVsync()进行开启Vsync信号的分发,然后调用到nativeScheduleVsync()底层native方法中。
    进行vsync信号分发后,在Choreographer中的FrameDisplayEventReceiveronVsync()方法中进行接收,通过mHandler(what没传默认为0)进入doFrame()方法中,其中

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

    调用doCallbacks()方法,其中

    callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
                        now / TimeUtils.NANOS_PER_MS);
                if (callbacks == null) {
                    return;
                }
    

    通过callbackType从mCallbackQueues中进行取值,在前面说过的mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null)
    则得到添加进入的类型为Choreographer.CALLBACK_TRAVERSAL,则可拿到callbacks的值不为null,通过

    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));
                    }
                    c.run(frameTimeNanos);
                }
    

    进入到CallbackRecord

    public void run(long frameTimeNanos) {
                if (token == FRAME_CALLBACK_TOKEN) {
                    ((FrameCallback)action).doFrame(frameTimeNanos);
                } else {
                    ((Runnable)action).run();
                }
            }
    

    run()方法中,之前传入的token为null,则执行((Runnable)action).run()代码,其中action为ViewRootImpl的mTraversalRunnable,执行它的run()方法,

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

    通过doTraversal()->performTraversals(),执行View的绘制步骤performMeasureperformLayoutperformDraw方法进行绘制。

    展开全文
  • 深入分析UI 上层事件处理核心机制 Choreographer 结论写在前面:Choreographer就是一个消息处理器,根据vsync 信号 来计算frame,而计算frame的方式就是处理三种回调,包括事件回调、动画回调、绘制回调。这三种...

    深入分析UI 上层事件处理核心机制 Choreographer

    结论写在前面:Choreographer就是一个消息处理器,根据vsync 信号 来计算frame,而计算frame的方式就是处理三种回调,包括事件回调、动画回调、绘制回调。这三种事件在消息输入、加入动画、准备绘图layout 等动作时均会发给Choreographer。

    下面来看分析过程

    看过一些源码后,发现ui 绘制的管理,调度都是通过Choreographer这个类。

    1 Choreographer 是什么?有什么?

    Choreographer 是个普通类,final 表示不能被继承修改其行为。

    public final class Choreographer
    单例模式持有一个本地进程的单例对象,

        private static final ThreadLocal<Choreographer> sThreadInstance =
                new ThreadLocal<Choreographer>()
    该对象必须持有looper,意味着将使用消息队列

            protected Choreographer initialValue() {
                Looper looper = Looper.myLooper();
                if (looper == null) {
                    throw new IllegalStateException("The current thread must have a looper!");
                }
                return new Choreographer(looper);
            }
    持有一个handler 对象

        private final FrameHandler mHandler;
    这个handler对象仅处理3种事件:

        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();
                        break;
                    case MSG_DO_SCHEDULE_CALLBACK:
                        doScheduleCallback(msg.arg1);
                        break;
                }
            }
        }
    在scheduleFrameLocked中事件被静态变量USE_VSYNC分开,也就是系统仅使用MSG_DO_SCHEDULE_VSYNC或MSG_DO_FRAME。

    查一下概念,的确符合,DO_FRAME 是帧刷新,SCHEDULE_VSYNC是 垂直同步刷新。

    在android4.1上加入的VSYNC 特性,并使用三重缓冲大幅改善了android 图像方面的性能。

    这里有篇文章讲的很清楚:http://www.androidpolice.com/2012/07/12/getting-to-know-android-4-1-part-3-project-butter-how-it-works-and-what-it-added/


        private void scheduleFrameLocked(long now) {
                if (USE_VSYNC) {
                    if (isRunningOnLooperThreadLocked()) {
                        scheduleVsyncLocked();
                    } else {
                        Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
                        msg.setAsynchronous(true);
                        mHandler.sendMessageAtFrontOfQueue(msg);
                    }
                } else {
                    Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
                    msg.setAsynchronous(true);
                    mHandler.sendMessageAtTime(msg, nextFrameTime);
                }
    

    2 从VSYNC 开始case MSG_DO_SCHEDULE_VSYNC:

        void doScheduleVsync() {
            synchronized (mLock) {
                if (mFrameScheduled) {
                    scheduleVsyncLocked();
                }
            }
        }
        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);
            }
        }
    一路调用到nativeScheduleVsync();

    native的东西先不往下看,回头来看mDisplayEventReceiver

        private final class FrameDisplayEventReceiver extends DisplayEventReceiver
                implements Runnable 
    观察一下方法名都是onXX ,一看就是回调的节奏

        // Called from native code.
        @SuppressWarnings("unused")
        private void dispatchVsync(long timestampNanos, int builtInDisplayId, int frame) {
            onVsync(timestampNanos, builtInDisplayId, frame);
        }
    看到这里的注释,就明确了这个类就是被native回调了。回调时调用onVsync();而这个方法时在FrameDisplayEventReceiver中重写的。

            @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 / NANOS_PER_MS);
    
            @Override
            public void run() {
                mHavePendingVsync = false;
                doFrame(mTimestampNanos, mFrame);
            }
    
    在onsync中把自己的runnable 加到消息队列中执行,这里使用异步消息,调用绘制do_frame()方法。

        void doFrame(long frameTimeNanos, int frame) {
    
            synchronized (mLock) {
                //省略一些赋值
                //可能时跳帧的情况,直接调用vsync,并return
                if (frameTimeNanos < mLastFrameTimeNanos) {
                    if (DEBUG) {
                        Log.d(TAG, "Frame time appears to be going backwards.  May be due to a "
                                + "previously skipped frame.  Waiting for next vsync.");
                    }
                    scheduleVsyncLocked();
                    return;
                }
    
            }
            //先后处理事件回调、动画回调、绘制回调
            doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
            doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
            doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
        }
    
    在do_frame()中判断有跳帧可能性,直接继续vsync。

    否则开始新的一帧的绘制,依次处理事件回调、动画回调、绘制回调

    在doCallbacks() 中先根据当前时间去除队列中第一个有效的回调,然后依次处理这些回调。

        void doCallbacks(int callbackType, long frameTimeNanos) {
            CallbackRecord callbacks;
            synchronized (mLock) {
                // We use "now" to determine when callbacks become due because it's possible
                // for earlier processing phases in a frame to post callbacks that should run
                // in a following phase, such as an input event that causes an animation to start.
                final long now = SystemClock.uptimeMillis();
                //按时间取队列头
                callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(now);
                if (callbacks == null) {
                    return;
                }
                mCallbacksRunning = true;
            }
            try {
                for (CallbackRecord c = callbacks; c != null; c = c.next) {
                    if (DEBUG) {
                        Log.d(TAG, "RunCallback: type=" + callbackType
                                + ", action=" + c.action + ", token=" + c.token
                                + ", latencyMillis=" + (SystemClock.uptimeMillis() - c.dueTime));
                    }
                    //回调事件
                    c.run(frameTimeNanos);
                }
            } finally {
                synchronized (mLock) {
                    mCallbacksRunning = false;
                    do {
                        final CallbackRecord next = callbacks.next;
                        recycleCallbackLocked(callbacks);
                        callbacks = next;
                    } while (callbacks != null);
                }
            }
        }

    回调的数据结构是CallbackRecord,一个单向链表。根据token 的标志,来回调doframe 或者runnable
        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();
                }
            }
        }
    从这里可以看到,处理的事件分为两类:一类是doframe 事件,另一类是runnable 方法。接着,就来看一下到底是加的这些事件。

    3. 哪里会使用Choreographer?

    在《Android 动画animation 深入分析http://blog.csdn.net/farmer_cc/article/details/18259117中分析到scheduleAnimation 的时候就是调用的android.view.Choreographer.postCallback(int, Runnable, Object) 方法。查看该方法的调用,在UI 绘制、和其他和动画相关的类中均有调用。这里并没有全部列出来。

    scheduleTraversals() : void - android.view.ViewRootImpl

    scheduleAnimation() : void - android.animation.ValueAnimator.AnimationHandler

    scheduleAnimationLocked() : void - com.android.server.wm.WindowManagerService

    postOnAnimation(Runnable) : void - android.view.View


    在postCallbackDelayedInternal()中 把新来的事件加入到队列中,并根据时间来判断是去frame 还是直接回调

        private void postCallbackDelayedInternal(int callbackType,
                Object action, Object token, long delayMillis) {
            if (DEBUG) {
                Log.d(TAG, "PostCallback: type=" + callbackType
                        + ", action=" + action + ", token=" + token
                        + ", delayMillis=" + delayMillis);
            }
    
            synchronized (mLock) {
                final long now = SystemClock.uptimeMillis();
                final long dueTime = now + delayMillis;
                // 入队列
                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);
                }
            }

    至此,事件的来龙去脉就明确了。Choreographer就是一个消息处理器,根据vsync 信号 来计算frame,而计算frame的方式就是处理三种回调,包括事件回调、动画回调、绘制回调。这三种事件在消息输入、加入动画、准备绘图layout 等动作时均会发给Choreographer。


    写在后面:结合前文《Android 动画animation 深入分析http://blog.csdn.net/farmer_cc/article/details/18259117中可以更多了解动画相关内容

    展开全文
  • Android黄油计划之Choreographer原理解析

    千次阅读 2017-04-11 07:52:53
    搞客户端开发,时间也有点了,但是每次想起来,总感觉自己掌握的东西零零散散,没有一点集在的感觉,应用层的懂,framework的也懂,框架啥的了解一点,分层的思想也有一些,JVM的原理啊,内存分配和管理啊,运行机制...
  • Android系统Choreographer机制实现过程

    千次阅读 2017-01-04 11:56:51
    Android系统Choreographer机制实现过程
  • 对于渲染绘制的分析,我们从ViewRootImpl中的scheduleTraversals方法着手,从这个方法开始触发performTraversals,之后会调用onMeasure,onLayout,onDraw进行界面的绘制。 那么先来看一看scheduleTraversals这个...
  • Android系统从4.1(API 16)开始加入Choreographer这个类来控制同步处理输入(Input)、动画(Animation)、绘制(Draw)三个UI操作。其实UI显示的时候每一帧要完成的事情只有这三种。如下图是官网的相关说明: ...
  • 在Android4.1之后增加了Choreographer机制,用于同Vsync机制配合,控制同步处理输入(Input)、动画(Animation)、绘制(Draw)三个UI操作。其实UI显示的时候每一帧要完成的事情只有这三种。如下图是官网的相关说明: ...
  • Choreographer Init frameworks/base/core/java/android/view/ViewRootImpl.java [ViewRootImpl.java–&gt;ViewRootImpl.ViewRootImpl()] public ViewRootImpl(Context context, Display display) { m...
  • 文章目录UI 卡顿定义卡顿原因及常见解决方式VSync定义作用工作原理Buffer 缓存机制单缓存双缓存三缓存Choreographer使用源码分析 UI 卡顿定义 用户角度:APP 操作比较缓慢,响应不及时,列表滑动卡顿,动画刷新不...
  • Android Choreographer源码分析

    千次阅读 2016-10-29 18:18:06
    想系统性的学习一下android动画,但是在学习前,还是需要先了解Choreographer,这样才能更好的了解动画
  • android.view.Choreographer

    2018-06-28 09:07:20
    /* * Copyright (C) 2011 The Android Open ... * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * Yo...
  • Android Choreographer

    千次阅读 2017-11-01 18:42:06
    引言之前其实并未关注过Choreographer,在一次调试App demo的过程中,偶然发现出现了一条这样的日志: I/Choreographer: Skipped 1201 frames! The application may be doing too much work on its main thread. 这...
  • 简单来说,Choreographer主要作用是协调动画,输入和绘制的时间,它从显示子系统接收定时脉冲(例如垂直同步),然后安排渲染下一个frame的一部分工作。从FrameCallback理解Choreographer原理,简单帧率监控应用

空空如也

1 2 3 4 5 ... 20
收藏数 3,663
精华内容 1,465
热门标签
关键字:

choreographer