屬性動畫源碼分析

* 本篇文章已授權微信公眾號 guolin_blog (郭霖)獨家發(fā)布
關于屬性動畫的介紹有很多,但是大部分都是介紹如何使用屬性動畫允跑。
本文通過追溯源碼澄惊,剖析屬性動畫內部實現機制。

屬性動畫有兩個比較重要的動畫執(zhí)行類

ObjectAnimator
ValueAnimator

其中 ObjectAnimator 是 ValueAnimator 的子類料饥。
ObjectAnimator 對 ValueAnimator 做了一層封裝,使得 api 變得更簡單朱监。所以這里我們選取 ObjectAnimator 作為研究對象岸啡。

使用方式

    ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(animationView, "X", 0, 500);
    objectAnimator.setInterpolator(new LinearInterpolator());
    objectAnimator.setEvaluator(new FloatEvaluator());
    objectAnimator.setDuration(5 * 1000);
    objectAnimator.start();

以上是一個簡單的 ObjectAnimator 實例。我們設置了一個時長為 5 秒赫编,勻速移動 500 像素的 View巡蘸。

一共有 5 個步驟

1. 創(chuàng)建 ObjectAnimator
2. 設置 Interpolator (插值器)
3. 設置 Evaluator(估值器)
4. 設置動畫時長
5. 開啟動畫

其中 『2』『3』 兩步比較簡單,但是概念比較難理解擂送。

1. Interpolator (插值器) 
   用來計算某一時間點對應動畫播放長度的百分比悦荒。
   例如:LinearInterpolator 表示一個勻速變化的動畫
        AccelerateInterpolator 表示一個先加速后減速的動畫
   
   Interpolator 會返回一個值,范圍為 0 ~ 1 表示一個百分比

2. Evaluator(估值器)
   表示計算某個時間點嘹吨,動畫需要更新 view 的值搬味。
   Evaluator.evaluate(float fraction, T startValue, T endValue) 是核心方法
   其中
       fraction 表示一個百分比
       startValue 和 endValue 表示動畫的起始值和結束值。
       
   通過 fraction、startValue碰纬、endValue 計算 view 對應的屬性位置萍聊。

分析源碼

從上面的代碼看,我們重點需要看兩個步驟

1. 創(chuàng)建  ObjectAnimator
2. 開啟動畫
   動畫都是一些列的重復繪制悦析,所以我們要找到負責重復繪制的代碼

創(chuàng)建 ObjectAnimator

ObjectAnimator.ofFloat(animationView, "X", 0, 500)

ofFloat() 是一個靜態(tài)方法

public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
    ObjectAnimator anim = new ObjectAnimator(target, propertyName);
    anim.setFloatValues(values);
    return anim;
}

該方法由 2 步操作寿桨,創(chuàng)建 ObjectAnimator 、調用 anim.setFloatValues(int... values)

創(chuàng)建 ObjectAnimator
private ObjectAnimator(Object target, String propertyName) {
    setTarget(target);
    setPropertyName(propertyName);
}

setTarget(target) 將 view 保存到若引用 mTarget

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

然后把 view 需要改變的屬性用 mPropertyName 保存

public void setPropertyName(@NonNull String propertyName) {
    ……
    此時這邊部分代碼不會被執(zhí)行
    ……
    mPropertyName = propertyName;
    // New property/values/target should cause re-initialization prior to starting
    mInitialized = false;
}

此時完成 ObjectAnimator 對象創(chuàng)建她按,但是還沒完牛隅。要對 ObjectAnimator 做一些設置。

anim.setFloatValues(int... values)
public void setFloatValues(float... 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.ofFloat(mProperty, values));
        } else {
            setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));
        }
    } else {
        super.setFloatValues(values);
    }
}

此時 mValues 還沒有賦值酌泰,所以會執(zhí)行 PropertyValuesHolder.ofFloat(mPropertyName, values)

public static PropertyValuesHolder ofFloat(String propertyName, float... values) {
    return new FloatPropertyValuesHolder(propertyName, values);
}

這里創(chuàng)建了一個 FloatPropertyValuesHolder 媒佣,所以有必要看下 FloatPropertyValuesHolder 的構造函數

    public FloatPropertyValuesHolder(String propertyName, float... values) {
        super(propertyName);
        setFloatValues(values);
    }

