动画框架(2)-属性动画源码分析(Choreographer"编舞者")

主目录见:Android高级进阶知识(这是总目录索引)
 昨天已经简单讲了下使用,今天又是源码分析环节,之前看别人分析的动画源码都是旧版的,因为Choreographer在android 4.1之后引入的,所以在之前的分析中都看不到这个身影。今天我们就来揭开动画的神秘面纱哈。

腻害

一.目标

 之所以分析这篇,也是自己的一个源码癖,总觉得没分析源码有点不完整,还有大家可以通过这个源码更加了解动画的过程怎么走,所以我们今天目标:
1.了解动画的运动过程;
2.隆重了解Choreographer这个类,翻译为编舞者,控制节奏有木有。

二.源码分析

1.ObjectAnimator ofInt()

我们都知道,我们前面一篇《属性动画的基础使用方法》已经讲过基本使用了,那么我们从使用出发,首先从ObjectAnimator的用法开始看,我们挑一个简单点的方法进入:

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

这个方法就几句话,老套路,一句一句来看,首先我们看到第一句实例化了一个ObjectAnimator对象,那我们进去看看这个构造函数做了啥:

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

我们看这里设置了Target即视图,还有就是属性名,我们这里简单看下干了啥,这两个方法,首先看下第一个方法:

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

这个方法很简单,其实就是先获取之前是否设置过Target了,设置过就不设置了,如果没有就判断下这个动画是否开始了,开始了就取消掉,然后将这个target重新设置一下。我们继续看属性怎么设置:

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

这个方法首先判断mValues即PropertyValuesHolder[]数组变量是否已经设置过了,没有设置过直接设置属性名,设置过了则进行关联属性名和PropertyValuesHolder对象。等会就知道PropertyValuesHolder干啥的了。看完第一句我们来看第二句 anim.setIntValues(values)了:

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

我们知道,这边的mProperty 我们还没有在哪里赋值过,这个地方肯定是走的else,所以我们来看看else里面做了啥,我们首先看下PropertyValuesHolder.ofInt()干了啥:

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

我们看到这个里面主要实例化了IntPropertyValuesHolder,所以我们看下:

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

第一句很简单我们就不看了,就是设置propertyName,我们直接看第二句:

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

第一句调用了父类的setIntValues方法,我们直接来看:

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

第一句是设置了PropertyValuesHolder值类型为int因为我们这里调用的是ofInt()方法,然后我们看下KeyframeSet.ofInt()怎么走的:

  public static KeyframeSet ofInt(int... values) {
        int numKeyframes = values.length;
        IntKeyframe keyframes[] = new IntKeyframe[Math.max(numKeyframes,2)];
        if (numKeyframes == 1) {
//如果只有一个值,我们就把这个值当做运动的最终值
            keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f);
            keyframes[1] = (IntKeyframe) Keyframe.ofInt(1f, values[0]);
        } else {
            keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f, values[0]);
            for (int i = 1; i < numKeyframes; ++i) {
//将每一帧都设置进IntKeyframe 数组中
                keyframes[i] =
                        (IntKeyframe) Keyframe.ofInt((float) i / (numKeyframes - 1), values[i]);
            }
        }
        return new IntKeyframeSet(keyframes);
    }

终于到了可以稍微讲下的方法了,不然总是简单地跳来跳去会被看的人打死。第一个numKeyframes == 1说明传进来ofInt的values就一个,那么我们就当做设置进来的值为运动的最终值。如果设置进来有几个值那么我们就设置为几帧放进IntKeyframe 数组然后最后赋值给IntKeyframeSet中的
List<Keyframe> mKeyframes中去。到这里我们的PropertyValuesHolder中的KeyframeSet赋值完了,KeyframeSet中又包含了Keyframe(关键帧)的数组,也就是说我们设置进来的values最后都转化为keyframe的数组。然后我们回去看下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;
    }

因为我们PropertyValuesHolder里面属性已经赋值完成了,现在我们就开始设置这个对象值。上面这段代码也很简单,就是将视图的属性名和PropertyValuesHolder对象关联放进mValuesMap中来即可。

2.ObjectAnimator start()

在上面除了TypeEvaluator(估值器)和TimeInterpolator(插值器)之外,值是都设置了,这两个我们等会可以看下,其实就是设置进去而已,现在因为我们的关键帧都设置完毕了,我们就可以开始运动了:

    @Override
    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();
    }

