動(dòng)畫框架(2)-屬性動(dòng)畫源碼分析(Choreographer"編舞者")

主目錄見:Android高級(jí)進(jìn)階知識(shí)(這是總目錄索引)
?昨天已經(jīng)簡單講了下使用烤礁,今天又是源碼分析環(huán)節(jié)晌涕,之前看別人分析的動(dòng)畫源碼都是舊版的,因?yàn)镃horeographer在android 4.1之后引入的,所以在之前的分析中都看不到這個(gè)身影饥侵。今天我們就來揭開動(dòng)畫的神秘面紗哈。

膩害

一.目標(biāo)

?之所以分析這篇衣屏,也是自己的一個(gè)源碼癖躏升,總覺得沒分析源碼有點(diǎn)不完整,還有大家可以通過這個(gè)源碼更加了解動(dòng)畫的過程怎么走狼忱,所以我們今天目標(biāo):
1.了解動(dòng)畫的運(yùn)動(dòng)過程膨疏;
2.隆重了解Choreographer這個(gè)類,翻譯為編舞者藕赞,控制節(jié)奏有木有成肘。

二.源碼分析

1.ObjectAnimator ofInt()

我們都知道,我們前面一篇《屬性動(dòng)畫的基礎(chǔ)使用方法》已經(jīng)講過基本使用了斧蜕,那么我們從使用出發(fā)双霍,首先從ObjectAnimator的用法開始看,我們挑一個(gè)簡單點(diǎn)的方法進(jìn)入:

    public static ObjectAnimator ofInt(Object target, String propertyName, int... values) {
        ObjectAnimator anim = new ObjectAnimator(target, propertyName);
        anim.setIntValues(values);
        return anim;
    }

這個(gè)方法就幾句話批销,老套路洒闸,一句一句來看,首先我們看到第一句實(shí)例化了一個(gè)ObjectAnimator對象均芽,那我們進(jìn)去看看這個(gè)構(gòu)造函數(shù)做了啥:

  private ObjectAnimator(Object target, String propertyName) {
        setTarget(target);
        setPropertyName(propertyName);
    }

我們看這里設(shè)置了Target即視圖丘逸,還有就是屬性名,我們這里簡單看下干了啥掀宋,這兩個(gè)方法深纲,首先看下第一個(gè)方法:

  @Override
    public void setTarget(@Nullable Object target) {
        final Object oldTarget = getTarget();
        if (oldTarget != target) {
            if (isStarted()) {
                cancel();
            }
            mTarget = target == null ? null : new WeakReference<Object>(target);
            // New target should cause re-initialization prior to starting
            mInitialized = false;
        }
    }

這個(gè)方法很簡單,其實(shí)就是先獲取之前是否設(shè)置過Target了劲妙,設(shè)置過就不設(shè)置了湃鹊,如果沒有就判斷下這個(gè)動(dòng)畫是否開始了,開始了就取消掉镣奋,然后將這個(gè)target重新設(shè)置一下币呵。我們繼續(xù)看屬性怎么設(shè)置:

   public void setPropertyName(@NonNull String propertyName) {
        // mValues could be null if this is being constructed piecemeal. Just record the
        // propertyName to be used later when setValues() is called if so.
        if (mValues != null) {
            PropertyValuesHolder valuesHolder = mValues[0];
            String oldName = valuesHolder.getPropertyName();
            valuesHolder.setPropertyName(propertyName);
            mValuesMap.remove(oldName);
            mValuesMap.put(propertyName, valuesHolder);
        }
        mPropertyName = propertyName;
        // New property/values/target should cause re-initialization prior to starting
        mInitialized = false;
    }