super(propertyName)是調用父類構造函數,里面只是保存了一個 propertyName 代碼就不貼了陵刹。然后看

    public void setFloatValues(float... values) {
        super.setFloatValues(values);
        mFloatKeyframes = (Keyframes.FloatKeyframes) mKeyframes;
    }

O__O "… 有是調用父類方法 super.setFloatValues(values)默伍,這個父類方法是不能跳過的

public void setFloatValues(float... values) {
    mValueType = float.class;
    mKeyframes = KeyframeSet.ofFloat(values);
}

有點剝洋蔥的感覺了,可是還要硬著頭皮繼續(xù)看 KeyframeSet.ofFloat(values)

public static KeyframeSet ofFloat(float... values) {
    boolean badValue = false;
    int numKeyframes = values.length;
    FloatKeyframe keyframes[] = new FloatKeyframe[Math.max(numKeyframes,2)];
    if (numKeyframes == 1) {
        keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f);
        keyframes[1] = (FloatKeyframe) Keyframe.ofFloat(1f, values[0]);
        if (Float.isNaN(values[0])) {
            badValue = true;
        }
    } else {
        keyframes[0] = (FloatKeyframe) Keyframe.ofFloat(0f, values[0]);
        for (int i = 1; i < numKeyframes; ++i) {
            keyframes[i] =
                    (FloatKeyframe) Keyframe.ofFloat((float) i / (numKeyframes - 1), values[i]);
            if (Float.isNaN(values[i])) {
                badValue = true;
            }
        }
    }
    if (badValue) {
        Log.w("Animator", "Bad value (NaN) in float animator");
    }
    return new FloatKeyframeSet(keyframes);
}

(@ο@) 哇~ 終于看到一大坨代碼了衰琐!貌似已經到洋蔥的最里面了也糊,認真研究一下這部分代碼

這里出現一個新的對象 KeyframeSet 和 FloatKeyframe

FloatKeyframe 是 Keyframe 的子類,我們傳入的 values 是一個可變參數羡宙。
KeyframeSet.ofFloat() 會把我們傳入的 values 轉化為一組 FloatKeyframe(因為我們傳入的是一組 float ) 狸剃。

Keyframe 翻譯一下就叫『關鍵幀』。顧名思義狗热,代表動畫執(zhí)行過程中某些特殊的幀钞馁,例如開始、結束匿刮。以及一些中間重要狀態(tài)僧凰。

如果我們傳入的 values 只有 1 個元素,KeyframeSet 則存放兩個『關鍵幀』分別表示 0~values[0]熟丸。

如果我們傳的 values 有多余 1 個元素训措,怎會生成 values.length -1 個關鍵幀。

每個關鍵幀之間過度光羞,就是所謂的『動畫』绩鸣,每次動畫都會觸發(fā) Evaluator.evaluate 的變動

Evaluator.evaluate(float fraction, Number startValue, Number endValue)

如果我們傳入了
ObjectAnimator.ofFloat(animationView, "X", 0, 100, 200, 500);

我們傳入 4 個值,那么我們的動畫將分為 3 個部分

    0 ~ 100 一個階段纱兑, fraction 從 0.0 ~ 1.0全闷;
        startValue = 0,endValue =100
    100 ~ 200 一個階段萍启, fraction 從 0.0 ~ 1.0;
        startValue = 100,endValue =200
    200 ~ 500 一個階段勘纯, fraction 從 0.0 ~ 1.0局服;
        startValue = 200,endValue =500

然后 FloatPropertyValuesHolder 創(chuàng)建部分完畢驳遵,一層一層向上返回淫奔。

(O__O "…,如果忘記這個部分在哪堤结,可以往上翻去看看)

然后執(zhí)行 ObjectAnimator.setValues() ,ObjectAnimator 直接調用了父類的 ValueAnimator.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;
}

然后我們看到 mValues 和 mValuesMap 賦值操作了唆迁。mValues 和 mValuesMap 持有的對象就是我們創(chuàng)建的 FloatPropertyValuesHolder

終于我們完成了 ObjectAnimator 創(chuàng)建過程的源代碼追蹤,總結如下

1. 創(chuàng)建 ObjectAnimator 對象竞穷,保存 target 和 propertyName
2. 根據傳入的 values 創(chuàng)建一組關鍵幀唐责。
3. 關鍵幀封裝到 FloatPropertyValuesHolder 中。
4. FloatPropertyValuesHolder 交給 mValues 和 mValuesMap 持有

