Android屬性動畫源碼分析(四)

??之前有點忙,有一陣沒寫了;好,今天開始屬性動畫的第四部分,前面三節把屬性動畫的初始化的過程基本分析了一遍,今天就結合之前寫的內容,將屬性動畫是如何進行"start"的,完整的分析一遍。
??如果沒有看過之前的文章,建議看下之前的分析過程,以便可以更好的理解接下來的內容:
[Android屬性動畫源碼分析(一)]
[Android屬性動畫源碼分析(二)]
[Android屬性動畫源碼分析(三)]

        animator.addListener(listener);//動畫監聽器
        animator.setDuration(3000);//每次動畫運行時間
        animator.setInterpolator(new LinearInterpolator());//插值器
        animator.setRepeatCount(ValueAnimator.INFINITE);//重復次數
        animator.start();//動畫開始

??前面四行的源碼很簡單,就不講了,從start開始講起,首先進入start的源碼:

//ObjectAnimator.java
    @Override
    public void start() {
        AnimationHandler.getInstance().autoCancelBasedOn(this);
        ...
        super.start();
    }

??進入這個AnimationHandler看一下:
??官方文檔對它的解釋是:

//AnimatorHandler.java
/**
 * This custom, static handler handles the timing pulse that is shared by all active
 * ValueAnimators. This approach ensures that the setting of animation values will happen on the
 * same thread that animations start on, and that all animations will share the same times for
 * calculating their values, which makes synchronizing animations possible.
 *
 * The handler uses the Choreographer by default for doing periodic callbacks. A custom
 * AnimationFrameCallbackProvider can be set on the handler to provide timing pulse that
 * may be independent of UI frame update. This could be useful in testing.
 *
 * @hide
 */

??該handler主要是用于處理所有活動的屬性動畫共享的“時間脈沖”,這個時間脈沖是通常所理解的屬性動畫的從開始到結束每個時間段的“值”,它還保證了一個動畫的完整播放都是發生在同一個線程,同時,它還使用了一個關鍵的類“Choreographer(編舞者)”來處理周期的回調;另一個類AnimationFrameCallBackProvider用來給handler提供用于UI更新的時間脈沖。(這幾個類先記下,后面再表)

//AnimationHandler.java
public final static ThreadLocal<AnimationHandler> sAnimatorHandler = new ThreadLocal<>();//這個handler的實例對象是一個ThreadLocal對象
public static AnimationHandler getInstance() {
        if (sAnimatorHandler.get() == null) {//為每一個線程創建一個handler對象,這樣做是用于確保一個動畫的開始和結束都在同一個線程
            sAnimatorHandler.set(new AnimationHandler());
        }
        return sAnimatorHandler.get();
    }
//AnimationHandler.java
void autoCancelBasedOn(ObjectAnimator objectAnimator) {
        for (int i = mAnimationCallbacks.size() - 1; i >= 0; i--) {//當前回調還未進行添加,
//所以這個for循環不會走,如果走的話,代表停止之前正在播放的動畫,具體內容大家可以自行研究
            AnimationFrameCallback cb = mAnimationCallbacks.get(i);
            if (cb == null) {
                continue;
            }
            if (objectAnimator.shouldAutoCancel(cb)) {
                ((Animator) mAnimationCallbacks.get(i)).cancel();
            }
        }
    }

??在判斷完是否應該取消之前播放的動畫之后,我們就來到了super.start()方法:

//ValuesAnimator.java
public void start() {
        start(false);//這里傳入了false,代表正序播放動畫
    }
//ValuesAnimator.java
...
        mReversing = playBackwards;
        // Special case: reversing from seek-to-0 should act as if not seeked at all.
        if (playBackwards && mSeekFraction != -1 && mSeekFraction != 0) {//第一個參數就是false,所以不會走這里
            ...
        }
        mStarted = true;//設置當前狀態為start狀態
        mPaused = false;
        mRunning = false;
        // Resets mLastFrameTime when start() is called, so that if the animation was running,
        // calling start() would put the animation in the
        // started-but-not-yet-reached-the-first-frame phase.
        mLastFrameTime = 0;
        AnimationHandler animationHandler = AnimationHandler.getInstance();//之前提到過的handler,這里進行了初始化(源碼在上面)
        animationHandler.addAnimationFrameCallback(this, (long) (mStartDelay * sDurationScale));//(1)將當前動畫對象添加到handler中,(第二個long的參數看初始化的值可知為0)

        if (mStartDelay == 0 || mSeekFraction >= 0) {//由初始化代碼得知mStartDelay == 0,所以這里會走
            // If there's no start delay, init the animation and notify start listeners right away
            // to be consistent with the previous behavior. Otherwise, postpone this until the first
            // frame after the start delay.
            startAnimation();//(2)
            if (mSeekFraction == -1) {//初始化值是-1,這里會走
                // No seek, start at play time 0. Note that the reason we are not using fraction 0
                // is because for animations with 0 duration, we want to be consistent with pre-N
                // behavior: skip to the final value immediately.
                setCurrentPlayTime(0);(3)
            } else {
                //本次不會走這里
                setCurrentFraction(mSeekFraction);
            }
        }