這個(gè)方法首先判斷mValues即PropertyValuesHolder[]數(shù)組變量是否已經(jīng)設(shè)置過了,沒有設(shè)置過直接設(shè)置屬性名侨颈,設(shè)置過了則進(jìn)行關(guān)聯(lián)屬性名和PropertyValuesHolder對象余赢。等會(huì)就知道PropertyValuesHolder干啥的了芯义。看完第一句我們來看第二句 anim.setIntValues(values)了:

 @Override
    public void setIntValues(int... values) {
        if (mValues == null || mValues.length == 0) {
            // No values yet - this animator is being constructed piecemeal. Init the values with
            // whatever the current propertyName is
            if (mProperty != null) {
                setValues(PropertyValuesHolder.ofInt(mProperty, values));
            } else {
                setValues(PropertyValuesHolder.ofInt(mPropertyName, values));
            }
        } else {
            super.setIntValues(values);
        }
    }

我們知道妻柒,這邊的mProperty 我們還沒有在哪里賦值過扛拨,這個(gè)地方肯定是走的else,所以我們來看看else里面做了啥蛤奢,我們首先看下PropertyValuesHolder.ofInt()干了啥:

   public static PropertyValuesHolder ofInt(Property<?, Integer> property, int... values) {
        return new IntPropertyValuesHolder(property, values);
    }

我們看到這個(gè)里面主要實(shí)例化了IntPropertyValuesHolder鬼癣,所以我們看下:

  public IntPropertyValuesHolder(String propertyName, int... values) {
            super(propertyName);
            setIntValues(values);
        }

第一句很簡單我們就不看了,就是設(shè)置propertyName啤贩,我們直接看第二句:

    @Override
        public void setIntValues(int... values) {
            super.setIntValues(values);
            mIntKeyframes = (Keyframes.IntKeyframes) mKeyframes;
        }

第一句調(diào)用了父類的setIntValues方法待秃,我們直接來看:

  public void setIntValues(int... values) {
        mValueType = int.class;
        mKeyframes = KeyframeSet.ofInt(values);
    }

第一句是設(shè)置了PropertyValuesHolder值類型為int因?yàn)槲覀冞@里調(diào)用的是ofInt()方法,然后我們看下KeyframeSet.ofInt()怎么走的:

  public static KeyframeSet ofInt(int... values) {
        int numKeyframes = values.length;
        IntKeyframe keyframes[] = new IntKeyframe[Math.max(numKeyframes,2)];
        if (numKeyframes == 1) {
//如果只有一個(gè)值痹屹,我們就把這個(gè)值當(dāng)做運(yùn)動(dòng)的最終值
            keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f);
            keyframes[1] = (IntKeyframe) Keyframe.ofInt(1f, values[0]);
        } else {
            keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f, values[0]);
            for (int i = 1; i < numKeyframes; ++i) {
//將每一幀都設(shè)置進(jìn)IntKeyframe 數(shù)組中
                keyframes[i] =
                        (IntKeyframe) Keyframe.ofInt((float) i / (numKeyframes - 1), values[i]);
            }
        }
        return new IntKeyframeSet(keyframes);
    }

終于到了可以稍微講下的方法了章郁,不然總是簡單地跳來跳去會(huì)被看的人打死。第一個(gè)numKeyframes == 1說明傳進(jìn)來ofInt的values就一個(gè)志衍,那么我們就當(dāng)做設(shè)置進(jìn)來的值為運(yùn)動(dòng)的最終值暖庄。如果設(shè)置進(jìn)來有幾個(gè)值那么我們就設(shè)置為幾幀放進(jìn)IntKeyframe 數(shù)組然后最后賦值給IntKeyframeSet中的
List<Keyframe> mKeyframes中去。到這里我們的PropertyValuesHolder中的KeyframeSet賦值完了楼肪,KeyframeSet中又包含了Keyframe(關(guān)鍵幀)的數(shù)組培廓,也就是說我們設(shè)置進(jìn)來的values最后都轉(zhuǎn)化為keyframe的數(shù)組。然后我們回去看下setValues()方法是干了什么:

   public void setValues(PropertyValuesHolder... values) {
        int numValues = values.length;
        mValues = values;
        mValuesMap = new HashMap<String, PropertyValuesHolder>(numValues);
        for (int i = 0; i < numValues; ++i) {
            PropertyValuesHolder valuesHolder = values[i];
            mValuesMap.put(valuesHolder.getPropertyName(), valuesHolder);
        }
        // New property/values/target should cause re-initialization prior to starting
        mInitialized = false;
    }