總結一下整個流程

animation_01.png

開啟動畫

這一步是整個屬性動畫中最麻煩的操作瘾带,一層一層的剝開源碼鼠哥,搞得我眼睛干澀,脾氣暴躁一度想放棄看政。

所以先做個深呼吸~朴恳,準備迎接更虐心的源碼追蹤。

objectAnimator.start()

public void start() {
    AnimationHandler.getInstance().autoCancelBasedOn(this);
    ……
    super.start();
}

這里有兩句關鍵代碼允蚣,其他代碼沒有貼出來于颖。第一句檢測如果動畫已經執(zhí)行,則停止動畫嚷兔。

如果不理解森渐,并不重要。因為第一次調用的時候谴垫,肯定沒有動畫在執(zhí)行章母。等看完整個過程就明白這一句的意思了。

然后我們發(fā)現又雙叒叕調用了父類的方法翩剪。

public void start() {
    start(false);
}

然后加了一個 false 參數繼續(xù)調用同名函數

private void start(boolean playBackwards) {
    if (Looper.myLooper() == null) {
        throw new AndroidRuntimeException("Animators may only be run on Looper threads");
    }
    mReversing = playBackwards;
    mSelfPulse = !mSuppressSelfPulseRequested;
    …… 這里的代碼此時不會執(zhí)行乳怎,先省略
    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 = -1;
    mFirstFrameTime = -1;
    mStartTime = -1;
    addAnimationCallback(0);

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

start(boolean playBackwards) 方法比較長,仔細咀嚼改方法后前弯,找到三個地方比較重要

1. addAnimationCallback(0)
2. startAnimation()
3. setCurrentPlayTime(0)/setCurrentFraction(mSeekFraction) 其實是一個方法蚪缀,因為 setCurrentPlayTime 會調用 setCurrentFraction(mSeekFraction)

這三個地方『1』是最負責的一步,但是又是最重要的一步恕出,再做一次深呼吸询枚,然后準備跟下去!

addAnimationCallback(0)
private void addAnimationCallback(long delay) {
    if (!mSelfPulse) {
        return;
    }
    getAnimationHandler().addAnimationFrameCallback(this, delay);
}

這里一下子出現兩個方法調用浙巫,先看下 getAnimationHandler()

   public AnimationHandler getAnimationHandler() {
       return AnimationHandler.getInstance();
   }

好像是獲取一個 AnimationHandler 對象金蜀。似乎還是一個單利

public final static ThreadLocal<AnimationHandler> sAnimatorHandler = new ThreadLocal<>();
private boolean mListDirty = false;

public static AnimationHandler getInstance() {
    if (sAnimatorHandler.get() == null) {
        sAnimatorHandler.set(new AnimationHandler());
    }
    return sAnimatorHandler.get();
}

原來 AnimationHandler 用 ThreadLocal 保證每個線程只有一個實例刷后。做到線程中單例。

然后看下 AnimationHandler.addAnimationFrameCallback()

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

執(zhí)行動畫的時候 mAnimationCallbacks.size() = 0 所以會執(zhí)行(這里稍微留意一下傳入的 callback)

getProvider().postFrameCallback(mFrameCallback)

看下 getProvider()

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

返回了一個 MyFrameCallbackProvider() 對象渊抄,而且改對象的構造方法并無特別之處尝胆。然后我們在看下 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);
        }
    }
};

mFrameCallback 又是一個回調方法护桦,而且 doFrame() 方法中好像有傳入了自身含衔。

Σ( ° △ °|||)︴這種反復執(zhí)行的操作,有點像是繪制動畫的控制地方了二庵。

所以我們必須去看一些 MyFrameCallbackProvider.postFrameCallback(mFrameCallback)

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

?? 這里有出現了一個新對象L叭尽!催享!mChoreographer 杭隙。
這家伙是在 MyFrameCallbackProvider 創(chuàng)建的時候就創(chuàng)建的

    final Choreographer mChoreographer = Choreographer.getInstance();

到這里我們必須去看下 mChoreographer 對象是怎么獲得的。

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

這里又出現了 ThreadLocal睡陪,而且還是靜態(tài)對象寺渗。

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

這時候我的腦海中已經一萬頭神獸再奔騰了,還要繼續(xù)跟下去兰迫。必須看下 Choreographer 的構造方法

