ObjectAnimator源码学习

基于源码9.0

本篇文章重点分析动画是怎么动起来的,本篇文章参考了不少的博客和自己许多猜测,难免会有错误之处,在后续的工作学习中会不断的完善。有不到之处和错误之处,还请多多指教。

以改变一个TextView的scaleX为例

ObjectAnimator objectAnimator = ObjectAnimator.ofFloat(tvAnimation, "scaleX", 1.0F, 2.0F);
objectAnimator.setDuration(3000);
objectAnimator.start();

创建动画

对这部分不感兴趣的可以直接跳到开启动画的部分

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

在ObjectAnimator的ofFloat方法注释1处,调用了ObjectAnimator的构造方法

private ObjectAnimator(Object target, String propertyName) {
    //保存要执行动画的对象
    setTarget(target);
    //设置要执行动画的对象的属性
    setPropertyName(propertyName);
}

ObjectAnimator的setTarget方法

 @Override
 public void setTarget(@Nullable Object target) {
      final Object oldTarget = getTarget();
      if (oldTarget != target) {
          if (isStarted()) {
              cancel();
       }
       //将传入的目标对象使用WeakReference包裹,然后赋值给mTarget
       mTarget = target == null ? null : new WeakReference<Object>(target);
       // 设置新的目标对象会导致动画在启动之前重新初始化。
       mInitialized = false;
    }
}

ObjectAnimator的setPropertyName方法

 /**
 * 设置目标对象执行动画的属性
 */
public void setPropertyName(@NonNull String propertyName) {
    // 我们此时只对一个属性做动画,并没有mValues进行赋值,所以此时mValues为null;
    if (mValues != null) {
        PropertyValuesHolder valuesHolder = mValues[0];
        String oldName = valuesHolder.getPropertyName();
        valuesHolder.setPropertyName(propertyName);
        mValuesMap.remove(oldName);
        mValuesMap.put(propertyName, valuesHolder);
    }
    //只用一个属性名来保存要执行动画的属性即可
    mPropertyName = propertyName;
    // 设置新的属性/值/目标对象会导致动画在启动之前重新初始化。
    mInitialized = false;
}

ObjectAnimator对象构建完了,我们继续看一下ObjectAnimator的ofFloat方法的注释2处。调用了ObjectAnimator的setFloatValues方法,这里对mValues进行赋值。

@Override
public void setFloatValues(float... values) {
    //经过setPropertyName方法,我们知道此时mValues == null
    if (mValues == null || mValues.length == 0) {
       if (mProperty != null) {
            setValues(PropertyValuesHolder.ofFloat(mProperty, values));
        } else {
             //注释1处,我们也没有为mProperty赋值,所以会执行这行代码
             setValues(PropertyValuesHolder.ofFloat(mPropertyName, values));
        }
    } else {
           super.setFloatValues(values);
    }
}

在上面方法的注释1处,先看下PropertyValuesHolder的ofFloat方法,这个方法是返回一个PropertyValuesHolder对象

public static PropertyValuesHolder ofFloat(String propertyName, float... values) {
    //构建了一个FloatPropertyValuesHolder对象
    return new FloatPropertyValuesHolder(propertyName, values);
}

FloatPropertyValuesHolder的构造函数

public FloatPropertyValuesHolder(String propertyName, float... values) {
     //调用父类PropertyValuesHolder的构造函数,内部将我们传入的propertyName赋值给mPropertyName
     super(propertyName);
     //调用setFloatValues方法
     setFloatValues(values);
}

FloatPropertyValuesHolder的setFloatValues方法

@Override
public void setFloatValues(float... values) {
    //调用父类PropertyValuesHolder的setFloatValues方法
    super.setFloatValues(values);
    //将获取到的一组关键帧赋值给mFloatKeyframes
    mFloatKeyframes = (Keyframes.FloatKeyframes) mKeyframes;
}

PropertyValuesHolder的setFloatValues方法

public void setFloatValues(float... values) {
    //float类型
    mValueType = float.class;
    //获取关键帧
    mKeyframes = KeyframeSet.ofFloat(values);
}

KeyframeSet的ofFloat方法

public static KeyframeSet ofFloat(float... values) {
    boolean badValue = false;
    //我们传入的values长度为2
    int numKeyframes = values.length;
    //keyframes的长度最少为2
    FloatKeyframe keyframes[] = new FloatKeyframe[Math.max(numKeyframes,2)];
    //传入的values只有一个值
    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 {
        //values长度大于1,values[0]作为动画开始时候的
        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;
            }
        }
    }
    //返回有一组关键帧的FloatKeyframeSet对象
    return new FloatKeyframeSet(keyframes); }

KeyframeSet的ofFloat方法就是根据我们初始化动画的时候传入的values数组的构建一组关键帧。

到此,PropertyValuesHolder的构造方法走完了。

然后我们回到ObjectAnimator的setFloatValues方法的注释1处,调用setValues()方法 并传入构建好的FloatPropertyValuesHolder对象。ObjectAnimator 直接调用了父类ValueAnimator的setValues方法