因?yàn)槲覀働ropertyValuesHolder里面屬性已經(jīng)賦值完成了春叫,現(xiàn)在我們就開始設(shè)置這個(gè)對象值肩钠。上面這段代碼也很簡單,就是將視圖的屬性名和PropertyValuesHolder對象關(guān)聯(lián)放進(jìn)mValuesMap中來即可暂殖。

2.ObjectAnimator start()

在上面除了TypeEvaluator(估值器)和TimeInterpolator(插值器)之外价匠,值是都設(shè)置了,這兩個(gè)我們等會(huì)可以看下呛每,其實(shí)就是設(shè)置進(jìn)去而已踩窖,現(xiàn)在因?yàn)槲覀兊年P(guān)鍵幀都設(shè)置完畢了,我們就可以開始運(yùn)動(dòng)了:

    @Override
    public void start() {
//如果設(shè)置了自動(dòng)取消動(dòng)畫晨横,則遍歷調(diào)用取消
        AnimationHandler.getInstance().autoCancelBasedOn(this);
        if (DBG) {
            Log.d(LOG_TAG, "Anim target, duration: " + getTarget() + ", " + getDuration());
            for (int i = 0; i < mValues.length; ++i) {
                PropertyValuesHolder pvh = mValues[i];
                Log.d(LOG_TAG, "   Values[" + i + "]: " +
                    pvh.getPropertyName() + ", " + pvh.mKeyframes.getValue(0) + ", " +
                    pvh.mKeyframes.getValue(1));
            }
        }
        super.start();
    }

這個(gè)方法就兩句代碼有用洋腮,因?yàn)榕袛嗬锩嬷R(shí)打印而已,第一句如注釋所說手形,我們看到下面super.start()方法啥供,我們知道ObjectAnimator是ValueAnimator的子類,所以我們看ValueAnimator的start()方法:

  private void start(boolean playBackwards) {
        if (Looper.myLooper() == null) {
            throw new AndroidRuntimeException("Animators may only be run on Looper threads");
        }
//這個(gè)標(biāo)志是說明是否循環(huán)動(dòng)畫的
        mReversing = playBackwards;
        // Special case: reversing from seek-to-0 should act as if not seeked at all.
        if (playBackwards && mSeekFraction != -1 && mSeekFraction != 0) {
            if (mRepeatCount == INFINITE) {
//這是無限循環(huán)動(dòng)畫計(jì)算fraction的方式叁幢,用到了Math.floor(向下取整數(shù)),也就是說mSeekFraction 為1的時(shí)候計(jì)算下來又變?yōu)?坪稽,從頭開始循環(huán)
                // Calculate the fraction of the current iteration.
                float fraction = (float) (mSeekFraction - Math.floor(mSeekFraction));
                mSeekFraction = 1 - fraction;
            } else {
//有限次循環(huán)動(dòng)畫計(jì)算fraction方式
                mSeekFraction = 1 + mRepeatCount - mSeekFraction;
            }
        }
//這幾個(gè)就是動(dòng)畫的執(zhí)行狀態(tài)的
        mStarted = true;
        mPaused = false;
        mRunning = false;
        mAnimationEndRequested = 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;
//#下面代碼都非常重要會(huì)依次分析
        AnimationHandler animationHandler = AnimationHandler.getInstance();
        animationHandler.addAnimationFrameCallback(this, (long) (mStartDelay * sDurationScale));

        if (mStartDelay == 0 || mSeekFraction >= 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();
            if (mSeekFraction == -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);
            } else {
                setCurrentFraction(mSeekFraction);
            }
        }
    }