private Choreographer(Looper looper, int vsyncSource) {
    mLooper = looper;
    mHandler = new FrameHandler(looper);
    mDisplayEventReceiver = USE_VSYNC
            ? new FrameDisplayEventReceiver(looper, vsyncSource)
            : null;
    mLastFrameTimeNanos = Long.MIN_VALUE;

    mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());

    mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
    for (int i = 0; i <= CALLBACK_LAST; i++) {
        mCallbackQueues[i] = new CallbackQueue();
    }
}

這個時候我們可以先歇一歇拉信殊!看到 Handler 了!VN芯小!O(∩_∩)O哈哈~据德,親人啊鳄乏,終于找到你了。

我們知道 Android 中很多地方都是通過 Handler 的消息機制做頻繁刷新棘利。似乎看到了黎明的曙光橱野。

但是這里只是創(chuàng)建了一個 Choreographer 對象,而且沒啥其他特別的動作善玫,所以還有回去看 mChoreographer.postFrameCallback(callback)

public void postFrameCallback(FrameCallback callback) {
    postFrameCallbackDelayed(callback, 0);
}

又是一層包裹

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

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

O__O "… 又又包裹了一層

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

終于我們又找到了 mHandler.sendMessageAtTime() 和我們猜的更加接近了

這里我們直接看 mHandler 中處理 MSG_DO_SCHEDULE_CALLBACK 的地方

public void handleMessage(Message msg) {
        switch (msg.what) {
            ……
            case MSG_DO_SCHEDULE_CALLBACK:
                doScheduleCallback(msg.arg1);
                break;
        }
 }

收到消息以后緊接著調用

void doScheduleCallback(int callbackType) {
    synchronized (mLock) {
        if (!mFrameScheduled) {
            final long now = SystemClock.uptimeMillis();
            if (mCallbackQueues[callbackType].hasDueCallbacksLocked(now)) {
                scheduleFrameLocked(now);
            }
        }
    }
}

獲得了一個時間戳水援,然后執(zhí)行

private void scheduleFrameLocked(long now) {
    if (!mFrameScheduled) {
        mFrameScheduled = true;
        if (USE_VSYNC) {
            ……
            if (isRunningOnLooperThreadLocked()) {
                scheduleVsyncLocked();
            } else {
                Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
                msg.setAsynchronous(true);
                mHandler.sendMessageAtFrontOfQueue(msg);
            }
        }
        ……
    }
}

對著這段代碼經過很長時間的思考,決定之間看 scheduleVsyncLocked()

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

然后我們找到了 jni 方法

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

凸(艸皿艸 ) jni 什么鬼茅郎!老子還怎么跟蹤下去蜗元。好像放棄了~~~(>_<)~~~

都到這里了,咬碎呀接著干

public DisplayEventReceiver(Looper looper, int vsyncSource) {
    if (looper == null) {
        throw new IllegalArgumentException("looper must not be null");
    }

    mMessageQueue = looper.getQueue();
    mReceiverPtr = nativeInit(new WeakReference<DisplayEventReceiver>(this), mMessageQueue,
            vsyncSource);

    mCloseGuard.open("dispose");
}

從這里我們可以得知 mReceiverPtr 就是一個 jni 層指向 DisplayEventReceiver(子類 FrameDisplayEventReceiver) 的指針jni 方法會回調 FrameDisplayEventReceiver.onVsync() 方法系冗。(這里先不管jni層如何實現了)

    public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
        ……
        Message msg = Message.obtain(mHandler, this);
        msg.setAsynchronous(true);
        mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
    }

然后通過 mHandler 調用自身 FrameDisplayEventReceiver.run()

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

然后調用 Choreographer.doFrame()

void doFrame(long frameTimeNanos, int frame) {
    final long startNanos;
    synchronized (mLock) {
        ……

    try {
        Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame");
        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);
    }

    ……
}

此時我們發(fā)現開始執(zhí)行各種 callback 操作了奕扣,先看 doCallbacks()

    void doCallbacks(int callbackType, long frameTimeNanos) {
    CallbackRecord callbacks;
    synchronized (mLock) {
        ……
    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);
    }
}

這里有個比較重要的方法 c.run(frameTimeNanos) 而這個 c 就是 CallbackRecord。我們在使用 mHandler 發(fā)送消息前掌敬,把 FrameCallback 存放到 CallbackRecord 中惯豆。

然后我們看 CallbackRecord.run() 方法池磁。

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