public void setValues(PropertyValuesHolder... values) {
        //value长度为1
        int numValues = values.length;
        //为mValues赋值
        mValues = values;
        mValuesMap = new HashMap<String, PropertyValuesHolder>(numValues);
        for (int i = 0; i < numValues; ++i) {
            PropertyValuesHolder valuesHolder = values[i];
            //将执行动画改变的属性名(在这个例子中就是scaleX)和属性值的持有者保存到HashMap中
            mValuesMap.put(valuesHolder.getPropertyName(), valuesHolder);
        }
        // 设置新的属性/值/目标对象会导致动画在启动之前重新初始化。
        mInitialized = false;
}

ObjectAnimator的setDuration方法

public ObjectAnimator setDuration(long duration) {
    //调用父类方法
    super.setDuration(duration);
    return this;
}
public ValueAnimator setDuration(long duration) {
        //时长必须大于0
        if (duration < 0) {
            throw new IllegalArgumentException("Animators cannot have negative duration: " +
                    duration);
        }
        //为mDuration赋值
        mDuration = duration;
        return this;
    }

到此,ObjectAnimator创建完毕。

启动动画

概括属性动画过程是怎样的,我想可以这样说:改变View的属性,提交重新绘制任务。在下一帧竖直同步信号到来时,重新绘制View树并再次改变View的属性,再次提交重新绘制任务。循环此过程直到动画结束。

下面详细叙述一下


竖直同步信号.png
  1. 我们在第0帧的时候向native层注册成为一个观察者,用来接收下一帧竖直同步信号(信号通知我们是异步的)。

  2. 我们根据时间计算View在第1帧的属性值应该是多少,改变View的属性值并请求重新绘制(请求重新绘制,只是提交一个任务,并等待下一个竖直同步信号到来的时候处理。在同一帧内,如果已经提交了一个绘制任务,那么再次提交绘制任务会被忽略。因为重新绘制,会从ViewRootImpl开始向下重新绘制,可以保证遍历所有的子 View)。

  3. 第1帧的竖直同步信号到来了,我们这时候动画还没有结束,我们继续接收下一帧竖直同步信号。我们根据时间计算View在第2帧的属性值应该是多少,改变View的属性值并请求重新绘制。然后处理第0帧提交的绘制任务,重新绘制View,将计算好的绘制数据保存在一个buffer里面。

  4. 第2帧的竖直同步信号来了,我们这个时候动画还没有结束,我们继续接收下一帧竖直同步信号。我们根据时间计算View在第3帧的属性值应该是多少,改变View的属性值并请求重新绘制。取出第1帧中buffer里面存储的绘制数据显示在屏幕上,然后处理第1帧提交的绘制任务,重新绘制View,将计算好的绘制数据保存在一个buffer里面。

  5. 第3帧的竖直同步信号来了,我们这个时候动画还没有结束,我们继续接收下一帧竖直同步信号。我们根据时间计算View在第4帧的属性值应该是多少,改变View的属性值并请求重新绘制。取出第2帧中buffer里面存储的绘制数据显示在屏幕上,然后处理第2帧提交的绘制任务,重新绘制View,将计算好的绘制数据保存在一个buffer里面。

  6. 如此重复

  7. 到了动画结束的时间,我们取消注册,不再接收下一帧的竖直同步信号,也就不会导致buffer里面的绘制数据更新,这个时候屏幕上显示的就一直是相同的绘制数据了,界面不会再发生变化。

ObjectAnimator启动的关键流程 .jpg

源码分析

ObjectAnimator的start方法

@Override
public void start() {
    //检测如果动画已经执行,则停止动画。
    AnimationHandler.getInstance().autoCancelBasedOn(this);
    //...
    //调用父类的start方法
    super.start();
}

ValueAnimator的start方法

@Override
public void start() {
    //调用重载函数
    start(false);
}

ValueAnimato的start(boolean playBackwards) 方法精简版

/**
 * @param playBackwards 标志动画是否应该反向播放
 */
private void start(boolean playBackwards) {
    //开启动画的线程要有Looper对象
    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;
    //... 
    //注释1处,
    addAnimationCallback(0);
    if (mStartDelay == 0 || mSeekFraction >= 0 || mReversing) {
          //注释2处,如果没有启动延迟,就立即启动动画 
          startAnimation();
          if (mSeekFraction == -1) {
          //注释3处,没有快进,从0开始播放动画 
          setCurrentPlayTime(0);
        } else {
            setCurrentFraction(mSeekFraction);
        }
    }
}

在ValueAnimator的start(boolean playBackwards) 方法的注释1处,调用了addAnimationCallback方法

private void addAnimationCallback(long delay) {
    if (!mSelfPulse) {
        return;
    }
    //添加一个AnimationFrameCallback,ValueAnimator实现了AnimationFrameCallback接口
    getAnimationHandler().addAnimationFrameCallback(this, delay);
}

getAnimationHandler()返回的是一个AnimationHandler对象。注意一下AnimationHandler对象是用线程本地变量来保存的。

AnimationHandler的addAnimationFrameCallback方法精简版