这个方法就两句代码有用,因为判断里面知识打印而已,第一句如注释所说,我们看到下面super.start()方法,我们知道ObjectAnimator是ValueAnimator的子类,所以我们看ValueAnimator的start()方法:

  private void start(boolean playBackwards) {
        if (Looper.myLooper() == null) {
            throw new AndroidRuntimeException("Animators may only be run on Looper threads");
        }
//这个标志是说明是否循环动画的
        mReversing = playBackwards;
        // Special case: reversing from seek-to-0 should act as if not seeked at all.
        if (playBackwards && mSeekFraction != -1 && mSeekFraction != 0) {
            if (mRepeatCount == INFINITE) {
//这是无限循环动画计算fraction的方式,用到了Math.floor(向下取整数),也就是说mSeekFraction 为1的时候计算下来又变为0,从头开始循环
                // Calculate the fraction of the current iteration.
                float fraction = (float) (mSeekFraction - Math.floor(mSeekFraction));
                mSeekFraction = 1 - fraction;
            } else {
//有限次循环动画计算fraction方式
                mSeekFraction = 1 + mRepeatCount - mSeekFraction;
            }
        }
//这几个就是动画的执行状态的
        mStarted = true;
        mPaused = false;
        mRunning = false;
        mAnimationEndRequested = false;
        // Resets mLastFrameTime when start() is called, so that if the animation was running,
        // calling start() would put the animation in the
        // started-but-not-yet-reached-the-first-frame phase.
        mLastFrameTime = 0;
//#下面代码都非常重要会依次分析
        AnimationHandler animationHandler = AnimationHandler.getInstance();
        animationHandler.addAnimationFrameCallback(this, (long) (mStartDelay * sDurationScale));

        if (mStartDelay == 0 || mSeekFraction >= 0) {
            // If there's no start delay, init the animation and notify start listeners right away
            // to be consistent with the previous behavior. Otherwise, postpone this until the first
            // frame after the start delay.
            startAnimation();
            if (mSeekFraction == -1) {
                // No seek, start at play time 0. Note that the reason we are not using fraction 0
                // is because for animations with 0 duration, we want to be consistent with pre-N
                // behavior: skip to the final value immediately.
                setCurrentPlayTime(0);
            } else {
                setCurrentFraction(mSeekFraction);
            }
        }
    }

这里就是我们启动动画的关键代码了,我们先看AnimationHandler的addAnimationFrameCallback方法,传进去的参数是本身this,也就是ObjectAnimator对象同时这个类实现了AnimationFrameCallback接口,我们记住了,因为等会会回调,我们先跟进这个方法里面吧:

 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明显就是空,因为我们还没给他设置呢,所以我们程序会执行getProvider().postFrameCallback(mFrameCallback)这一句代码,然后我们会把传进来的参数callback添加进mAnimationCallbacks这个List中,postFrameCallback这句代码其实非常关键,我们首先来看getProvider()方法:

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

我们看到getProvider就是获取了MyFrameCallbackProvider对象,那么我们跟进这个对象的postFrameCallback()方法,记住这个方法里面的参数是
mFrameCallback不是我们传进来的参数,不要看错了,我最开始就看错了,这个CallBack是什么呢:

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

因为这个是回调,所以等会才会调用,我们这个地方就留意一下,我们现在进入postFrameCallback方法里面:

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

这个地方我们终于看到我们的主角了mChoreographer(编舞者的对象),我们迫不及待赶紧往Choreographer类里面的postFrameCallback跟进,最终会到下面这个方法 postCallbackDelayedInternal(CALLBACK_ANIMATION,callback, FRAME_CALLBACK_TOKEN, delayMillis)里面:

 private void postCallbackDelayedInternal(int callbackType,
            Object action, Object token, long delayMillis) {
...............
        synchronized (mLock) {
            final long now = SystemClock.uptimeMillis();
            final long dueTime = now + delayMillis;
            mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);

            if (dueTime <= now) {
                scheduleFrameLocked(now);
            } else {
                Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
                msg.arg1 = callbackType;
                msg.setAsynchronous(true);
                mHandler.sendMessageAtTime(msg, dueTime);
            }
        }
    }

我们注意传进来的参数第一个callbackType为CALLBACK_ANIMATION,这个后面会用到,其实这个值是固定的为1,也就是说后面这个mCallbackQueues[callbackType]这个其实是往这个数组的这个位置(这个位置连接着一个单链表结构)加入CallbackRecord,我们看下这个addCallbackLocked干了些啥:

 public void addCallbackLocked(long dueTime, Object action, Object token) {
            CallbackRecord callback = obtainCallbackLocked(dueTime, action, token);
            CallbackRecord entry = mHead;
            if (entry == null) {
                mHead = callback;
                return;
            }
            if (dueTime < entry.dueTime) {
                callback.next = entry;
                mHead = callback;
                return;
            }
            while (entry.next != null) {
                if (dueTime < entry.next.dueTime) {
                    callback.next = entry.next;
                    break;
                }
                entry = entry.next;
            }
            entry.next = callback;
        }