又看到了一個 ((Runnable)action).run() 方法。

這里的 action 就是 MyFrameCallbackProvider.postFrameCallback() 傳入的Choreographer.FrameCallback 即 AnimationHandler.mFrameCallback 對象循帐。

這里我就找到*** 重復繪制動畫 ***的核心代碼框仔。

Choreographer.FrameCallback.doFrame(long frameTimeNanos) 會反復執(zhí)行,達到繪制動畫的效果拄养。
然后我們可以大膽擦次 AnimationHandler.doAnimationFrame() 里面肯定有繪制動畫的邏輯

private void doAnimationFrame(long frameTime) {
    long currentTime = SystemClock.uptimeMillis();
    final int size = mAnimationCallbacks.size();
    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();
}

我們又看到了一個 callback callback.doAnimationFrame(frameTime) .
這里的callback 來自 ValueAnimator.addAnimationCallback()

getAnimationHandler().addAnimationFrameCallback(this, delay)

所以我們需要看下 ValueAnimator.callback.doAnimationFrame()

public final boolean doAnimationFrame(long frameTime) {
    ……直接看最后代碼
    boolean finished = animateBasedOnTime(currentTime);

    if (finished) {
        endAnimation();
    }
    return finished;
}

先憋住勝利的喜悅,我們繼續(xù)看 animateBasedOnTime(currentTime)

boolean animateBasedOnTime(long currentTime) {
    boolean done = false;
    if (mRunning) {
        ……
        animateValue(currentIterationFraction);
    }
    return done;
}

animateValue() 看法方法名好像要到改變動畫屬性的地方

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

這里我們終于看到調用 Interpolation 和 mValues[i].calculateValue(fraction)

void calculateValue(float fraction) {
    Object value = mKeyframes.getValue(fraction);
    mAnimatedValue = mConverter == null ? value : mConverter.convert(value);
}

最后調用 mUpdateListeners.get(i).onAnimationUpdate(this)

用過 ValueAnimator 的人知道

valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            ……
            ……
        }
});

是獲得動畫更新操作的地方银舱。*** 但是我們沒有設置 AnimatorUpdateListener ***

這個時候有點絕望了瘪匿!到底如何把值更新到 view 上!Q傲蟆棋弥!

冷靜!冷靜诚欠!冷靜顽染!

我們剛看到的 animateValue() 方法是 ValueAnimator 中的,而我們說過 ObjectAnimator 繼承了 ValueAnimator 轰绵,不妨去 ObjectAnimator 中看下粉寞,

void animateValue(float fraction) {
    final Object target = getTarget();
    if (mTarget != null && target == null) {
        // We lost the target reference, cancel and clean up. Note: we allow null target if the
        /// target has never been set.
        cancel();
        return;
    }

    super.animateValue(fraction);
    int numValues = mValues.length;
    for (int i = 0; i < numValues; ++i) {
        mValues[i].setAnimatedValue(target);
    }
}

蒼天啊左腔!大地斑罂选!嚇死寶寶了液样,我們剛一直看的都是 super.animateValue() ,先只有一句代碼能拯救我們

mValues[i].setAnimatedValue(target)

還記得 mValues 里面放了什么嗎振亮?是*** FloatPropertyValuesHolder ***

勝利的曙光再次出現了 FloatPropertyValuesHolder.setAnimatedValue(target)

    void setAnimatedValue(Object target) {
        if (mFloatProperty != null) {
            mFloatProperty.setValue(target, mFloatAnimatedValue);
            return;
        }
        if (mProperty != null) {
            mProperty.set(target, mFloatAnimatedValue);
            return;
        }
        if (mJniSetter != 0) {
            nCallFloatMethod(target, mJniSetter, mFloatAnimatedValue);
            return;
        }
        if (mSetter != null) {
            try {
                mTmpValueArray[0] = mFloatAnimatedValue;
                mSetter.invoke(target, mTmpValueArray);
            } catch (InvocationTargetException e) {
                Log.e("PropertyValuesHolder", e.toString());
            } catch (IllegalAccessException e) {
                Log.e("PropertyValuesHolder", e.toString());
            }
        }
    }

O__O "…好像有四個分支,到底改走哪一個鞭莽。而且好像每個條件都不符合胺唤铡!E炫褒搔!

就像風箏斷了線,我們好像跟不下去了~~~(>_<)~~~