public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {
    //debug的时候发现如果是 Button的话,这个mAnimationCallbacks.size==2,如果是TextView的话,
    //mAnimationCallbacks.size==0考虑这个是Button点击有水波纹的动画,
    //暂时不去管mAnimationCallbacks.size!=0的情况
    if (mAnimationCallbacks.size() == 0) {
        //注释1处,注意一下这个mFrameCallback对象
        getProvider().postFrameCallback(mFrameCallback);
    }
    //注释2处,把传入的回调添加到mAnimationCallbacks中判断是否存在,避免重复添加
    if (!mAnimationCallbacks.contains(callback)) {
       mAnimationCallbacks.add(callback);
    }
    //...
}

AnimationHandler的addAnimationFrameCallback方法注释1处,getProvider方法返回的是一个MyFrameCallbackProvider对象

private AnimationFrameCallbackProvider getProvider() {
   //debug的时候发现这个时候mProvider已经不为null了,指向了一个MyFrameCallbackProvider实例,
   //猜测是因为在主线程其他地方已经初始化过了
    if (mProvider == null) {
        mProvider = new MyFrameCallbackProvider();
    }
    return mProvider;
}

MyFrameCallbackProvider的postFrameCallback方法

private class MyFrameCallbackProvider implements AnimationFrameCallbackProvider {
    //获取Choreographer对象
   final Choreographer mChoreographer = Choreographer.getInstance();

   @Override
   public void postFrameCallback(Choreographer.FrameCallback callback) {
       //调用Choreographer的postFrameCallback方法
       mChoreographer.postFrameCallback(callback);
   }
   //...
}

注意一下这个mFrameCallback对象

private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
    @Override
    public void doFrame(long frameTimeNanos) {
        //注释1处
        doAnimationFrame(getProvider().getFrameTime());
        if (mAnimationCallbacks.size() > 0) {
        //注释2处,如果mAnimationCallbacks.size() > 0,再次调用
        //MyFrameCallbackProvider的postFrameCallback方法,并把mFrameCallback传进去了
        //注意这里的this就是mFrameCallback
        getProvider().postFrameCallback(this);
     }
   }
};

Choreographer类的postFrameCallback方法

public void postFrameCallback(FrameCallback callback) {
    //调用重载函数,传入的延迟是0毫秒
    postFrameCallbackDelayed(callback, 0);
}

Choreographer类的postFrameCallbackDelayed方法

public void postFrameCallbackDelayed(FrameCallback callback, long delayMillis) {
   //...        
   postCallbackDelayedInternal(CALLBACK_ANIMATION,callback, FRAME_CALLBACK_TOKEN, delayMillis);
}

注意一下我们传入postCallbackDelayedInternal方法的4个参数
callbackType:CALLBACK_ANIMATION
action:mFrameCallback
token:FRAME_CALLBACK_TOKEN
delayMillis: 0

Choreographer类的postCallbackDelayedInternal方法

private void postCallbackDelayedInternal(int callbackType,Object action, Object token, long delayMillis) {
    synchronized (mLock) {
        //开机以来的时间
        final long now = SystemClock.uptimeMillis();
        final long dueTime = now + delayMillis;//我们传入的delayMillis是0
        //注释1处,向mCallbackQueues中添加一个CallbackRecord
        mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
        if (dueTime <= now) {//进入此分支
           //注释2处,没有延迟,直接开始调度
           scheduleFrameLocked(now);
        } else {
            //注释3处,有延迟,就通过handler延时发送一个message
            Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
            //注意我们把回调类型赋值给msg的arg1字段了
            msg.arg1 = callbackType;
            msg.setAsynchronous(true);
            mHandler.sendMessageAtTime(msg, dueTime);
        }
    }
}

Choreographer类的postCallbackDelayedInternal方法的注释1处,这里的mCallbackQueues是一个CallbackQueue[]类型的对象。CallbackQueue这个类并不是严格的先进先出,当向CallbackQueue中插入数据的时候,会根据时间从小到大排序,时间小的靠近头部,时间大的靠近尾部。

Choreographer类的postCallbackDelayedInternal方法的注释3处,最后处理message的时候,如果条件满足,还是会调用scheduleFrameLocked方法。

Choreographer类的postCallbackDelayedInternal方法的注释2处,Choreographer类的scheduleFrameLocked方法精简版

private void scheduleFrameLocked(long now) {
    if (!mFrameScheduled) {//如果没有被安排,就安排上,哈哈
        mFrameScheduled = true;
        if (USE_VSYNC) {
           // 此条件为默认为true
           // 如果是主线程,就直接调度,否则发送一个消息到主线程
            if (isRunningOnLooperThreadLocked()) {
                //我们在这个例子中,是在主线程开启动画的
                scheduleVsyncLocked();
            } else {
                // 当消息被处理的时候,如果条件满足,还是会调用scheduleVsyncLocked方法
                Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
                msg.setAsynchronous(true);
                mHandler.sendMessageAtFrontOfQueue(msg);
            }
        }
        //...
   }
}

Choreographer类的scheduleVsyncLocked方法

