你一定看得懂的属性动画源码分析

Posted 郭霖

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了你一定看得懂的属性动画源码分析相关的知识,希望对你有一定的参考价值。



今日科技快讯



作者简介


本篇文章来自 看我眼前007 的投稿。主要介绍了android中属性动画源码相关知识,希望对大家有所帮助!

https://www.jianshu.com/u/f92ca7c0d2df


前言


关于属性动画的介绍有很多,但是大部分都是介绍如何使用属性动画。本文通过追溯源码,剖析属性动画内部实现机制。

属性动画有两个比较重要的动画执行类

  • 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. 创建 ObjectAnimator

  2. 设置 Interpolator (插值器)

  3. 设置 Evaluator(估值器)

  4. 设置动画时长

  5. 开启动画

其中 『2』『3』 两步比较简单,但是概念比较难理解。

  • Interpolator (插值器) 

用来计算某一时间点对应动画播放长度的百分比。例如:LinearInterpolator 表示一个匀速变化的动画。AccelerateInterpolator 表示一个先加速后减速的动画

Interpolator 会返回一个值,范围为 0 ~ 1 表示一个百分比

  • Evaluator(估值器)

表示计算某个时间点,动画需要更新 view 的值。

Evaluator.evaluate(float fraction, T startValue, T endValue) 是核心方法。其中,fraction 表示一个百分比。startValue 和 endValue 表示动画的起始值和结束值。通过 fraction、startValue、endValue 计算 view 对应的属性位置。

分析源码

从上面的代码看,我们重点需要看两个步骤

  • 创建  ObjectAnimator

  • 开启动画

动画都是一些列的重复绘制,所以我们要找到负责重复绘制的代码

  • 创建 ObjectAnimator

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

ofFloat() 是一个静态方法

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

该方法由 2 步操作,创建 ObjectAnimator 、调用 anim.setFloatValues(int... values)

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) { 
    …… 
    此时这边部分代码不会被执行 
    …… 
    mPropertyName = propertyName; 
    // New property/values/target should cause re-initialization prior to starting 
    mInitialized = false; 
}

此时完成 ObjectAnimator 对象创建,但是还没完。要对 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 还没有赋值,所以会执行 PropertyValuesHolder.ofFloat(mPropertyName, values)

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

这里创建了一个 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); 
}

