基于源码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的属性,再次提交重新绘制任务。循环此过程直到动画结束。
下面详细叙述一下
我们在第0帧的时候向native层注册成为一个观察者,用来接收下一帧竖直同步信号(信号通知我们是异步的)。
我们根据时间计算View在第1帧的属性值应该是多少,改变View的属性值并请求重新绘制(请求重新绘制,只是提交一个任务,并等待下一个竖直同步信号到来的时候处理。在同一帧内,如果已经提交了一个绘制任务,那么再次提交绘制任务会被忽略。因为重新绘制,会从ViewRootImpl开始向下重新绘制,可以保证遍历所有的子 View)。
第1帧的竖直同步信号到来了,我们这时候动画还没有结束,我们继续接收下一帧竖直同步信号。我们根据时间计算View在第2帧的属性值应该是多少,改变View的属性值并请求重新绘制。然后处理第0帧提交的绘制任务,重新绘制View,将计算好的绘制数据保存在一个buffer里面。
第2帧的竖直同步信号来了,我们这个时候动画还没有结束,我们继续接收下一帧竖直同步信号。我们根据时间计算View在第3帧的属性值应该是多少,改变View的属性值并请求重新绘制。取出第1帧中buffer里面存储的绘制数据显示在屏幕上,然后处理第1帧提交的绘制任务,重新绘制View,将计算好的绘制数据保存在一个buffer里面。
第3帧的竖直同步信号来了,我们这个时候动画还没有结束,我们继续接收下一帧竖直同步信号。我们根据时间计算View在第4帧的属性值应该是多少,改变View的属性值并请求重新绘制。取出第2帧中buffer里面存储的绘制数据显示在屏幕上,然后处理第2帧提交的绘制任务,重新绘制View,将计算好的绘制数据保存在一个buffer里面。
如此重复
到了动画结束的时间,我们取消注册,不再接收下一帧的竖直同步信号,也就不会导致buffer里面的绘制数据更新,这个时候屏幕上显示的就一直是相同的绘制数据了,界面不会再发生变化。
源码分析
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的属性也改变了,重新绘制的任务已经提交了,就等待下一帧的屏幕刷新信号来了。。。
等了好久的屏幕刷新信号终于来了,哈哈。
注意:我们向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这个条件不成立呢?用图片说明一下。
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帧屏幕刷新信号到来的时候,就可以正常处理了。
结尾:本篇文章重点分析动画是怎么动起来的,本篇文章参考了不少的博客和自己许多猜测,难免会有错误之处,在后续的工作学习中会不断的完善。有不到之处和错误之处,还请多多指教。
参考链接