这个方法就是很典型的链表的一些操作,我们先看第一个判断,如果是entry为空,也就是头结点为空,那么我们就把新创建的作为头结点。如果头结点不为空的话,我们看第二个判断我们判断如果开始动画时间早于头结点则采用头部插入的方法把CallbackRecord 插入链表。不然我们就循环这个链表,一个一个比较动画开始时间,然后在合适位置插入。好啦添加完链表,我们回去前面代码if (dueTime <= now) ,因为我们的delayMillis我们没有传值,所以判断肯定是小于等于的,所以我们会走到scheduleFrameLocked方法:

 private void scheduleFrameLocked(long now) {
        if (!mFrameScheduled) {
            mFrameScheduled = true;
            if (USE_VSYNC) {
                // If running on the Looper thread, then schedule the vsync immediately,
                // otherwise post a message to schedule the vsync from the UI thread
                // as soon as possible.
                if (isRunningOnLooperThreadLocked()) {
                    scheduleVsyncLocked();
                } else {
                    Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
                    msg.setAsynchronous(true);
                    mHandler.sendMessageAtFrontOfQueue(msg);
                }
            } else {
                final long nextFrameTime = Math.max(
                        mLastFrameTimeNanos / TimeUtils.NANOS_PER_MS + sFrameDelay, now);
                if (DEBUG_FRAMES) {
                    Log.d(TAG, "Scheduling next frame in " + (nextFrameTime - now) + " ms.");
                }
                Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
                msg.setAsynchronous(true);
                mHandler.sendMessageAtTime(msg, nextFrameTime);
            }
        }
    }

我们看到会判断if(USE_VSYNC),我们知道用的就是VSYNC(垂直同步信号),所以我们代码会走进来,我们看到 if (isRunningOnLooperThreadLocked()) 这句是判断是否当前的looper和我所在的looper一致,一般情况下是成立的,所以我们会调用scheduleVsyncLocked()方法:

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

我们看下mDisplayEventReceiver这个对象是FrameDisplayEventReceiver,所以我们要跟进到这个类的scheduleVsync()方法,懵逼了,发现这个类并没有这个方法,那怎么办,在父类嘛,最后找到了:

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

泪流满面,这个nativeScheduleVsync方法是native方法,线索顿时断了有木有,其实到这里只能去看底层的源码,或者你看到这个方法下面也会有惊喜的发现:

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

看到英文的注释了没有,不要太清晰,我们这里不分析底层c++的源码了,我们直接给出结论,会调用这个方法。然后我们看到onVsync()方法,这个方法在子类FrameDisplayEventReceiver中有实现,我们来看做了啥:

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

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

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

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

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

我们看到最后是发了一个消息,这个消息的obtain方法的第二个参数是一个callback,也就是说这个消息会执行这个回调(如果熟悉消息机制应该知道),这个回调会调用到这个类的run方法,所以我们看到这个类的run()方法:

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

这个方法很简单,就是调用doFrame方法,所以我们直接跟进去,大家不要乱,脑子要清醒,这里我提醒你,这个方法很重要!!!

 void doFrame(long frameTimeNanos, int frame) {
        final long startNanos;
        synchronized (mLock) {
            if (!mFrameScheduled) {
                return; // no work to do
            }
......
            long intendedFrameTimeNanos = frameTimeNanos;
            startNanos = System.nanoTime();
            final long jitterNanos = startNanos - frameTimeNanos;
            if (jitterNanos >= mFrameIntervalNanos) {
//这个判断是判断是否有跳帧情况,我们知道我们的mFrameIntervalNanos系统建议是16ms一帧,如果大于这个值则会出现丢帧的情况,这也是为什么页面会卡顿的原因
                final long skippedFrames = jitterNanos / mFrameIntervalNanos;
                if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) {
                    Log.i(TAG, "Skipped " + skippedFrames + " frames!  "
                            + "The application may be doing too much work on its main thread.");
                }
                final long lastFrameOffset = jitterNanos % mFrameIntervalNanos;
                if (DEBUG_JANK) {
                    Log.d(TAG, "Missed vsync by " + (jitterNanos * 0.000001f) + " ms "
                            + "which is more than the frame interval of "
                            + (mFrameIntervalNanos * 0.000001f) + " ms!  "
                            + "Skipping " + skippedFrames + " frames and setting frame "
                            + "time to " + (lastFrameOffset * 0.000001f) + " ms in the past.");
                }
                frameTimeNanos = startNanos - lastFrameOffset;
            }
//因为之前出现了丢帧的情况,所以这个时间好像还是之前的时间,所以直接等待下一帧
            if (frameTimeNanos < mLastFrameTimeNanos) {
                if (DEBUG_JANK) {
                    Log.d(TAG, "Frame time appears to be going backwards.  May be due to a "
                            + "previously skipped frame.  Waiting for next vsync.");
                }
                scheduleVsyncLocked();
                return;
            }

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

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

前面部分我们已经注释解释了一下,我们现在分析几个doCallbacks()方法,我们看到几个方法分别是:

            mFrameInfo.markInputHandlingStart();
            doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);

            mFrameInfo.markAnimationsStart();
            doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);

            mFrameInfo.markPerformTraversalsStart();
            doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);

            doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);