再回到開始的時候丹拯,我們說有三個方法比較重要站超,而我們只看了 addAnimationCallback(0) 。

要不我們再看下其他兩個方法

startAnimation();
private void startAnimation() {
    if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
        Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, getNameForTrace(),
                System.identityHashCode(this));
    }

    mAnimationEndRequested = false;
    initAnimation();
    mRunning = true;
    if (mSeekFraction >= 0) {
        mOverallFraction = mSeekFraction;
    } else {
        mOverallFraction = 0f;
    }
    if (mListeners != null) {
        notifyStartListeners();
    }
}

調用 startAnimation() 以后 mRunning 設置為 true乖酬,表示動畫開始執(zhí)行死相。

也就是說,我們上一部分析的沒錯咬像。動畫還沒開始呢算撮!然后看一下 initAnimation()

void initAnimation() {
    if (!mInitialized) {
        int numValues = mValues.length;
        for (int i = 0; i < numValues; ++i) {
            mValues[i].init();
        }
        mInitialized = true;
    }
}

好像調用了 mValues[i].init() 即 FloatPropertyValuesHolder.init()

void init() {
    if (mEvaluator == null) {
        // We already handle int and float automatically, but not their Object
        // equivalents
        mEvaluator = (mValueType == Integer.class) ? sIntEvaluator :
                (mValueType == Float.class) ? sFloatEvaluator :
                null;
    }
    if (mEvaluator != null) {
        // KeyframeSet knows how to evaluate the common types - only give it a custom
        // evaluator if one has been set on this class
        mKeyframes.setEvaluator(mEvaluator);
    }
}

好像也沒啥特別的地方生宛。但是這次我們留了一個心眼,我們發(fā)現 ObjectAnimator 里重載了此方法

void initAnimation() {
    if (!mInitialized) {
        // mValueType may change due to setter/getter setup; do this before calling super.init(),
        // which uses mValueType to set up the default type evaluator.
        final Object target = getTarget();
        if (target != null) {
            final int numValues = mValues.length;
            for (int i = 0; i < numValues; ++i) {
                mValues[i].setupSetterAndGetter(target);
            }
        }
        super.initAnimation();
    }
}

并且調用了 mValues[i].setupSetterAndGetter(target) 即FloatPropertyValuesHolder.setupSetterAndGetter(target)

    void setupSetterAndGetter(Object target) {
        setupSetter(target.getClass());
    }

好像有點意思了~肮柜,繼續(xù)跟進

    void setupSetter(Class targetClass) {
        if (mJniSetter != 0) {
            return;
        }
        synchronized(sJNISetterPropertyMap) {
            HashMap<String, Long> propertyMap = sJNISetterPropertyMap.get(targetClass);
            boolean wasInMap = false;
            if (propertyMap != null) {
                wasInMap = propertyMap.containsKey(mPropertyName);
                if (wasInMap) {
                    Long jniSetter = propertyMap.get(mPropertyName);
                    if (jniSetter != null) {
                        mJniSetter = jniSetter;
                    }
                }
            }
            if (!wasInMap) {
                String methodName = getMethodName("set", mPropertyName);
                calculateValue(0f);
                float[] values = (float[]) getAnimatedValue();
                int numParams = values.length;
                try {
                    mJniSetter = nGetMultipleFloatMethod(targetClass, methodName, numParams);
                } catch (NoSuchMethodError e) {
                    // try without the 'set' prefix
                    try {
                        mJniSetter = nGetMultipleFloatMethod(targetClass, mPropertyName,
                                numParams);
                    } catch (NoSuchMethodError e2) {
                        // just try reflection next
                    }
                }
                if (propertyMap == null) {
                    propertyMap = new HashMap<String, Long>();
                    sJNISetterPropertyMap.put(targetClass, propertyMap);
                }
                propertyMap.put(mPropertyName, mJniSetter);
            }
        }
    }
}

這里終于發(fā)現了天大的秘密O菥恕!审洞!我們會從 sJNISetterPropertyMap 查詢有沒有 setter 方法的 jni 指針莱睁,如果沒有則調用

mJniSetter = nGetMultipleFloatMethod(targetClass, methodName, numParams) 

這里我們終于揭開了一個疑惑,FloatPropertyValuesHolder.setAnimatedValue(target) 中會走

        if (mJniSetter != 0) {
            nCallFloatMethod(target, mJniSetter, mFloatAnimatedValue);
            return;
        }

