深入分析UI 上層事件處理核心機制 Choreographer

深入分析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中可以更多瞭解動畫相關內容

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章