还记得CALLBACK_ANIMATION这个标志1吗,我们之前在链表中添加回到的时候特地说明了这个CALLBACK_ANIMATION,其他的标志分别对应于Input事件,还有我们说过的这个对应于动画,接下来对应的是视图重绘三个重要回调。我们这里先看看这个doCallbacks()方法干啥:

  void doCallbacks(int callbackType, long frameTimeNanos) {
        CallbackRecord callbacks;
        synchronized (mLock) {
            final long now = System.nanoTime();
            callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
                    now / TimeUtils.NANOS_PER_MS);
            if (callbacks == null) {
                return;
            }
            mCallbacksRunning = true;
.........
        try {
            Trace.traceBegin(Trace.TRACE_TAG_VIEW, CALLBACK_TRACE_TITLES[callbackType]);
            for (CallbackRecord c = callbacks; c != null; c = c.next) {
  .......
                c.run(frameTimeNanos);
            }
        } finally {
            synchronized (mLock) {
                mCallbacksRunning = false;
                do {
                    final CallbackRecord next = callbacks.next;
                    recycleCallbackLocked(callbacks);
                    callbacks = next;
                } while (callbacks != null);
            }
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
    }

这个方法刚进来会取出mCallbackQueues[callbackType],比如我们传进来的callbackType是CALLBACK_ANIMATION(为1),那么我们就会取出这个队列对应的1位置的单链表。extractDueCallbacksLocked这个方法我们不详细看,这个方法就是从单链表中取出符合当前时间执行动画的CallbackRecord,然后从我们当前取出来的这个节点开始遍历分别调用他的run方法:

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

我们之前看到我们的token就是FRAME_CALLBACK_TOKEN,所以我们会调用if里面的代码,而且我们刚才介绍过了这个地方的action就是我们之前传进来的callback,这个地方重新贴一下:

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

所以我们就会回调这里的doFrame,我们看看这里面做了些什么,首先我们看第一个方法doAnimationFrame(),这里getProvider().getFrameTime()获取的是开始的帧时间:

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

这个方法是遍历mAnimationCallbacks,这个是什么呢?这个我们之前已经说过了其实,在ValueAnimator的start(boolean playBackwards)方法里面的animationHandler.addAnimationFrameCallback(this, (long) (mStartDelay * sDurationScale))添加进去的,这个地方this就是ValueAnimator。所以这个地方的回调其实是ValueAnimator里面(实现了AnimationHandler.AnimationFrameCallback接口),且回调接口有两个方法:doAnimationFrame(long frameTime)和commitAnimationFrame(long frameTime)。所以我们很明了,后面遍历调用callback.doAnimationFrame(frameTime)其实就是调用ValueAnimator的这个方法,我们来看看这个方法:

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

        if (finished) {
            endAnimation();
        }
    }

我们看到这个方法得到了当前的时间currentTime然后传进animateBasedOnTime方法,我们看下这个方法干啥了:

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

首先判断是否是running状态,显然我们从前面知道是true的。我们前面不用去认真管,直接看到最后调用animateValue()方法,传进去的参数就是动画执行完成度的比率。我们看下这个方法:

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

我们看到这个方法首先是调用插值器的getInterpolation()方法,这个插值器我们知道我们根据我们传进来的完成度fraction来重新计算,我们如果自定义插值器也就是要重写这个方法。然后遍历mValues这个List(这就是PropertyValuesHolder[]数组),然后调用他的calculateValue()方法:

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

这个方法是获取这个完成度所对应的值。最后回调mUpdateListeners,如果用户注册了这个监听这时候就会被回调。我们看会animateBasedOnTime方法,这个方法里面会返回一个done,如果这个动画已经完成,这个done就会返回true执行endAnimation()方法,如果没完成就返回false,不执行结束方法。我们继续回到AnimationHandler的doAnimationFrame方法中,看到下一句:

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

我们看到这边也是调用getProvider()方法即得到MyFrameCallbackProvider方法,然后调用这个类的postCommitCallback方法,我们一步一步像前面一样跟进去,我们发现也是到达Choreographer类中的postCallbackDelayedInternal()这个方法,然后又会调用addCallbacks方法,我们发现又循环回来了。就这样我们动画就一直计算fraction然后循环执行。
总结:到这里动画的源码分析已经讲解完毕了,因为流程太多,如果分析漏的请见谅,不懂的地方可以提出来,知道的可以解答。

android进化论
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容