通過 jni 指針芒澜,修改對應的對象參數仰剿。機 view.setX(float vlaue)

接著執(zhí)行

    if (mListeners != null) {
        notifyStartListeners();
    }

回調通知動畫開始執(zhí)行。這里似乎已經完成了整個 objectAnimator.start()
但是我們還有最后一步痴晦。

setCurrentPlayTime(0)/setCurrentFraction(mSeekFraction)
public void setCurrentPlayTime(long playTime) {
    float fraction = mDuration > 0 ? (float) playTime / mDuration : 1;
    setCurrentFraction(fraction);
}

所以 setCurrentPlayTime(0) 還是會調用 setCurrentFraction(mSeekFraction)

public void setCurrentFraction(float fraction) {
    initAnimation();
    fraction = clampFraction(fraction);
    mStartTimeCommitted = true; // do not allow start time to be compensated for jank
    if (isPulsingInternal()) {
        long seekTime = (long) (getScaledDuration() * fraction);
        long currentTime = AnimationUtils.currentAnimationTimeMillis();
        // Only modify the start time when the animation is running. Seek fraction will ensure
        // non-running animations skip to the correct start time.
        mStartTime = currentTime - seekTime;
    } else {
        // If the animation loop hasn't started, or during start delay, the startTime will be
        // adjusted once the delay has passed based on seek fraction.
        mSeekFraction = fraction;
    }
    mOverallFraction = fraction;
    final float currentIterationFraction = getCurrentIterationFraction(fraction, mReversing);
    animateValue(currentIterationFraction);
}

這里我們發(fā)現有調用了 animateValue(currentIterationFraction)南吮,而上面我們已經知道,animateValue() 是更新 view 屬性的操作,這里又執(zhí)行了一次。

可以理解為在 startAnimation() 之后,立馬執(zhí)行一次 animateValue() 敷搪,因為此時可能 handler 的回調還沒有執(zhí)行到。(個人猜測)

以上就是屬性動畫源碼剖析全過程赡勘。

用一個時序圖總結收尾。

animation_02.png

參考資料

Android 屬性動畫 源碼解析 深入了解其內部實現

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末捞镰,一起剝皮案震驚了整個濱河市拷邢,隨后出現的幾起案子,更是在濱河造成了極大的恐慌悔雹,老刑警劉巖莱没,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件臼隔,死亡現場離奇詭異氨淌,居然都是意外死亡豪筝,警方通過查閱死者的電腦和手機严望,發(fā)現死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進店門梢莽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來置鼻,“玉大人储藐,你說我怎么就攤上這事∷皇牵” “怎么了钙勃?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長聂喇。 經常有香客問我辖源,道長,這世上最難降的妖魔是什么希太? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任克饶,我火速辦了婚禮,結果婚禮上跛十,老公的妹妹穿的比我還像新娘彤路。我一直安慰自己,他們只是感情好芥映,可當我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布洲尊。 她就那樣靜靜地躺著,像睡著了一般奈偏。 火紅的嫁衣襯著肌膚如雪坞嘀。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天惊来,我揣著相機與錄音丽涩,去河邊找鬼。 笑死,一個胖子當著我的面吹牛矢渊,可吹牛的內容都是我干的继准。 我是一名探鬼主播,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼矮男,長吁一口氣:“原來是場噩夢啊……” “哼移必!你這毒婦竟也來了?” 一聲冷哼從身側響起毡鉴,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤崔泵,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后猪瞬,有當地人在樹林里發(fā)現了一具尸體憎瘸,經...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年陈瘦,在試婚紗的時候發(fā)現自己被綠了幌甘。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡痊项,死狀恐怖含潘,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情线婚,我是刑警寧澤,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布盆均,位于F島的核電站塞弊,受9級特大地震影響,放射性物質發(fā)生泄漏泪姨。R本人自食惡果不足惜游沿,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望肮砾。 院中可真熱鬧诀黍,春花似錦、人聲如沸仗处。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽婆誓。三九已至吃环,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間洋幻,已是汗流浹背郁轻。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人好唯。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓竭沫,卻偏偏與公主長得像,于是被迫代替她去往敵國和親骑篙。 傳聞我的和親對象是個殘疾皇子蜕提,可洞房花燭夜當晚...
    茶點故事閱讀 42,925評論 2 344

推薦閱讀更多精彩內容