private void scheduleVsyncLocked() {
    //mDisplayEventReceiver是一个FrameDisplayEventReceiver类型的对象
    mDisplayEventReceiver.scheduleVsync();
}

FrameDisplayEventReceiver继承了DisplayEventReceiver,scheduleVsync方法在DisplayEventReceiver就实现了。

/**
 * 当开始显示下一帧的时候发送一个竖直同步脉冲(VSYNC)
 */
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 {
       //mReceiverPtr 会在DisplayEventReceiver的构造函数中被初始化
       //调用native方法,这个方法其实是向native层注册成为观察者,用来接收屏幕刷新信号
       nativeScheduleVsync(mReceiverPtr);
    }
}

到这里,AnimationHandler的addAnimationFrameCallback方法的注释1处,getProvider().postFrameCallback(mFrameCallback);这行代码执行就完毕了。我们继续执行后面的逻辑,同时等待异步的屏幕刷新信号的到来。

DisplayEventReceiver对应的native类是NativeDisplayEventReceiver。有兴趣的可以了解一下。

我们回到AnimationHandler的addAnimationFrameCallback方法继续向下执行。

//注释2处,把传入的回调添加到mAnimationCallbacks中判断是否存在,避免重复添加
if (!mAnimationCallbacks.contains(callback)) {
   mAnimationCallbacks.add(callback);
}

到这里AnimationHandler的addAnimationFrameCallback方法执行完毕,我们继续回到ValueAnimator的start(boolean playBackwards) 方法继续向下执行。

//注释2处如果没有启动延迟,就立即启动动画 
startAnimation();

ValueAnimator的startAnimation方法精简版

/**
 * 通过把动画加入到一个活动的动画列表来启动动画。这个方法必须在UI线程调用。
 */
private void startAnimation() {
    //...
    mAnimationEndRequested = false;
    //注释1处,调用initAnimation()方法
    initAnimation();
    //标记动画已经在执行了
    mRunning = true;
   //...
}

在startAnimation的注释1处,调用了initAnimation()方法,这里注意一下ObjectAnimator重写了这个方法,我们先来看一下ObjectAnimator的initAnimation方法。

ObjectAnimator的initAnimation方法

/**
 * 这个方法在处理动画的第一帧之前会被立即调用。如果动画的启动延迟不为0,那么这个方法会在延迟结束以后执行。
 * 这个方法负责动画的最终初始化步骤。包括设置mEvaluator,如果用户没有提供的话,
 * 还有设置setter/getter方法,如果用户没有提供的话。
 *
 * 重写这方法应该调用超类的方法来确保内部的机制被正确的设置。
 */
@CallSuper
@Override
void initAnimation() {
    if (!mInitialized) {//如果动画还没有被初始化
        //获取要执行动画的对象
        final Object target = getTarget();
        if (target != null) {
            final int numValues = mValues.length;
            for (int i = 0; i < numValues; ++i) {
                //设置每个属性的setter/getter方法
                mValues[i].setupSetterAndGetter(target);
            }
        }
        //调用父类的initAnimation方法
        super.initAnimation();
    }
}

PropertyValuesHolder的setupSetterAndGetter方法,这个方法就看看头部的注释好了,实现细节可以暂时不去管。

/**
* 这个是一个内部方法(ObjectAnimator会调用)在运行动画之前设置setter/getter方法。
* 如果没有手动的为这个对象设置setter方法,则会根据提供的属性名称,目标对象和值类型自动派生。
* 如果没有设置getter方法,那么当且仅当给定的值是null的情况下才会派生getter方法。如果值是null,
* 那么 getter方法(无论是提供的还是派生的),会被调用来把这些null值设置成目标对象的属性的值。
*
* @param target setter方法(和可能的getter方法)存在的对象。
*/
void setupSetterAndGetter(Object target) {
    if (mProperty != null) {
        // check to make sure that mProperty is on the class of target
        try {
            Object testValue = null;
            //获取所有的动画帧
            List<Keyframe> keyframes = mKeyframes.getKeyframes();
            int keyframeCount = keyframes == null ? 0 : keyframes.size();
            for (int i = 0; i < keyframeCount; i++) {
                Keyframe kf = keyframes.get(i);
                //如果Keyframe没有值,或者要从目标对象中获取关键帧的值
                if (!kf.hasValue() || kf.valueWasSetOnStart()) {
                    if (testValue == null) {
                        testValue = convertBack(mProperty.get(target));
                    }
                    //为关键帧设置值
                    kf.setValue(testValue);
                    kf.setValueWasSetOnStart(true);
                }
            }
           return;
        } catch (ClassCastException e) {
            Log.w("PropertyValuesHolder","No such property (" + mProperty.getName() +
                        ") on target object " + target + ". Trying reflection instead");
            mProperty = null;
        }
    }
    // We can't just say 'else' here because the catch statement sets mProperty to null.
    if (mProperty == null) {
        //如果对象不存在对应的属性,比如View没有girlFriend属性,
        Class targetClass = target.getClass();
        if (mSetter == null) {
            //设置setter,并把setter方法保存在一个map中,这样不用每次都使用反射来获取setter了。
            setupSetter(targetClass);
       }
        List<Keyframe> keyframes = mKeyframes.getKeyframes();
        int keyframeCount = keyframes == null ? 0 : keyframes.size();
        for (int i = 0; i < keyframeCount; i++) {
            Keyframe kf = keyframes.get(i);
            if (!kf.hasValue() || kf.valueWasSetOnStart()) {
                if (mGetter == null) {
                    //设置getter,并把getter方法保存在一个map中,这样不用每次都使用反射来获取getter了。
                    setupGetter(targetClass);
                    if (mGetter == null) {
                       // Already logged the error - just return to avoid NPE
                        return;
                    }
                }
                try {
                    Object value = convertBack(mGetter.invoke(target));
                    kf.setValue(value);
                    kf.setValueWasSetOnStart(true);
                } catch (InvocationTargetException e) {
                    Log.e("PropertyValuesHolder", e.toString());
                } catch (IllegalAccessException e) {
                    Log.e("PropertyValuesHolder", e.toString());
                }
            }
        }
    } 
}