這里就是我們啟動(dòng)動(dòng)畫的關(guān)鍵代碼了曼玩,我們先看AnimationHandler的addAnimationFrameCallback方法鳞骤,傳進(jìn)去的參數(shù)是本身this,也就是ObjectAnimator對象同時(shí)這個(gè)類實(shí)現(xiàn)了AnimationFrameCallback接口黍判,我們記住了豫尽,因?yàn)榈葧?huì)會(huì)回調(diào),我們先跟進(jìn)這個(gè)方法里面吧:

 public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {
        if (mAnimationCallbacks.size() == 0) {
            getProvider().postFrameCallback(mFrameCallback);
        }
        if (!mAnimationCallbacks.contains(callback)) {
            mAnimationCallbacks.add(callback);
        }

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

我們看到第一個(gè)判斷mAnimationCallbacks明顯就是空顷帖,因?yàn)槲覀冞€沒給他設(shè)置呢美旧,所以我們程序會(huì)執(zhí)行g(shù)etProvider().postFrameCallback(mFrameCallback)這一句代碼,然后我們會(huì)把傳進(jìn)來的參數(shù)callback添加進(jìn)mAnimationCallbacks這個(gè)List中贬墩,postFrameCallback這句代碼其實(shí)非常關(guān)鍵榴嗅,我們首先來看getProvider()方法:

   private AnimationFrameCallbackProvider getProvider() {
        if (mProvider == null) {
            mProvider = new MyFrameCallbackProvider();
        }
        return mProvider;
    }

我們看到getProvider就是獲取了MyFrameCallbackProvider對象,那么我們跟進(jìn)這個(gè)對象的postFrameCallback()方法陶舞,記住這個(gè)方法里面的參數(shù)是
mFrameCallback不是我們傳進(jìn)來的參數(shù)嗽测,不要看錯(cuò)了,我最開始就看錯(cuò)了肿孵,這個(gè)CallBack是什么呢:

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

因?yàn)檫@個(gè)是回調(diào)唠粥,所以等會(huì)才會(huì)調(diào)用,我們這個(gè)地方就留意一下停做,我們現(xiàn)在進(jìn)入postFrameCallback方法里面:

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

這個(gè)地方我們終于看到我們的主角了mChoreographer(編舞者的對象)晤愧,我們迫不及待趕緊往Choreographer類里面的postFrameCallback跟進(jìn),最終會(huì)到下面這個(gè)方法 postCallbackDelayedInternal(CALLBACK_ANIMATION,callback, FRAME_CALLBACK_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);
            }
        }
    }

我們注意傳進(jìn)來的參數(shù)第一個(gè)callbackType為CALLBACK_ANIMATION蛉腌,這個(gè)后面會(huì)用到官份,其實(shí)這個(gè)值是固定的為1,也就是說后面這個(gè)mCallbackQueues[callbackType]這個(gè)其實(shí)是往這個(gè)數(shù)組的這個(gè)位置(這個(gè)位置連接著一個(gè)單鏈表結(jié)構(gòu))加入CallbackRecord眉抬,我們看下這個(gè)addCallbackLocked干了些啥:

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

這個(gè)方法就是很典型的鏈表的一些操作贯吓,我們先看第一個(gè)判斷,如果是entry為空蜀变,也就是頭結(jié)點(diǎn)為空悄谐,那么我們就把新創(chuàng)建的作為頭結(jié)點(diǎn)。如果頭結(jié)點(diǎn)不為空的話库北,我們看第二個(gè)判斷我們判斷如果開始動(dòng)畫時(shí)間早于頭結(jié)點(diǎn)則采用頭部插入的方法把CallbackRecord 插入鏈表爬舰。不然我們就循環(huán)這個(gè)鏈表,一個(gè)一個(gè)比較動(dòng)畫開始時(shí)間寒瓦,然后在合適位置插入情屹。好啦添加完鏈表,我們回去前面代碼if (dueTime <= now) 杂腰,因?yàn)槲覀兊膁elayMillis我們沒有傳值垃你,所以判斷肯定是小于等于的,所以我們會(huì)走到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 {
                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.");
                }
                Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
                msg.setAsynchronous(true);
                mHandler.sendMessageAtTime(msg, nextFrameTime);
            }
        }
    }