??初始化的時候主要的工作:(1)將當前動畫給了AnimatonHandler;(2)調用了startAnimaton()方法;(3)調用了setCurrentPlayTime()方法,并且傳入了參數0,接下來按順序分析下調用的這三個方法:
(1)

    //AnimatonHandler.java
    /**
     * Register to get a callback on the next frame after the delay.
     */
    public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {
        if (mAnimationCallbacks.size() == 0) {//這里的size是0,所以這里會走
            getProvider().postFrameCallback(mFrameCallback);
        }
        if (!mAnimationCallbacks.contains(callback)) {當前的list還無數據,所以這里會進入
            mAnimationCallbacks.add(callback);//這里添加的是ObjectAnimator對象
        }

        if (delay > 0) {//當前delay是0
            mDelayedCallbackStartTime.put(callback, (SystemClock.uptimeMillis() + delay));
        }
    }

??這個mFrameCallback是誰呢,我們看下它的定義:

private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
        @Override
        public void doFrame(long frameTimeNanos) {
            doAnimationFrame(getProvider().getFrameTime());
            if (mAnimationCallbacks.size() > 0) {
                getProvider().postFrameCallback(this);
            }
        }
    };

??是一個Choreographer#FrameCallBack類型,這個mFrameCallback是怎么運作的呢,在分析完后面的內容在繼續分析,這里先標記下。
??我們在看下getProvider().postFrameCallBack(mFrameCallback);的內容

    //AnimatonHandler.java
    private AnimationFrameCallbackProvider getProvider() {
        if (mProvider == null) {
            mProvider = new MyFrameCallbackProvider();
        }
        return mProvider;
    }
    //MyFrameCallbackProvider.java
     /**
     * Default provider of timing pulse that uses Choreographer for frame callbacks.
     */
    private class MyFrameCallbackProvider implements AnimationFrameCallbackProvider {

        final Choreographer mChoreographer = Choreographer.getInstance();

        @Override
        public void postFrameCallback(Choreographer.FrameCallback callback) {
            mChoreographer.postFrameCallback(callback);
        }

        @Override
        public void postCommitCallback(Runnable runnable) {
            mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT, runnable, null);
        }

        @Override
        public long getFrameTime() {
            return mChoreographer.getFrameTime();
        }

        @Override
        public long getFrameDelay() {
            return Choreographer.getFrameDelay();
        }

        @Override
        public void setFrameDelay(long delay) {
            Choreographer.setFrameDelay(delay);
        }
    }

??由上面可以看到,MyFrameCallBackProvider相當于Choregrapher的代理類,所以接下來這塊的分析我們直接進入Choregrapher類去查看,關于這個類的相關機制,鑒于篇幅有限,我們就踩在巨人的肩膀上,可以參考這篇文章:Android消息機制Looper與VSync的傳播 ,我們只看相關動畫的部分:

//Choreographer.java
public void postFrameCallback(FrameCallback callback) {
        postFrameCallbackDelayed(callback, 0);
    }

??上邊的方法傳入了回調對象,延遲時間為0,進入postFrameCallbackDelayed:

//Choreographer.java
public void postFrameCallbackDelayed(FrameCallback callback, long delayMillis) {
        ...\\省略一些判斷過程
        }

        postCallbackDelayedInternal(CALLBACK_ANIMATION,
                callback, FRAME_CALLBACK_TOKEN, delayMillis);
    }

??在上邊的方法里,最后會調用postCallBackDelayedInternal方法進行最終處理,callBacktype是CALLBACK_ANIMATION,action就是回調對象,token是FRAME_CALLBACK_TOKEN,delayMillis是0:

//Choreographer.java
private void postCallbackDelayedInternal(int callbackType,
            Object action, Object token, long delayMillis) {
        ...
        synchronized (mLock) {
            final long now = SystemClock.uptimeMillis();
            final long dueTime = now + delayMillis;//這里delayMillis = 0
           mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);//回調存入了CALLBACK_ANIMATION隊列中

            if (dueTime <= now) {
                scheduleFrameLocked(now);//由于dueTime == now,這里將被執行,進入垂直同步過程
            } else {
                ...
            }
        }
    }

??由上面的代碼可知,我們將回調對象最終存入了mCallBackQueues[CALLBACK_ANIMATION]中(這是一個存放各個類型的數組,每個數組元素是一個CallbackQueue用于存放不同類型的回調:

//Choreographer.java
private final CallbackQueue[] mCallbackQueues;

??由之前的代碼分析可知,我們播放的動畫是CALLBACK_ANIMATION類型,addCallbackLocked方法我們將在下面進行分析)然后編舞者將會執行一次垂直同步(具體參見相關文章,這里不展開),一系列的過程后,會調用Choregrapher中 CallBackRecord對象的run方法,最終進入doFrame方法:

//Choreographer.java
void doFrame(long frameTimeNanos, int frame) {
       ...
        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);
        }
        ...
    }

(補充,根據VSYNC機制,每次接收到垂直同步信號時,都會經過一系列的分發執行到doFrame方法)
??這里只列出一些關鍵部分,通過這部分可以看出,doFrame方法調用了doCallBacks方法用來處理不同類型的回調,由于不涉及其他三種類型,只看傳入的類型是Choreographer.CALLBACK_ANIMATION,doCallBacks是如何處理的

//Choreographer.java
void doCallbacks(int callbackType, long frameTimeNanos) {
        CallbackRecord callbacks;
        synchronized (mLock) {
            final long now = System.nanoTime();
            callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
                    now / TimeUtils.NANOS_PER_MS);//取出回調方法
            if (callbacks == null) {
                return;
            }
            mCallbacksRunning = true;
           ...
        try {
            ...
            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 {//這里recycle了相關類型的回調
                    final CallbackRecord next = callbacks.next;
                    recycleCallbackLocked(callbacks);
                    callbacks = next;
                } while (callbacks != null);
            }
            ...
        }
    }

??上邊的代碼主要做了兩件事,取出相應類型的“回調隊列”(其實是個鏈表),執行隊列中每個回調的run方法;
先說第一件事,前面說過要分析一下addCallbackLocked方法,正好在這里和extractDueCallbacksLocked一起分析:
這兩個方法都是CallbackQueue里的方法:

mCallbackQueues[CALLBACK_ANIMATION].addCallbackLocked(now, callback, token);//這里只是列出之前的調用及傳參
//Choreographer#CallbackQueue
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;
        }

??上邊的代碼就是按照dueTime的順序將通過obtainCallbackLocked生成的CallbackRecord放到鏈表的合適位置進行存儲(CallbackRecord就是一個鏈表的存儲結構);

//Choreographer#CallbackQueue
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放到了這里
        callback.token = token;
        return callback;
    }

??上邊就是生成一個CallbackRecord的過程,動畫播放的callback放到了CallbackRecord的action中。
??接下來看看取出的時候是怎么做的:

//Choreographer#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 = next;
            return callbacks;
        }

??總體來說,就是將鏈表頭部返回,并且移除了當前時間之后的元素。
??上邊一系列方法看完以后就拿到了callbacks,通過for循環對這個鏈表遍歷執行每個CallbackRecord的run方法:

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

??由前邊的代碼可知,這里的token就是FRAME_CALLBACK_TOKEN,所以將執行doFrame方法,還記得這個action是誰嗎,沒錯,就是前面說要在后面分析的mFrameCallback!
??到這里,getProvider().postFrameCallback(mFrameCallback);的過程就分析完了,總結起來就是,我們的這次animation創建了一個AnimationFrameCallbackProvider,在AnimationHander里添加了一個FrameCallback類型的回調(如果之前沒有的話),這個FrameCallback將通過provider發送給Choregrapher(編舞者),被編舞者封裝為CallbackRecord類型的內容,在一次垂直同步信號分發時,這個callbackRecord將會被調用,并最終調用FrameCallback的doFrame方法,同時,本次animation也添加到了AnimationHander的回調列表中,接下來的一篇文章我們將開始分析mFrameCallback的doFrame方法。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,362評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,577評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 178,486評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,852評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,600評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,944評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,944評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,108評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,652評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,385評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,616評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,111評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,798評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,205評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,537評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,334評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,570評論 2 379

推薦閱讀更多精彩內容

  • Android系統從4.1(API 16)開始加入Choreographer這個類來控制同步處理輸入(Input)...
    DeltaTech閱讀 35,593評論 22 138
  • 1、通過CocoaPods安裝項目名稱項目信息 AFNetworking網絡請求組件 FMDB本地數據庫組件 SD...
    陽明AGI閱讀 16,003評論 3 119
  • 1 背景 不能只分析源碼呀,分析的同時也要整理歸納基礎知識,剛好有人微博私信讓全面說說Android的動畫,所以今...
    未聞椛洺閱讀 2,735評論 0 10
  • 目錄君 離開胖達官府的時侯,拓桑扶著一鳴一個勁的喊爹爹,試圖喚回一點一鳴的記憶,可一鳴瞳孔無神,根本不知道拓桑在說...
    有井閱讀 395評論 3 4
  • 誰說湖面下面也一定平靜? 小小的魚兒雖難以激起太多漣漪, 偶爾的跳動也是它存在的痕跡, 當陽光再次鋪灑大地, 曾經...
    三人行則必有我師焉閱讀 224評論 0 0