Value的initAnimation方法

void initAnimation() {
   if (!mInitialized) {
       int numValues = mValues.length;
       for (int i = 0; i < numValues; ++i) {
           //调用PropertyValuesHolder的init方法
           mValues[i].init();
       }
       //到这里mInitialized才为true
       mInitialized = true;
   }
}

PropertyValuesHolder的init方法

/**
* 内部方法,ValueAnimator调用这个方法来设置计算动画值的TypeEvaluator
*/
void init() {
    if (mEvaluator == null) {
        // 我们这里是Float类型所以mEvaluator被赋值为sFloatEvaluator
        mEvaluator = (mValueType == Integer.class) ? sIntEvaluator :
                    (mValueType == Float.class) ? sFloatEvaluator :
                    null;
    }
    if (mEvaluator != null) {
        //将sFloatEvaluator传递给mKeyframes
        mKeyframes.setEvaluator(mEvaluator);
    } 
}

到这里,ValueAnimator的startAnimation方法到此结束,我们回到ValueAnimator的start(boolean playBackwards)方法的注释3处,调用setCurrentPlayTime方法。

ValueAnimator的setCurrentPlayTime方法

/**
 * 设置动画的位置到指定的时间点。参数playTime应该在0到动画的时长之间。如果动画还没开始,
 * 当动画开始的时候,那么动画会设置当前的播放时间为playTime,然后从这个时间点继续执行
 * 如果动画已经开始了,那么动画会设置当前的播放时间为playTime,然后从这个时间点继续执行,
 *
 * @param playTime 动画前进或后退的时间(以毫秒为单位)。
 */
public void setCurrentPlayTime(long playTime) {
    //根据传入的时间,计算动画执行的百分比
    float fraction = mDuration > 0 ? (float) playTime / mDuration : 1;
    //调用setCurrentFraction方法
    setCurrentFraction(fraction);
}

ValueAnimator的setCurrentFraction方法精简版

public void setCurrentFraction(float fraction) {
    //如果初始化了,不会重复初始化
    initAnimation();
    //计算对应的百分比
    fraction = clampFraction(fraction);
    //...
    final float currentIterationFraction = getCurrentIterationFraction(fraction, mReversing);
    //感觉这个方法有戏啊
    animateValue(currentIterationFraction);
}

ObjectAnimator重写了animateValue方法,我们先看下ObjectAnimator的animateValue方法

@CallSuper
@Override
void animateValue(float fraction) {
    final Object target = getTarget();
    if (mTarget != null && target == null) {
        //没有目标了,还做个毛动画,取消动画并返回
        cancel();
        return;
    }
    //调用父类的animateValue
    super.animateValue(fraction);
    int numValues = mValues.length;
    for (int i = 0; i < numValues; ++i) {
        //注释1处property设置新的值
        mValues[i].setAnimatedValue(target);
    }
}

ValueAnimator的animateValue方法精简版

@CallSuper
void animateValue(float fraction) {
    //使用插值器计算百分比
    fraction = mInterpolator.getInterpolation(fraction);
    mCurrentFraction = fraction;
    int numValues = mValues.length;
    for (int i = 0; i < numValues; ++i) {
        //每个属性根据fraction计算当前的值,调用PropertyValuesHolder的calculateValue方法
        mValues[i].calculateValue(fraction);
    }
    //...
}

使用 PropertyValuesHolder的calculateValue方法

/**
 * 使用 PropertyValuesHolder的 evaluator来计算值
 *
 * @param fraction 动画的经过时间插值部分。
 */
void calculateValue(float fraction) {
    Object value = mKeyframes.getValue(fraction);
    //改变后的属性值
    mAnimatedValue = mConverter == null ? value : mConverter.convert(value);
}

具体怎么计算的先不看。然后我们回到ObjectAnimator的animateValue方法的注释1处

mValues[i].setAnimatedValue(target);

调用了PropertyValuesHolder的setAnimatedValue方法

