属性动画玩得好的话,可以为用户呈现出比较好的效果。但是工作中用到的比较少,即便用到也比较简单,也是为了增强自己对这块内容的理解,所以,就以属性动画源码分析为题写这么一篇文章,权当做笔记了。
我们不去看ValueAnimator、ObjectAnimator的ofXxx方法,直奔主题,从ObjectAnimator#start开始看,
1 ObjectAnimator#start
@Override
public void start() {
//删除重复动画
AnimationHandler.getInstance().autoCancelBasedOn(this);
super.start();
}
start首先会将重复动画删除,然后调用ValueAnimator#start
2 ValueAnimator#start
//传入的playBackwards == false
private void start(boolean playBackwards) {
//当前Looper线程不能为null
if (Looper.myLooper() == null) {
throw new AndroidRuntimeException("Animators may only be run on Looper threads");
}
//末尾倒播默认为false
mReversing = playBackwards;
mSelfPulse = !mSuppressSelfPulseRequested;
...
//标识动画状态的一些变量
mStarted = true;
mPaused = false;
mRunning = false;
mAnimationEndRequested = false;
//重置mLastFrameTime
mLastFrameTime = -1;
mFirstFrameTime = -1;
mStartTime = -1;
addAnimationCallback(0);
if (mStartDelay == 0 || mSeekFraction >= 0 || mReversing) {
//动画启动没有延时,接下来做一些初始化工作,熟知的AnimatorStart方法会被调用
startAnimation();
//mSeekFraction没有被赋值,进入if分支
if (mSeekFraction == -1) {
setCurrentPlayTime(0);
} else {
setCurrentFraction(mSeekFraction);
}
}
}
2.1 ValueAnimator#addAnimationCallback
private void addAnimationCallback(long delay) {
if (!mSelfPulse) {
return;
}
getAnimationHandler().addAnimationFrameCallback(this, delay);
}
注意addAnimationFrameCallback方法中的参数,ValueAnimator传入了自身对象,因为ValueAnimatro实现了AnimationHandler.AnimationFrameCallback接口。看一下这个接口定义,其中包含两个方法,doAnimationFrame与一帧中动画有关,这里也简单提一下,留个印象,后面会研究ValueAnimator#doAnimationFrame具体细节。
interface AnimationFrameCallback {
/**
* Run animation based on the frame time.
*/
boolean doAnimationFrame(long frameTime);
void commitAnimationFrame(long frameTime);
}
接下来,ValueAnimator#getAnimationHandler调用AnimationHandler#getInstance
2.1.1 AnimationHandler#getInstance
public static AnimationHandler getInstance() {
//sAnimatorHandler为ThreadLoacl<AnimatorHandler>类型
if (sAnimatorHandler.get() == null) {
sAnimatorHandler.set(new AnimationHandler());
}
return sAnimatorHandler.get();
}
sAnimatorHandler为ThreadLoacl类型,即线程局部变量,状态线程私有。
2.2 AnimationHandler#addAnimationFrameCallback
public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {
// 向mAnimationCallbacks添加元素只出现在下面的if判断中,
// 第一次调用该方法,此时mAnimationCallbacks中没有元素
// 所以会进入这个if判断
if (mAnimationCallbacks.size() == 0) {
getProvider().postFrameCallback(mFrameCallback);
}
//AnimationHandler类中只有这一处调用了mAnimationCallbacks#add
if (!mAnimationCallbacks.contains(callback)) {
mAnimationCallbacks.add(callback);
}
//此时delay == 0
if (delay > 0) {
mDelayedCallbackStartTime.put(callback, (SystemClock.uptimeMillis() + delay));
}
}
此时,mAnimationCallbacks容量为0,会执行第一个、第二个if判断逻辑。getProvider返回的是MyFrameCallbackProvider对象,调用该对象的postFrameCallback方法,同时向mAnimationCallbacks添加callback元素。
2.3 MyFrameCallbackProvider#postFrameCallback
getProvider方法返回的是MyFrameCallbackProvider单例对象,看一下MyFrameCallbackProvider定义
private class MyFrameCallbackProvider implements AnimationFrameCallbackProvider {
//MyFrameCallbackProvider中的Choreographer对象,大BOSS出现了
final Choreographer mChoreographer = Choreographer.getInstance();
@Override
public void postFrameCallback(Choreographer.FrameCallback callback) {
mChoreographer.postFrameCallback(callback);
}
...
}
注意到postFrameCallback方法实质是调用了Choreographer的postFrameCallback方法。
2.4 Choreographer类
先来看一下开发文档怎么说,
Each Looper thread has its own choreographer. Other threads can post callbacks to run on the choreographer but they will run on the Looper to which the choreographer belongs.
翻译一下就是,每个Looper线程都有自己的choreographer。其他线程可以提交callback以在choreographer上运行,但是它们将在choreographer所属的Looper上运行。
2.4.1 Choreographer构造方法
private Choreographer(Looper looper, int vsyncSource) {
// Looper线程
mLooper = looper;
// Handler实例
mHandler = new FrameHandler(looper);
// 默认情况下USE_VSYNC为true
// FrameDisplayEventReceiver用于接收垂直同步信号
mDisplayEventReceiver = USE_VSYNC
? new FrameDisplayEventReceiver(looper, vsyncSource)
: null;
// 上一帧绘制结束时间
mLastFrameTimeNanos = Long.MIN_VALUE;
// 一帧绘制的时间间隔,默认为16ms
mFrameIntervalNanos = (long)(1000000000 / getRefreshRate());
//新建四个Callback队列
mCallbackQueues = new CallbackQueue[CALLBACK_LAST + 1];
for (int i = 0; i <= CALLBACK_LAST; i++) {
mCallbackQueues[i] = new CallbackQueue();
}
}
Choreographer构造方法中会做如下几部分工作:
- 新建FrameHandler类型的Handler用来处理消息,具体看2.4.2小节。
- 默认使用垂直同步信号来渲染帧,新建FrameDisplayEventReceiver用来接收脉冲信号。
- 设置一些帧率渲染的时间变量,一帧绘制的时间间隔,默认16ms。
- 新建四个Callback队列,分别用来组织INPUT、ANIMATION、TRAVERSAL、COMMIT回调。
2.4.2 FrameHandler类,
private final class FrameHandler extends Handler {
public FrameHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_DO_FRAME:
doFrame(System.nanoTime(), 0);
break;
case MSG_DO_SCHEDULE_VSYNC:
doScheduleVsync();
break;
case MSG_DO_SCHEDULE_CALLBACK:
doScheduleCallback(msg.arg1);
break;
}
}
}
FrameHandler类逻辑很简单,它继承了Handler类,重写了handleMessage方法,里面对三种消息进行处理:MSG_DO_FRAME、MSG_DO_SCHEDULE_VSYNC、MSG_DO_SCHEDULE_CALLBACK。
2.4.3 Choreographer$FrameDisplayEventReceiver
先看一下,DisplayEventReceiver注释
Provides a low-level mechanism for an application to receive display events such as vertical sync.
译:为应用程序提供低层机制,以接收垂直同步等显示事件
private final class FrameDisplayEventReceiver extends DisplayEventReceiver
implements Runnable {
private boolean mHavePendingVsync;
private long mTimestampNanos;
private int mFrame;
public FrameDisplayEventReceiver(Looper looper, int vsyncSource) {
super(looper, vsyncSource);
}
@Override
public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
...
//将同步信号事件提交给mHandler
...
mTimestampNanos = timestampNanos;
mFrame = frame;
Message msg = Message.obtain(mHandler, this);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
}
@Override
public void run() {
mHavePendingVsync = false;
doFrame(mTimestampNanos, mFrame);
}
}
当垂直同步信号来时会调用onVsync,这里FrameHandler会处理,注意到FrameDisplayEventReceiver实现了Runnable接口,FrameHandler将FrameDisplayEventReceiver自身对象提交到了消息队列中等待被处理。重写run方法中会调用Choreographer#doFrame,该方法会在本文后面提到。下面还是接着看Choreographer#postFrameCallback。
2.5 Choreographer#postFrameCallback
调用栈很简单,postFrameCallback——>postFrameCallbackDelayed——>postCallbackDelayedInternal,注意一下postCallbackDelayedInternal方法参数,这里传入的token为FRAME_CALLBACK_TOKEN,这个会在if逻辑判断中使用。
2.6 postCallbackDelayedInternal
private void postCallbackDelayedInternal(int callbackType,
Object action, Object token, long delayMillis) {
...
synchronized (mLock) {
final long now = SystemClock.uptimeMillis();
final long dueTime = now + delayMillis;
// 此时,callbackType为CALLBACK_ANIMATION
// 将callback添加到CALLBACK_ANIMATION对应的CallbackQueue中
mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
//该Callback执行的截止时间先于当前时间
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);
}
}
}
2.7 addCallbackLocked
public void addCallbackLocked(long dueTime, Object action, Object token) {
//传入的action封装在CallbackRecord中
CallbackRecord callback = obtainCallbackLocked(dueTime, action, token);
CallbackRecord entry = mHead;
if (entry == null) {
mHead = callback;
return;
}
// 从这个if的逻辑块中知道,CallbackRecord是以链表的形式组织
// 根据执行时间组织在链表中的顺序
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;
}
CALLBACK_ANIMATION类型的Callback被封装到CallbackRecord中,并根据执行时间dueTime以链表的形式组织。
2.8 scheduleFrameLocked
private void scheduleFrameLocked(long now) {
if (!mFrameScheduled) {
mFrameScheduled = true;
//USE_VSYNC默认为true
if (USE_VSYNC) {
// Scheduling next frame on vsync.
// 要确保运行在Looper线程中
if (isRunningOnLooperThreadLocked()) {
scheduleVsyncLocked();
} else {
Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
msg.setAsynchronous(true);
mHandler.sendMessageAtFrontOfQueue(msg);
}
} else {
...
}
}
}
scheduleVsyncLocked经过几次简单调用会定位到方法DisplayEventReceiver#nativeScheduleVsync,从方法名也可以看出,这是一个native方法
@FastNative
private static native void nativeScheduleVsync(long receiverPtr);
此时,进入Native层,请求下一个Vsyn信号,
2.8.1 DisplayEventDispatcher#scheduleVsync
status_t DisplayEventDispatcher::scheduleVsync() {
if (!mWaitingForVsync) {
// Drain all pending events.
nsecs_t vsyncTimestamp;
int32_t vsyncDisplayId;
uint32_t vsyncCount;
...
//requestNextVsync
status_t status = mReceiver.requestNextVsync();
...
mWaitingForVsync = true;
}
return OK;
}
收到下一个同步垂直信号后,FrameDisplayEventReceiver#onVsync会被调用,2.4.3小节提到过FrameDisplayEventReceiver是一个Runnable,向Handler提交自身对象后,到执行时间后,执行run方法,调用Choreographer#doFrame。
2.9 Choreographer#doFrame
void doFrame(long frameTimeNanos, int frame) {
final long startNanos;
synchronized (mLock) {
if (!mFrameScheduled) {//mFrameScheduled = true
return; // no work to do
}
...
//当前帧计划开始绘制的时间
long intendedFrameTimeNanos = frameTimeNanos;
//当前这帧开始绘制的时间
startNanos = System.nanoTime();
//时间差,根据这个值来判断是否出现一帧绘制时长超过标准时长16ms
final long jitterNanos = startNanos - frameTimeNanos;
//超过一帧绘制时长16ms
if (jitterNanos >= mFrameIntervalNanos) {
final long skippedFrames = jitterNanos / mFrameIntervalNanos;
//跳帧次数超过次数报warning,这条LOG见过很多次了有木有😭
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;
...
//修正到mFrameIntervalNanos的整数倍
frameTimeNanos = startNanos - lastFrameOffset;
}
// 正常情况下,frameTimeNanos > mLastFrameTimeNanos
// 如果出现一帧绘制时长超过16ms,那么该帧下一帧的frameTimeNanos就会
// 小于mLastFrameTimeNanos,也就是Log中所说的,发生了跳帧,直接调用
// scheduleVsyncLocked请求下一个垂直同步信号
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);
/* 执行一帧里面包含的INPUT ANIMATION TRAVERSALS COMMIT四个回调动作 */
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);
}
...
}
这个方法比较长,但是也都做了比较详细的注释,所以就不展开说了。(后续会补充一些图)。接着看Choreographer#doCallbacks
2.10 Choreographer#doCallbacks
void doCallbacks(int callbackType, long frameTimeNanos) {
CallbackRecord callbacks;
synchronized (mLock) {
final long now = System.nanoTime();
//从CALLBACK_ANIMATION对应的Callback队列中获取相应类型的callback
callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
now / TimeUtils.NANOS_PER_MS);
if (callbacks == null) {
return;
}
mCallbacksRunning = true;
//一帧中最后的执行动作为CALLBACK_COMMIT
if (callbackType == Choreographer.CALLBACK_COMMIT) {
//COMMIT回调截至时间与改帧计划开始渲染时间做差
final long jitterNanos = now - frameTimeNanos;
//差值是否超过2个标准时间间隔
if (jitterNanos >= 2 * mFrameIntervalNanos) {
final long lastFrameOffset = jitterNanos % mFrameIntervalNanos
+ mFrameIntervalNanos;
if (DEBUG_JANK) {
Log.d(TAG, "Commit callback delayed by " + (jitterNanos * 0.000001f)
+ " ms which is more than twice the frame interval of "
+ (mFrameIntervalNanos * 0.000001f) + " ms! "
+ "Setting frame time to " + (lastFrameOffset * 0.000001f)
+ " ms in the past.");
mDebugPrintNextFrameTimeDelta = true;
}
//时间修正
frameTimeNanos = now - lastFrameOffset;
mLastFrameTimeNanos = frameTimeNanos;
}
}
}
try {
//按照时序执行Callback
for (CallbackRecord c = callbacks; c != null; c = c.next) {
c.run(frameTimeNanos);
}
} finally {
synchronized (mLock) {
mCallbacksRunning = false;
//回收CallbackRecord中的每个callback
do {
final CallbackRecord next = callbacks.next;
recycleCallbackLocked(callbacks);
callbacks = next;
} while (callbacks != null);
}
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
}
doCallbacks中会根据callbackType从对应的CallbackQueue中获取CallbackRecord对象,然后调用该对象的run方法。注意,在这之前会有一个对CALLBACK_COMMIT的判断,说明一帧渲染结束时,也会做时间修正,这样可能会有跳帧发生,但整体上不会破坏Choreographer渲染帧的节奏。看一下CallbackRecord#run
2.11 CallbackRecord#run
private static final class CallbackRecord {
public CallbackRecord next;
public long dueTime;
public Object action; // Runnable or FrameCallback
public Object token;
public void run(long frameTimeNanos) {
//还记得postCallbackDelayedInternal token参数传入的是FRAME_CALLBACK_TOKEN吗。
if (token == FRAME_CALLBACK_TOKEN) {
((FrameCallback)action).doFrame(frameTimeNanos);
} else {
((Runnable)action).run();
}
}
}
所以,这里走FrameCallback的doFrame方法,对应AnimationHandler#addAnimationFrameCallback提交的mFrameCallback
private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
doAnimationFrame(getProvider().getFrameTime());
if (mAnimationCallbacks.size() > 0) {
getProvider().postFrameCallback(this);
}
}
};
看到doAnimationFrame方法了么,动画绘制要开始了。动画绘制完成,继续调用postFrameCallback请求下一个同步垂直信号,开始下一帧的渲染。这里注意一下if的判断条件,
mAnimationCallbacks.size() > 0
前面2.2小节,ValueAnimator#start调用栈中会涉及到向mAnimationCallbacks中添加元素的逻辑,如果涉及到有对mAnimationCallbacks容量的判断,应该会mAnimationCallbacks移除元素的逻辑。但到目前为止,确实还没有看到相关部分,但是移除元素的逻辑应该是在if判断之前,而且几乎可以肯定是在doAnimationFrame的调用栈中,所以,让我们仔细研究一下这个方法。
2.12 AnimationHandler#doAnimationFrame
private void doAnimationFrame(long frameTime) {
long currentTime = SystemClock.uptimeMillis();
final int size = mAnimationCallbacks.size();
//遍历mAnimationCallbacks
for (int i = 0; i < size; i++) {
final AnimationFrameCallback callback = mAnimationCallbacks.get(i);
...
if (isCallbackDue(callback, currentTime)) {
//挨个调用callback的doAnimationFrame方法
callback.doAnimationFrame(frameTime);
...
}
}
cleanUpList();
}
AnimationHandler#doAnimationFrame首先会遍历mAnimationCallbacks,挨个调用AnimationFrameCallback类型元素的doAnimationFrame方法,最后调用cleanUpList。首先看一下ValueAnimator#doAnimationFramedoAnimationFrame
2.13 ValueAnimator#doAnimationFrame
public final boolean doAnimationFrame(long frameTime) {
...
// 处理动画的pause/resume
...
if (!mRunning) {
if (mStartTime > frameTime && mSeekFraction == -1) {
return false;
}
//正常情况下,
else {
mRunning = true;
startAnimation();
}
}
//调整一些时间线
...
final long currentTime = Math.max(frameTime, mStartTime);
boolean finished = animateBasedOnTime(currentTime);
if (finished) {
//动画结束,释放资源
endAnimation();
}
return finished;
}
startAnimation做一些动画开始的初始工作,notifyStartListeners会调用熟知的AnimatorListener#onAnimationStart做一些动画开始的回调。
2.13.1 ValueAnimator#startAnimation
private void startAnimation() {
...
initAnimation();
...
if (mListeners != null) {
notifyStartListeners();
}
}
private void notifyStartListeners() {
...
int numListeners = tmpListeners.size();
for (int i = 0; i < numListeners; ++i) {
tmpListeners.get(i).onAnimationStart(this, mReversing);
}
...
}
2.14 ValueAnimator#animateBasedOnTime
//根据时长规划动画
boolean animateBasedOnTime(long currentTime) {
boolean done = false;
...
if (mRunning) {
...
animateValue(currentIterationFraction);
}
return done;
}
2.15 ValueAnimator#animateValue
void animateValue(float fraction) {
// mInterpolator为插值器 fraction传入方法前为线性的,
// 通过插值器处理成想要的fraction
fraction = mInterpolator.getInterpolation(fraction);
mCurrentFraction = fraction;
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
//mValus为PropertyValuesHolder类型数组
mValues[i].calculateValue(fraction);
}
if (mUpdateListeners != null) {
int numListeners = mUpdateListeners.size();
for (int i = 0; i < numListeners; ++i) {
//熟知的onAnimationUpdate回调
mUpdateListeners.get(i).onAnimationUpdate(this);
}
}
}
animateValue中首先通过插值器对默认的fraction进行处理,获得想要动画的fraction。这里提一下插值器Interpolator,因为我大学专业是地理信息系统,专业内容有涉及过插值器。它的使用场景是:由点数据插值获得面数据。怎么理解呢?举一个之前老师讲过的一个例子,我们经常看天气预报,讲到哪里哪里的气温多少度。这都是对区域的预报,就是一个气温面数据,气温数据是连续变化的,我们不可能得到一片区域的气温数据,那么这种数据是怎么获得的呢?我们知道有气象站的存在,通过气象站来采集某个地方的气温数据,这些数据都是一个个点数据,科学的采集足够多的气温点数据,然后用这些点数据经过某种算法得到该区域的气温面数据,这种方式就是插值。所以,我对动画里的插值器还算比较理解的。放到Android动画中,插值器能使整个动画能够连续、流畅地变化。所以,无论动画变化是遵循线性、方贝塞尔曲线或是其他别的不规律变化,我们看到的动画可能变化速率有差异,但是表现在视觉上都是连续、渐变的。
2.15.1 PropertyValuesHolder#calculateValue
void calculateValue(float fraction) {
Object value = mKeyframes.getValue(fraction);
mAnimatedValue = mConverter == null ? value : mConverter.convert(value);
}
animateValue中也能看到熟悉的onAnimationUpdate的回调。我们再看一下ObjectAnimator#animateValue
2.16 ObjectAnimator#animateValue
void animateValue(float fraction) {
final Object target = getTarget();
if (mTarget != null && target == null) {
//target为null,取消动画
cancel();
return;
}
//调用ValueAnimator的animateValue方法
super.animateValue(fraction);
int numValues = mValues.length;
//遍历PropertyValuesHolder类型的数组
for (int i = 0; i < numValues; ++i) {
mValues[i].setAnimatedValue(target);
}
}
2.17 PropertyValuesHolder#setAnimatedValue
void setAnimatedValue(Object target) {
if (mIntProperty != null) {
mIntProperty.setValue(target, mIntAnimatedValue);
return;
}
if (mProperty != null) {
mProperty.set(target, mIntAnimatedValue);
return;
}
if (mJniSetter != 0) {
nCallIntMethod(target, mJniSetter, mIntAnimatedValue);
return;
}
//mSetter类型为Method
if (mSetter != null) {
try {
mTmpValueArray[0] = getAnimatedValue();
//通过反射调用 set 方法,更新属性值。
mSetter.invoke(target, mTmpValueArray);
} catch (InvocationTargetException e) {
Log.e("PropertyValuesHolder", e.toString());
} catch (IllegalAccessException e) {
Log.e("PropertyValuesHolder", e.toString());
}
}
}
在PropertyValuesHolder#setAnimatedValue中最引人注目的就是Method类型的mSetter变量,通过反射调用set方法,达到属性值更新的效果,这也就是为什么我们在自定义属性需要getter、setter方法的原因。究其本质,属性动画是通过反射达到最终效果。
最后我们接着2.13,看一下动画结束后的endAniamtion方法,
2.18 ValueAnimator#endAnimation
private void endAnimation() {
if (mAnimationEndRequested) {
return;
}
removeAnimationCallback();
mAnimationEndRequested = true;
mPaused = false;
...
//恢复动画状态变量
mRunning = false;
mStarted = false;
mStartListenersCalled = false;
mLastFrameTime = -1;
mFirstFrameTime = -1;
mStartTime = -1;
if (notify && mListeners != null) {
ArrayList<AnimatorListener> tmpListeners =
(ArrayList<AnimatorListener>) mListeners.clone();
int numListeners = tmpListeners.size();
for (int i = 0; i < numListeners; ++i) {
//onAnimationEnd回调
tmpListeners.get(i).onAnimationEnd(this, mReversing);
}
}
// mReversing needs to be reset *after* notifying the listeners for the end callbacks.
mReversing = false;
}
endAnimation开始做一些动画结束的善后工作,包括移除mAnimationCallbacks集合中已经渲染的动画,恢复动画的一些状态变量,调用可能设置的onAnimationEnd回调等。我们主要看一下removeCallback方法,
2.19 AnimationHandler#removeCallback
public void removeCallback(AnimationFrameCallback callback) {
mCommitCallbacks.remove(callback);
mDelayedCallbackStartTime.remove(callback);
int id = mAnimationCallbacks.indexOf(callback);
if (id >= 0) {
// 设置索引为id的callback为null
mAnimationCallbacks.set(id, null);
// 标识mAnimationCallbacks有数据变null
mListDirty = true;
}
}
AnimationHandler#removeCallback将绘制过的动画回调设为null。看完ValueAnimator#doAnimationFrame,我们接着看AnimationHandler#doAnimationFrame,最后的cleanUpList,
2.20 cleanUpList()
private void cleanUpList() {
//removeCallback中mListDirty刚变成true
if (mListDirty) {
//遍历mAnimationCallbacks,将null元素移除
for (int i = mAnimationCallbacks.size() - 1; i >= 0; i--) {
if (mAnimationCallbacks.get(i) == null) {
mAnimationCallbacks.remove(i);
}
}
//更新标识状态
mListDirty = false;
}
}
注意到cleanUpList方法移除了mAnimationCallbacks集合中null元素。也就是说mAnimationCallbacks的元素会逐渐变少直到0。这样也能对应到if逻辑块,最后mAnimationCallbacks容量为0,就不会请求下一垂直同步信号,也就不会继续渲染帧,也就没了动画。
if (mAnimationCallbacks.size() > 0) {
getProvider().postFrameCallback(this);
}
总结
属性动画源码分析到这就结束了,后期会绘制一个方法调用栈图(一直都没画过)。文中有误的地方,欢迎指出,一起探讨,共同进步。谢谢!