我們看到會(huì)判斷if(USE_VSYNC),我們知道用的就是VSYNC(垂直同步信號(hào)),所以我們代碼會(huì)走進(jìn)來惜颇,我們看到 if (isRunningOnLooperThreadLocked()) 這句是判斷是否當(dāng)前的looper和我所在的looper一致皆刺,一般情況下是成立的,所以我們會(huì)調(diào)用scheduleVsyncLocked()方法:

  private void scheduleVsyncLocked() {
        mDisplayEventReceiver.scheduleVsync();
    }

我們看下mDisplayEventReceiver這個(gè)對象是FrameDisplayEventReceiver凌摄,所以我們要跟進(jìn)到這個(gè)類的scheduleVsync()方法羡蛾,懵逼了,發(fā)現(xiàn)這個(gè)類并沒有這個(gè)方法锨亏,那怎么辦痴怨,在父類嘛,最后找到了:

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

淚流滿面器予,這個(gè)nativeScheduleVsync方法是native方法浪藻,線索頓時(shí)斷了有木有,其實(shí)到這里只能去看底層的源碼劣摇,或者你看到這個(gè)方法下面也會(huì)有驚喜的發(fā)現(xiàn):

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

看到英文的注釋了沒有珠移,不要太清晰,我們這里不分析底層c++的源碼了末融,我們直接給出結(jié)論钧惧,會(huì)調(diào)用這個(gè)方法。然后我們看到onVsync()方法勾习,這個(gè)方法在子類FrameDisplayEventReceiver中有實(shí)現(xiàn)浓瞪,我們來看做了啥:

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

            if (builtInDisplayId != SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN) {
                scheduleVsync();
                return;
            }

            long now = System.nanoTime();
            if (timestampNanos > now) {
                Log.w(TAG, "Frame time is " + ((timestampNanos - now) * 0.000001f)
                        + " ms in the future!  Check that graphics HAL is generating vsync "
                        + "timestamps using the correct timebase.");
                timestampNanos = now;
            }

            if (mHavePendingVsync) {
                Log.w(TAG, "Already have a pending vsync event.  There should only be "
                        + "one at a time.");
            } else {
                mHavePendingVsync = true;
            }

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

我們看到最后是發(fā)了一個(gè)消息,這個(gè)消息的obtain方法的第二個(gè)參數(shù)是一個(gè)callback巧婶,也就是說這個(gè)消息會(huì)執(zhí)行這個(gè)回調(diào)(如果熟悉消息機(jī)制應(yīng)該知道)乾颁,這個(gè)回調(diào)會(huì)調(diào)用到這個(gè)類的run方法,所以我們看到這個(gè)類的run()方法:

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

