【源码简析】ObjectAnimator & ValueAnimator

【疑问1】ObjectAnimator如何通过"translationX"这样实现的动画

【疑问2】ValueAnimator在INFINITE模式下,为什么cancel和end会失效(误会?其他未知bug?)

 

【1】ObjectAnimator.ofFloat(...)着手

         先附上创建ObjectAnimator的源码

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

(1)
    private ObjectAnimator(Object target, String propertyName) {
         //->(2)
         setTarget(target);
         //->(3)
         setPropertyName(propertyName);
    }

(2)
    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;
        }
    }
(3)
    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;
    }

代码(2)setTarget是设置目标View

代码(3)setPropertyName是设置具体属性

设置完 目标View 和 目标属性 后略过其他,直接分析start

    public void start() {
        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();
    }

ObjectAnimator是ValueAnimator的子类,super.start()就是ValueAnimator的start()

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

    //playBackwards是false,表示不reverse执行动画
    private void start(boolean playBackwards) {
        if (Looper.myLooper() == null) {
            throw new AndroidRuntimeException("Animators may only be run on Looper threads");
        }
        mReversing = playBackwards;
        mSelfPulse = !mSuppressSelfPulseRequested;
        // Special case: reversing from seek-to-0 should act as if not seeked at all.
        if (playBackwards && mSeekFraction != -1 && mSeekFraction != 0) {
            if (mRepeatCount == INFINITE) {
                // Calculate the fraction of the current iteration.
                float fraction = (float) (mSeekFraction - Math.floor(mSeekFraction));
                mSeekFraction = 1 - fraction;
            } else {
                mSeekFraction = 1 + mRepeatCount - mSeekFraction;
            }
        }
(1)
        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;
(2)
        addAnimationCallback(0);
(3)
        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);
            }
        }
    }

(1)此时mStarted为true,mRunning为false。说明属性动画的Start状态先于Running状态,符合后面的注释描述。

(2)addAnimationHandler中涉及到Choreographer,以此实现动画。

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

    //每个线程持有一个单例,基于ThreadLocal实现的
    public AnimationHandler getAnimationHandler() {
        return AnimationHandler.getInstance();
    }

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

第一个start,会执行代码: getProvider().postFrameCallback(mFrameCallback); 

追踪代码,发现getProvider()会得到MyFrameCallbackProvider的实例类,从这里就涉及到Choreographer了

private class MyFrameCallbackProvider implements AnimationFrameCallbackProvider {

        final Choreographer mChoreographer = Choreographer.getInstance();

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

        ...
    }

这里不分析Choreographer,记住callback是mFrameCallback。后续流程会将mFrameCallback加入Choreographer中的mCallbackQueues,并开始监听下一个Vsync信号(当信号来到时就会处理动画逻辑)。追踪代码的过程中会有这么一行代码,后面分析会涉及到。

postCallbackDelayedInternal(CALLBACK_ANIMATION,callback, FRAME_CALLBACK_TOKEN, delayMillis);
private final class FrameDisplayEventReceiver extends DisplayEventReceiver
            implements Runnable {
        private boolean mHavePendingVsync;
        private long mTimestampNanos;
        private int mFrame;

        public FrameDisplayEventReceiver(Looper looper, int vsyncSource) {
            super(looper, vsyncSource);
        }

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

            mTimestampNanos = timestampNanos;
            mFrame = frame;
            //利用同步屏障,立即执行run()方法
            Message msg = Message.obtain(mHandler, this);
            msg.setAsynchronous(true);
            mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
        }

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

接收信号,走到onVsync中,并通过Handler执行到run() -> doFrame(...)方法。在doFrame中,会调用doCallbacks(...)

    void doFrame(long frameTimeNanos, int frame) {
        final long startNanos;
        ...

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

        ...
    }

void doCallbacks(int callbackType, long frameTimeNanos) {
        CallbackRecord callbacks;
        ...
        try {
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, CALLBACK_TRACE_TITLES[callbackType]);
            for (CallbackRecord c = callbacks; c != null; c = c.next) {
                if (DEBUG_FRAMES) {
                    Log.d(TAG, "RunCallback: type=" + callbackType
                            + ", action=" + c.action + ", token=" + c.token
                            + ", latencyMillis=" + (SystemClock.uptimeMillis() - c.dueTime));
                }
                c.run(frameTimeNanos);
            }
        }
        ...
    }

    private static final class CallbackRecord {
        public CallbackRecord next;
        public long dueTime;
        public Object action; // Runnable or FrameCallback
        public Object token;

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

doCallbacks(...)又会进一步执行mCallbackQueues中的“callback”。对于ObjectAnimator,我们本次仅关注Choreographer.CALLBACK_ANIMATION类型的callback。类似于Handler的Message,根据时间判断,获取run的callback并按顺序执行。上面说到会涉及到这段代码,这里就用到了

postCallbackDelayedInternal(CALLBACK_ANIMATION,callback, FRAME_CALLBACK_TOKEN, delayMillis);

因为FRAME_CALLBACK_TOKEN,所以最终会回归到之前的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);
            }
        }
    };