/**
 * ObjectAnimator 调用这个方法把ValueAnimator计算出来的值转化成目标对象的属性的值
 * @param target 设置值的目标对象
 */
void setAnimatedValue(Object target) {
    if (mProperty != null) {
           //我们传入的是float类型,所以调用FloatProperty的set方法
          mProperty.set(target, getAnimatedValue());
    }
    if (mSetter != null) {
        try {
            mTmpValueArray[0] = getAnimatedValue();
            //通过反射的方式设置目标对象的属性值
            mSetter.invoke(target, mTmpValueArray);
        } catch (InvocationTargetException e) {
            Log.e("PropertyValuesHolder", e.toString());
        } catch (IllegalAccessException e) {
            Log.e("PropertyValuesHolder", e.toString());
        }
    }
}

这里通过反射来设置我们的目标对象(在这个例子中是一个TextView)的属性值,在这个例子中就是设置scaleX。然后View设置完scaleX属性值后会调用View的invalidate方法,关于invalidate方法不多说,最终会调用ViewRootImpl#scheduleTraversals(),就是提交一个请求重新绘制的任务。

ViewRootImpl的scheduleTraversals()方法

void scheduleTraversals() {
    if (!mTraversalScheduled) {
        mTraversalScheduled = true;
        mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        //这里Choreographer,提交了了一个回调类型为Choreographer.CALLBACK_TRAVERSAL的runnable,
        //这个runnable就是重新绘制任务。
        mChoreographer.postCallback(
                Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        if (!mUnbufferedInputDispatch) {
            scheduleConsumeBatchedInput();
        }
        notifyRendererOfFramePending();
        pokeDrawLockIfNeeded();
    }
}

我们先看一下这个mTraversalRunnable对象

这里的Runnable就是ViewRootImpl里面的mTraversalRunnable,我们看一下mTraversalRunnable这个对象

final class TraversalRunnable implements Runnable {
    @Override
    public void run() {
        //调用ViewRootImpl的doTraversal方法
        doTraversal();
    }
}

调用ViewRootImpl的doTraversal方法

void doTraversal() {
    if (mTraversalScheduled) {
        mTraversalScheduled = false;
        mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
        //调用performTraversals此方法
        performTraversals();
    }
}

ViewRootImpl的performTraversals方法

private void performTraversals() {

  //...
  performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
  //...
  performLayout(lp, mWidth, mHeight);
  //...
  performDraw();
  //...
}

只要这个mTraversalRunnable对象运行的时候,就会重新绘制View,将绘制好的数据放入buffer中等待屏幕取出并显示。

接下来我们看一下Choreographer的postCallback方法。

/**
 * 提交一个任务,在下一帧的时候执行。回调执行完毕后,会被自动移除。
 *
 * @param callbackType 回调类型
 * @param action The callback action to run during the next frame.
 * @param token The callback token, or null if none.
 *
 * @see #removeCallbacks
 * @hide
 */    
public void postCallback(int callbackType, Runnable action, Object token) {
    //调用重载函数
    postCallbackDelayed(callbackType, action, token, 0);
}
public void postCallbackDelayed(int callbackType,
            Runnable action, Object token, long delayMillis) {
    //...
    postCallbackDelayedInternal(callbackType, action, token, delayMillis);
}
private void postCallbackDelayedInternal(int callbackType,
            Object action, Object token, long delayMillis) {
    //...
    synchronized (mLock) {
        final long now = SystemClock.uptimeMillis();
        //我们传入的延迟是0
        final long dueTime = now + delayMillis;
        //利用传入的action,构建一个CallbackRecord对象,添加到对应类型的队列中
        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);
        }
    }
}

Choreographer类的scheduleFrameLocked方法精简版

private void scheduleFrameLocked(long now) {
    if (!mFrameScheduled) {//如果没有被安排,就安排上,哈哈
        mFrameScheduled = true;
        if (USE_VSYNC) {
            // 此条件为默认为true
           // 如果是主线程,就直接调度,否则发送一个消息到主线程
           // 当消息被处理的时候,如果条件满足,还是会调用scheduleVsyncLocked方法
            if (isRunningOnLooperThreadLocked()) {
                //在主线程,直接调度
                scheduleVsyncLocked();
            } else {
                Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
                msg.setAsynchronous(true);
                mHandler.sendMessageAtFrontOfQueue(msg);
            }
        }
        //...
   }
}

Choreographer类的scheduleVsyncLocked方法

private void scheduleVsyncLocked() {
    //mDisplayEventReceiver是一个FrameDisplayEventReceiver类型的对象
    mDisplayEventReceiver.scheduleVsync();
}

我们发现我们提交的重新绘制任务,最终也是走到了Choreographer类的scheduleVsyncLocked方法中,向native层注册成为观察者,用来接收屏幕刷新信号。

现在,View的属性也改变了,重新绘制的任务已经提交了,就等待下一帧的屏幕刷新信号来了。。。

等了好久的屏幕刷新信号终于来了,哈哈。


第1帧VSYNC信号到来.png