這個(gè)方法很簡單艺栈,就是調(diào)用doFrame方法英岭,所以我們直接跟進(jìn)去,大家不要亂湿右,腦子要清醒诅妹,這里我提醒你,這個(gè)方法很重要R闳恕?越啤!

 void doFrame(long frameTimeNanos, int frame) {
        final long startNanos;
        synchronized (mLock) {
            if (!mFrameScheduled) {
                return; // no work to do
            }
......
            long intendedFrameTimeNanos = frameTimeNanos;
            startNanos = System.nanoTime();
            final long jitterNanos = startNanos - frameTimeNanos;
            if (jitterNanos >= mFrameIntervalNanos) {
//這個(gè)判斷是判斷是否有跳幀情況丈莺,我們知道我們的mFrameIntervalNanos系統(tǒng)建議是16ms一幀划煮,如果大于這個(gè)值則會(huì)出現(xiàn)丟幀的情況,這也是為什么頁面會(huì)卡頓的原因
                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;
                if (DEBUG_JANK) {
                    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;
            }
//因?yàn)橹俺霈F(xiàn)了丟幀的情況缔俄,所以這個(gè)時(shí)間好像還是之前的時(shí)間弛秋,所以直接等待下一幀
            if (frameTimeNanos < mLastFrameTimeNanos) {
                if (DEBUG_JANK) {
                    Log.d(TAG, "Frame time appears to be going backwards.  May be due to a "
                            + "previously skipped frame.  Waiting for next vsync.");
                }
                scheduleVsyncLocked();
                return;
            }

            mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos);
            mFrameScheduled = false;
            mLastFrameTimeNanos = frameTimeNanos;
        }

        try {
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame");
            AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);
//我們下面打算分析這個(gè)地方
            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);
        }
............
    }

前面部分我們已經(jīng)注釋解釋了一下器躏,我們現(xiàn)在分析幾個(gè)doCallbacks()方法,我們看到幾個(gè)方法分別是:

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

還記得CALLBACK_ANIMATION這個(gè)標(biāo)志1嗎蟹略,我們之前在鏈表中添加回到的時(shí)候特地說明了這個(gè)CALLBACK_ANIMATION邀桑,其他的標(biāo)志分別對應(yīng)于Input事件,還有我們說過的這個(gè)對應(yīng)于動(dòng)畫科乎,接下來對應(yīng)的是視圖重繪三個(gè)重要回調(diào)。我們這里先看看這個(gè)doCallbacks()方法干啥:

  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 {
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, CALLBACK_TRACE_TITLES[callbackType]);
            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);
            }
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
    }

這個(gè)方法剛進(jìn)來會(huì)取出mCallbackQueues[callbackType]贼急,比如我們傳進(jìn)來的callbackType是CALLBACK_ANIMATION(為1)茅茂,那么我們就會(huì)取出這個(gè)隊(duì)列對應(yīng)的1位置的單鏈表。extractDueCallbacksLocked這個(gè)方法我們不詳細(xì)看太抓,這個(gè)方法就是從單鏈表中取出符合當(dāng)前時(shí)間執(zhí)行動(dòng)畫的CallbackRecord空闲,然后從我們當(dāng)前取出來的這個(gè)節(jié)點(diǎn)開始遍歷分別調(diào)用他的run方法:

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

我們之前看到我們的token就是FRAME_CALLBACK_TOKEN,所以我們會(huì)調(diào)用if里面的代碼走敌,而且我們剛才介紹過了這個(gè)地方的action就是我們之前傳進(jìn)來的callback碴倾,這個(gè)地方重新貼一下:

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

所以我們就會(huì)回調(diào)這里的doFrame,我們看看這里面做了些什么掉丽,首先我們看第一個(gè)方法doAnimationFrame()跌榔,這里getProvider().getFrameTime()獲取的是開始的幀時(shí)間:

  private void doAnimationFrame(long frameTime) {
        int size = mAnimationCallbacks.size();
        long currentTime = SystemClock.uptimeMillis();
        for (int i = 0; i < size; i++) {
            final AnimationFrameCallback callback = mAnimationCallbacks.get(i);
            if (callback == null) {
                continue;
            }
            if (isCallbackDue(callback, currentTime)) {
                callback.doAnimationFrame(frameTime);
                if (mCommitCallbacks.contains(callback)) {
                    getProvider().postCommitCallback(new Runnable() {
                        @Override
                        public void run() {
                            commitAnimationFrame(callback, getProvider().getFrameTime());
                        }
                    });
                }
            }
        }
        cleanUpList();
    }