doAnimationFrame中执行一些动画状态相关的处理,并通过boolean finished = animateBasedOnTime(currentTime);计算属性值,所以属性动画是通过ANIMATION类型的callback不断计算属性值。

doAnimationFrame执行完后,根据mAnimationCallbacks判断是否还有需要继续执行的属性动画,如果有,则继续利用getProvider().postFrameCallback(this); 循环监听Vsync信号更新界面。而

这里涉及到一点,就是只有当mAnimationCallbacks的size为0时,才会将callback加入Choreographer,后续的都是在复用之前的同一个Vsync信号。

为了让动画真正执行,肯定需要不断计算更新view的属性,并触发绘制。回到最开始,ValueAnimator的start(...)方法中,接着addAnimationCallback往后执行。

private void start(boolean playBackwards) {
        ...
        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);
            }
        }
    }

mStartDelay是否延迟

mSeekFraction执行百分比

mReversing动画是否反转执行

对于默认情况 最开始 mSeekFraction为-1,mReversing为false,而mStartDelay为为0,所以会走进逻辑。但是当我们设置delay延迟后,就走不进来了?这时可以看看doAnimationFrame会发现里面有段逻辑是专门处理这种情况。当然还会有各种各样的情况,等遇到时,再补上分析的代码吧。接着分析调用流程  startAnimation() -> initAnimation() -> notifyStartListeners()

    //ObjectAnimator.java
    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) {
                    //设置getter,setter方法
                    mValues[i].setupSetterAndGetter(target);
                }
            }
            super.initAnimation();
        }
    }    
    
    //ValueAnimator.java
    void initAnimation() {
        if (!mInitialized) {
            int numValues = mValues.length;
            for (int i = 0; i < numValues; ++i) {
                mValues[i].init();
            }
            mInitialized = true;
        }
    }

mValues是创建ObjectAnimator示例时生成的 PropertyValuesHolder[] 数组,那么往下追踪PropertyValuesHolder的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);
        }
    }

就是为每个属性设置估值器,暂不深入。再之后就是调用setCurrentPlayTime,其实内部也是调用setCurrentFraction

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

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

计算fraction信息,调用animateValue

    //ObjectAnimator.java
    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) {
            //实际就是调用对应的setter方法
            mValues[i].setAnimatedValue(target);
        }
    }

    //ValueAnimator.java
    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);
            }
        }
    }

对于ObjectAnimator,mValues不为空,mUpdatelisteners为空,所以关键代码是 mValues[i].calculateValue(fraction);

对于ValueAnimator,使用时会addUpdateListener,所以mUpdateListeners不为空。

所以对于ObjectAnimator.ofFloat(...),对于每个属性,调用calculateValue计算属性值,再调用setAnimatedValue调用setter方法。在setter方法中又会调用invalidate触发重新绘制。到这里ObjectAnimator的绘制流程大致就分析完了,其实这里还欠缺生成ObjectAnimator示例的分析。

【疑问2】上述分析其实已经涉及到ValueAnimator动画的一些流程。那么分析cancel和end应该就可以得到想要的结果。

    public void cancel() {
        ...
        endAnimation();

    }

    public void end() {
        ...
        endAnimation();
    }

关键要分析endAnimation方法

    private void endAnimation() {
        if (mAnimationEndRequested) {
            return;
        }
        removeAnimationCallback();

        mAnimationEndRequested = true;
        mPaused = false;
        boolean notify = (mStarted || mRunning) && mListeners != null;
        if (notify && !mRunning) {
            // If it's not yet running, then start listeners weren't called. Call them now.
            notifyStartListeners();
        }
        mRunning = false;
        mStarted = false;
        mStartListenersCalled = false;
        mLastFrameTime = -1;
        mFirstFrameTime = -1;
        mStartTime = -1;
        if (notify && mListeners != null) {
            ArrayList<AnimatorListener> tmpListeners =
                    (ArrayList<AnimatorListener>) mListeners.clone();
            int numListeners = tmpListeners.size();
            for (int i = 0; i < numListeners; ++i) {
                tmpListeners.get(i).onAnimationEnd(this, mReversing);
            }
        }
        // mReversing needs to be reset *after* notifying the listeners for the end callbacks.
        mReversing = false;
        if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
            Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, getNameForTrace(),
                    System.identityHashCode(this));
        }
    }

其实就是调用Listeners回调,并且重置标志位等一些基本操作。关键是removeCallback,也就是将当前ValueAnimator从列表中移除,那么后续Choreographer处理时,就不会再执行动画。有意思的事情是,当我分析完这部分代码。再次运行代码验证时,疑问2的问题竟然不再复现了。。。

 

 

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值