注意:我们向native层注册成为观察者,用来接收屏幕刷新信号,当下一帧屏幕刷新信号到来的时候会调用DisplayEventReceiver的onVsync方法(为什么会调用这个方法,以及怎么去调用的还有待考察,这里先记住)

FrameDisplayEventReceiver重写了DisplayEventReceiver的onVsync方法。这个方法是被native层异步调用的。

private final class FrameDisplayEventReceiver extends DisplayEventReceiver
            implements Runnable {
         
    //...   
    @Override
    public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
        // ...            
           
        mTimestampNanos = timestampNanos;
        mFrame = frame;
        //这个要注意,message的callback就是FrameDisplayEventReceiver, 
        //FrameDisplayEventReceiver实现了Runnable接口
        //当message被处理的时候,会调用FrameDisplayEventReceiver的run方法
        Message msg = Message.obtain(mHandler, this);
        msg.setAsynchronous(true);
        mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
    }
     //... 
}

在onVsync方法中发送了一个message,这个message的回调就是FrameDisplayEventReceiver本身,因为FrameDisplayEventReceiver实现了Runnable接口,当message被处理的时候,就会调用FrameDisplayEventReceiver的run方法。

FrameDisplayEventReceiver的run方法

/*message被处理的时候会调用run方法*/
@Override
public void run() {
    mHavePendingVsync = false;
    //调用 Choreographer的doFrame方法
    doFrame(mTimestampNanos, mFrame);
}

Choreographer类的doFrame方法精简版

void doFrame(long frameTimeNanos, int frame) {
        
    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();
         //注释1处,处理动画事件
         doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
         mFrameInfo.markPerformTraversalsStart();
         //注释2处,处理View重新绘制事件,measure,layout,draw
         doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);

         doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
     } finally {
         AnimationUtils.unlockAnimationClock();
         Trace.traceEnd(Trace.TRACE_TAG_VIEW);
     }
}

Choreographer类的doFrame方法注释1处,调用doCallbacks 方法处理动画事件。

Choreographer类的doCallbacks方法精简版。

void doCallbacks(int callbackType, long frameTimeNanos) {
      CallbackRecord callbacks;
      synchronized (mLock) {
                
          final long now = System.nanoTime();
          //取出CALLBACK_ANIMATION类型的CallbackRecord对象
          callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
                    now / TimeUtils.NANOS_PER_MS);
          if (callbacks == null) {
              return;
          }
          mCallbacksRunning = true;          
          //...
          try {
              for (CallbackRecord c = callbacks; c != null; c = c.next) {
                  //执行CallbackRecord的run方法
                  c.run(frameTimeNanos);
              }
          } finally {
              synchronized (mLock) {
                  mCallbacksRunning = false;
                  do {
                      final CallbackRecord next = callbacks.next;
                      //注释1处,回收callbacks,回调执行完毕后会被移除
                      recycleCallbackLocked(callbacks);
                      callbacks = next;
                   } while (callbacks != null);
              }
          Trace.traceEnd(Trace.TRACE_TAG_VIEW);
      }
}

我们先看一下CallbackRecord的run方法

public void run(long frameTimeNanos) {
      if (token == FRAME_CALLBACK_TOKEN) {
           //动画事件对应的callbackType类型是Choreographer.CALLBACK_ANIMATION,执行这行代码
          ((FrameCallback)action).doFrame(frameTimeNanos);
      } else {
          ((Runnable)action).run();
      }   
}

如果我们的callbackType类型是Choreographer.CALLBACK_ANIMATION,就会执行FrameCallback的doFrame方法

这里的FrameCallback就是mFrameCallback

private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
        @Override
        public void doFrame(long frameTimeNanos) {
            //调用AnimationHandler的doAnimationFrame方法,注意这里我们是传入了一个时间
            doAnimationFrame(getProvider().getFrameTime());
           //如果mAnimationCallbacks.size() > 0,再次调用MyFrameCallbackProvider的
           //postFrameCallback方法,来监听下一帧的屏幕刷新信号,
           //这里传入的this就是mFrameCallback  
            if (mAnimationCallbacks.size() > 0) {     
                getProvider().postFrameCallback(this);
            }
        }
 };

注意:mFrameCallback的doFrame方法中,在调用AnimationHandler的doAnimationFrame方法的时候,我们是传入了一个时间,表示动画会根据这个时间计算动画的下一帧的属性值是多少。

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)) {
            //注释1处,调用AnimationFrameCallback的doAnimationFrame方法
            callback.doAnimationFrame(frameTime);
            if (mCommitCallbacks.contains(callback)) {
                getProvider().postCommitCallback(new Runnable() {
                    @Override
                    public void run() {
                        commitAnimationFrame(callback, getProvider().getFrameTime());
                    }
                });
            }
        }
    }
    //注意一下,cleanUpList方法会移除mAnimationCallbacks中为null的AnimationFrameCallback对象。
    //当mAnimationCallbacks.size==0的时候,我们就不会再向native层注册接收屏幕刷新信号了,
    //也就不会再执行动画,以及View的绘制事件。        
    cleanUpList();
}