這個(gè)方法是遍歷mAnimationCallbacks,這個(gè)是什么呢捶障?這個(gè)我們之前已經(jīng)說過了其實(shí)僧须,在ValueAnimator的start(boolean playBackwards)方法里面的animationHandler.addAnimationFrameCallback(this, (long) (mStartDelay * sDurationScale))添加進(jìn)去的,這個(gè)地方this就是ValueAnimator项炼。所以這個(gè)地方的回調(diào)其實(shí)是ValueAnimator里面(實(shí)現(xiàn)了AnimationHandler.AnimationFrameCallback接口)担平,且回調(diào)接口有兩個(gè)方法:doAnimationFrame(long frameTime)和commitAnimationFrame(long frameTime)。所以我們很明了锭部,后面遍歷調(diào)用callback.doAnimationFrame(frameTime)其實(shí)就是調(diào)用ValueAnimator的這個(gè)方法暂论,我們來看看這個(gè)方法:

    public final void doAnimationFrame(long frameTime) {
        AnimationHandler handler = AnimationHandler.getInstance();
  .........
        final long currentTime = Math.max(frameTime, mStartTime);
        boolean finished = animateBasedOnTime(currentTime);

        if (finished) {
            endAnimation();
        }
    }

我們看到這個(gè)方法得到了當(dāng)前的時(shí)間currentTime然后傳進(jìn)animateBasedOnTime方法,我們看下這個(gè)方法干啥了:

  boolean animateBasedOnTime(long currentTime) {
        boolean done = false;
        if (mRunning) {
            final long scaledDuration = getScaledDuration();
            final float fraction = scaledDuration > 0 ?
                    (float)(currentTime - mStartTime) / scaledDuration : 1f;
            final float lastFraction = mOverallFraction;
            final boolean newIteration = (int) fraction > (int) lastFraction;
            final boolean lastIterationFinished = (fraction >= mRepeatCount + 1) &&
                    (mRepeatCount != INFINITE);
            if (scaledDuration == 0) {
                // 0 duration animator, ignore the repeat count and skip to the end
                done = true;
            } else if (newIteration && !lastIterationFinished) {
                // Time to repeat
                if (mListeners != null) {
                    int numListeners = mListeners.size();
                    for (int i = 0; i < numListeners; ++i) {
                        mListeners.get(i).onAnimationRepeat(this);
                    }
                }
            } else if (lastIterationFinished) {
                done = true;
            }
            mOverallFraction = clampFraction(fraction);
            float currentIterationFraction = getCurrentIterationFraction(mOverallFraction);
            animateValue(currentIterationFraction);
        }
        return done;
    }

首先判斷是否是running狀態(tài)拌禾,顯然我們從前面知道是true的取胎。我們前面不用去認(rèn)真管,直接看到最后調(diào)用animateValue()方法蹋砚,傳進(jìn)去的參數(shù)就是動(dòng)畫執(zhí)行完成度的比率扼菠。我們看下這個(gè)方法:

  @CallSuper
    void animateValue(float fraction) {
        fraction = mInterpolator.getInterpolation(fraction);
        mCurrentFraction = fraction;
        int numValues = mValues.length;
        for (int i = 0; i < numValues; ++i) {
            mValues[i].calculateValue(fraction);
        }
        if (mUpdateListeners != null) {
            int numListeners = mUpdateListeners.size();
            for (int i = 0; i < numListeners; ++i) {
                mUpdateListeners.get(i).onAnimationUpdate(this);
            }
        }
    }

我們看到這個(gè)方法首先是調(diào)用插值器的getInterpolation()方法,這個(gè)插值器我們知道我們根據(jù)我們傳進(jìn)來的完成度fraction來重新計(jì)算坝咐,我們?nèi)绻远x插值器也就是要重寫這個(gè)方法循榆。然后遍歷mValues這個(gè)List(這就是PropertyValuesHolder[]數(shù)組),然后調(diào)用他的calculateValue()方法:

   @Override
        void calculateValue(float fraction) {
            mIntAnimatedValue = mIntKeyframes.getIntValue(fraction);
        }