有点剥洋葱的感觉了,可是还要硬着头皮继续看 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 翻译一下就叫『关键帧』。顾名思义,代表动画执行过程中某些特殊的帧,例如开始、结束。以及一些中间重要状态。如果我们传入的 values 只有 1 个元素,KeyframeSet 则存放两个『关键帧』分别表示 0~values[0]。如果我们传的 values 有多余 1 个元素,怎会生成 values.length -1 个关键帧。每个关键帧之间过度,就是所谓的『动画』,每次动画都会触发 Evaluator.evaluate 的变动,然后 FloatPropertyValuesHolder 创建部分完毕,一层一层向上返回。(O__O "…,如果忘记这个部分在哪,可以往上翻去看看),然后执行 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 持有的对象就是我们创建的 FloatPropertyValuesHolder,终于我们完成了  ObjectAnimator 创建过程的源代码追踪,总结如下

  • 创建 ObjectAnimator 对象,保存 target 和 propertyName

  • 根据传入的 values 创建一组关键帧。

  • 关键帧封装到 FloatPropertyValuesHolder 中。

  • FloatPropertyValuesHolder 交给 mValues 和 mValuesMap 持有

总结一下整个流程

你一定看得懂的属性动画源码分析

开启动画

这一步是整个属性动画中最麻烦的操作,一层一层的剥开源码,搞得我眼睛干涩,脾气暴躁一度想放弃。

所以先做个深呼吸~,准备迎接更虐心的源码追踪。

objectAnimator.start()

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

这里有两句关键代码,其他代码没有贴出来。第一句检测如果动画已经执行,则停止动画。

如果不理解,并不重要。因为第一次调用的时候,肯定没有动画在执行。等看完整个过程就明白这一句的意思了。

然后我们发现又双叒叕调用了父类的方法。

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

然后加了一个 false 参数继续调用同名函数

private void start(boolean playBackwards) { 
    if (Looper.myLooper() == null) { 
        throw new AndroidRuntimeException("Animators may only be run on Looper threads"); 
    } 
    mReversing = playBackwards; 
    mSelfPulse = !mSuppressSelfPulseRequested; 
    …… 这里的代码此时不会执行,先省略 
    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) 方法比较长,仔细咀嚼改方法后,找到三个地方比较重要

  • addAnimationCallback(0) 

  • startAnimation()

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

执行动画的时候 mAnimationCallbacks.size() = 0 所以会执行(这里稍微留意一下传入的 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() 方法中好像有传入了自身。

Σ( ° △ °|||)︴这种反复执行的操作,有点像是绘制动画的控制地方了。

所以我们必须去看一些 MyFrameCallbackProvider.postFrameCallback(mFrameCallback)

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

这里有出现了一个新对象!!!mChoreographer 。

这家伙是在 MyFrameCallbackProvider 创建的时候就创建的

final Choreographer mChoreographer = Choreographer.getInstance();

到这里我们必须去看下 mChoreographer 对象是怎么获得的。

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

这里又出现了 ThreadLocal,而且还是静态对象。

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

这时候我的脑海中已经一万头神兽再奔腾了,还要继续跟下去。必须看下 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 了!!!!O(∩_∩)O哈哈~,亲人啊,终于找到你了。

我们知道 Android 中很多地方都是通过 Handler 的消息机制做频繁刷新。似乎看到了黎明的曙光。

但是这里只是创建了一个 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); 
            } 
        } 
    } 
}

获得了一个时间戳,然后执行

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

    …… 
}

此时我们发现开始执行各种 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 发送消息前,把 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) 会反复执行,达到绘制动画的效果。

然后我们可以大胆擦次 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; 
}

先憋住胜利的喜悦,我们继续看 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 上!!!

冷静!冷静!冷静!

我们刚看到的 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 "…好像有四个分支,到底改走哪一个。而且好像每个条件都不符合啊!!!

就像风筝断了线,我们好像跟不下去了~~~(>_<)~~~

再回到开始的时候,我们说有三个方法比较重要,而我们只看了 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,表示动画开始执行。

也就是说,我们上一部分析的没错。动画还没开始呢!然后看一下 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); 
    } 
}

好像也没啥特别的地方。但是这次我们留了一个心眼,我们发现 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()); 
}

好像有点意思了~,继续跟进

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

这里终于发现了天大的秘密!!!我们会从 sJNISetterPropertyMap 查询有没有 setter 方法的 jni 指针,如果没有则调用

mJniSetter = nGetMultipleFloatMethod(targetClass, methodName, numParams)

这里我们终于揭开了一个疑惑,FloatPropertyValuesHolder.setAnimatedValue(target) 中会走

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

通过 jni 指针,修改对应的对象参数。机 view.setX(float vlaue)

接着执行

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

回调通知动画开始执行。这里似乎已经完成了整个 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); 
}

这里我们发现有调用了 animateValue(currentIterationFraction),而上面我们已经知道,animateValue() 是更新 view 属性的操作,这里又执行了一次。

可以理解为在 startAnimation() 之后,立马执行一次 animateValue() ,因为此时可能 handler 的回调还没有执行到。(个人猜测)


总结


以上就是属性动画源码剖析全过程。

用一个时序图总结收尾。

你一定看得懂的属性动画源码分析


欢迎长按下图 -> 识别图中二维码

以上是关于你一定看得懂的属性动画源码分析的主要内容,如果未能解决你的问题,请参考以下文章

小孩都看得懂的推荐系统

人人都能看得懂的数据分析书

人人都能看得懂的数据分析书

Promise之你看得懂的Promise

Promise之你看得懂的Promise

从零复刻王者荣耀活动系统,策划都能看得懂的活动系统,源码奉送