在上面方法的注释1处,调用了调用AnimationFrameCallback的doAnimationFrame方法,这里的callback就是ValueAnimator。

ValueAnimator的doAnimationFrame方法

public final boolean doAnimationFrame(long frameTime) {
    //... 
   //动画还没有启动
   if (!mRunning) {
            // 如果此时动画还没启动,说明动画正处在延迟启动的等待过程中
            if (mStartTime > frameTime && mSeekFraction == -1) {
                // 进入这个分支说明在启动延迟的等待过程中,用户没有改变快进。
                //如果在启动延迟的等待过程中,用户快进了,那么动画会从快进的位置立刻开始。
                return false;
            } else {
                // 如果现在没有设置mRunning,则表示非零启动延迟,没有快进,没有反向播放动画。 
                //此时,启动延迟已经过去,应该开启动画了。
                mRunning = true;
                startAnimation();
            }
        }

       //...
        mLastFrameTime = frameTime;
        final long currentTime = Math.max(frameTime, mStartTime);
        //其他逻辑先不用看,只看这一行,调用ValueAnimator的animateBasedOnTime方法
        boolean finished = animateBasedOnTime(currentTime);

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

ValueAnimator的animateBasedOnTime方法

boolean animateBasedOnTime(long currentTime) {
    boolean done = false;
    //如果动画已经开始了
    if (mRunning) {
        //...
        //这个方法会导致属性的改变,View的提交重新绘制的任务
        animateValue(currentIterationFraction);
    }
    return done;
}

我们回到mFrameCallback的doFrame方法中,继续往下走

//如果mAnimationCallbacks.size() > 0,再次调用MyFrameCallbackProvider的
//postFrameCallback方法,来监听下一帧的屏幕刷新信号,
//这里传入的this就是mFrameCallback  
if (mAnimationCallbacks.size() > 0) {     
     getProvider().postFrameCallback(this);
}

注意:这里mAnimationCallbacks.size() > 0表示动画还没结束,还需要继续监听屏幕刷新信号来改变View的属性。但是这里为什要再次调用getProvider().postFrameCallback(this);,这里猜测是,native层通知完我们一次以后,会把我们从观察者列表中移除。

处理完Choreographer类的doFrame方法中的注释1处,继续处理Choreographer类的doFrame方法中的注释2处,处理提交的View的重新绘制事件。重新绘制View,将计算好的绘制数据保存在一个buffer里面。

 void doFrame(long frameTimeNanos, int frame) {
        
        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();
            //注释1处,处理动画事件
            doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
            mFrameInfo.markPerformTraversalsStart();
            //注释2处,处理View重新绘制事件,measure,layout,draw
            doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);

            doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
        } finally {
            AnimationUtils.unlockAnimationClock();
            Trace.traceEnd(Trace.TRACE_TAG_VIEW);
        }
 }

再看一下处理View重新绘制事件的过程。

doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
private static final class CallbackRecord {
   //...
    public void run(long frameTimeNanos) {
        if (token == FRAME_CALLBACK_TOKEN) {
                ((FrameCallback)action).doFrame(frameTimeNanos);
            } else {
                //处理View的重新绘制事件
                ((Runnable)action).run();
            }
    }
}

这里action就是mTraversalRunnable对象。run的时候会调用ViewRootImpl的performTraversals方法,重新绘制整个View树,将绘制数据保存在buffer里面。

ViewRootImpl的performTraversals方法

private void performTraversals() {

  //...
  performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);
  //...
  performLayout(lp, mWidth, mHeight);
  //...
  performDraw();
  //...
}

精力有限,这里只分析了第一张图里面的第1帧和第2帧。

其实我一直有一个疑问,下面这段代码

private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
        @Override
        public void doFrame(long frameTimeNanos) {
            //调用AnimationHandler的doAnimationFrame方法,注意这里我们是传入了一个时间
            doAnimationFrame(getProvider().getFrameTime());
            //如果mAnimationCallbacks.size() > 0,再次调用MyFrameCallbackProvider的
            //postFrameCallback方法,来监听下一帧的屏幕刷新信号,
            //这里传入的this就是mFrameCallback  
            if (mAnimationCallbacks.size() > 0) {     
                getProvider().postFrameCallback(this);
            }
        }
 };

存不存在当屏幕刷新信号到来时mAnimationCallbacks.size() > 0这个条件不成立呢?用图片说明一下。


第1帧VSYNC信号到来size.png
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));
        }
    }

也就是说AnimationHandler的addAnimationFrameCallback到第1帧屏幕刷新信号到之后,才把回调加入到mAnimationCallbacks。

我猜测这种情况应该极少情况会存在,即使发生了这种情况,也不会造成严重的影响,这种情况导致的影响就是在第1帧到来的时候,还没有提交View的重新绘制任务,那么就会造成延迟一帧,当第2帧屏幕刷新信号到来的时候,就可以正常处理了。

结尾:本篇文章重点分析动画是怎么动起来的,本篇文章参考了不少的博客和自己许多猜测,难免会有错误之处,在后续的工作学习中会不断的完善。有不到之处和错误之处,还请多多指教。

参考链接

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