這個(gè)方法是獲取這個(gè)完成度所對應(yīng)的值墨坚。最后回調(diào)mUpdateListeners秧饮,如果用戶注冊了這個(gè)監(jiān)聽這時(shí)候就會(huì)被回調(diào)映挂。我們看會(huì)animateBasedOnTime方法,這個(gè)方法里面會(huì)返回一個(gè)done盗尸,如果這個(gè)動(dòng)畫已經(jīng)完成柑船,這個(gè)done就會(huì)返回true執(zhí)行endAnimation()方法,如果沒完成就返回false泼各,不執(zhí)行結(jié)束方法鞍时。我們繼續(xù)回到AnimationHandler的doAnimationFrame方法中,看到下一句:

             if (mCommitCallbacks.contains(callback)) {
                    getProvider().postCommitCallback(new Runnable() {
                        @Override
                        public void run() {
                            commitAnimationFrame(callback, getProvider().getFrameTime());
                        }
                    });
                }

我們看到這邊也是調(diào)用getProvider()方法即得到MyFrameCallbackProvider方法扣蜻,然后調(diào)用這個(gè)類的postCommitCallback方法逆巍,我們一步一步像前面一樣跟進(jìn)去,我們發(fā)現(xiàn)也是到達(dá)Choreographer類中的postCallbackDelayedInternal()這個(gè)方法莽使,然后又會(huì)調(diào)用addCallbacks方法锐极,我們發(fā)現(xiàn)又循環(huán)回來了。就這樣我們動(dòng)畫就一直計(jì)算fraction然后循環(huán)執(zhí)行芳肌。
總結(jié):到這里動(dòng)畫的源碼分析已經(jīng)講解完畢了灵再,因?yàn)榱鞒烫啵绻治雎┑恼堃娬徱隗裕欢牡胤娇梢蕴岢鰜眙崆ǎ赖目梢越獯稹?/strong>

android進(jìn)化論
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市净薛,隨后出現(xiàn)的幾起案子鸳兽,更是在濱河造成了極大的恐慌,老刑警劉巖罕拂,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件揍异,死亡現(xiàn)場離奇詭異,居然都是意外死亡爆班,警方通過查閱死者的電腦和手機(jī)衷掷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來柿菩,“玉大人戚嗅,你說我怎么就攤上這事∈嗖埃” “怎么了懦胞?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長凉泄。 經(jīng)常有香客問我躏尉,道長,這世上最難降的妖魔是什么后众? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任胀糜,我火速辦了婚禮颅拦,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘教藻。我一直安慰自己距帅,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布括堤。 她就那樣靜靜地躺著碌秸,像睡著了一般。 火紅的嫁衣襯著肌膚如雪悄窃。 梳的紋絲不亂的頭發(fā)上哮肚,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天,我揣著相機(jī)與錄音广匙,去河邊找鬼。 笑死恼策,一個(gè)胖子當(dāng)著我的面吹牛鸦致,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播涣楷,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼分唾,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了狮斗?” 一聲冷哼從身側(cè)響起绽乔,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎碳褒,沒想到半個(gè)月后折砸,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡沙峻,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年睦授,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片摔寨。...
    茶點(diǎn)故事閱讀 40,664評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡去枷,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出是复,到底是詐尸還是另有隱情删顶,我是刑警寧澤,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布淑廊,位于F島的核電站逗余,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏季惩。R本人自食惡果不足惜猎荠,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一坚弱、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧关摇,春花似錦荒叶、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至宪睹,卻和暖如春愁茁,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背亭病。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工鹅很, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人罪帖。 一個(gè)月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓促煮,卻偏偏與公主長得像,于是被迫代替她去往敵國和親整袁。 傳聞我的和親對象是個(gè)殘疾皇子菠齿,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,675評論 2 359

推薦閱讀更多精彩內(nèi)容