看啥推荐读物
专栏名称: 蚂蚁金服ProtoTeam
数据前端团队
今天看啥  ›  专栏  ›  蚂蚁金服ProtoTeam

属性动画源码分析

蚂蚁金服ProtoTeam  · 掘金  · 前端  · 2017-12-08 05:47
* 本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布
关于属性动画的介绍有很多,但是大部分都是介绍如何使用属性动画。
本文通过追溯源码,剖析属性动画内部实现机制。

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

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』 两步比较简单,但是概念比较难理解。

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. 创建  ObjectAnimator
2. 开启动画
   动画都是一些列的重复绘制,所以我们要找到负责重复绘制的代码

创建 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)

创建 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) {
    ……
    此时这边部分代码不会被执行
    ……
    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 的变动

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 创建部分完毕,一层一层向上返回。

(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 创建过程的源代码追踪,总结如下

1. 创建 ObjectAnimator 对象,保存 target 和 propertyName
2. 根据传入的 values 创建一组关键帧。
3. 关键帧封装到 FloatPropertyValuesHolder 中。
4. FloatPropertyValuesHolder 交给 mValues 和 mValuesMap 持有

总结一下整个流程

animation_01.png

开启动画

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

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

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) 方法比较长,仔细咀嚼改方法后,找到三个地方比较重要

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

执行动画的时候 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 的回调还没有执行到。(个人猜测)

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

用一个时序图总结收尾。

animation_02.png

参考资料

Android 属性动画 源码解析 深入了解其内部实现




原文地址:访问原文地址
快照地址: 访问文章快照