属性动画执行流程探究

博主最近看了下属性动画执行流程相关的源码,在此写篇博客记录一下

ValueAnimator源码

我们先来看下ValueAnimator#start方法:
这里的源码版本是api28

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
@Override
public void start() {
start(false);
}

private void start(boolean playBackwards) {
if (Looper.myLooper() == null) {
throw new AndroidRuntimeException("Animators may only be run on Looper threads");
}
...
addAnimationCallback(0);

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

从上面的方法中,我们可以得到以下几个信息:

  1. 我们必须在一个拥有Looper的线程中启动属性动画
  2. 通过ValueAnimator#addAnimationCallback设置动画每帧的回调

接下来我们来看看ValueAnimator#addAnimationCallback:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private void addAnimationCallback(long delay) {
if (!mSelfPulse) {
return;
}
getAnimationHandler().addAnimationFrameCallback(this, delay);
}

public AnimationHandler getAnimationHandler() {
return AnimationHandler.getInstance();
}

public final static ThreadLocal<AnimationHandler> sAnimatorHandler = new ThreadLocal<>();

public static AnimationHandler getInstance() {
if (sAnimatorHandler.get() == null) {
sAnimatorHandler.set(new AnimationHandler());
}
return sAnimatorHandler.get();
}

ValueAnimator#addAnimationCallback方法调用了AnimationHandler#addAnimationFrameCallback方法,把自己注册为动画帧的回调
通过AnimationHandler#getInstance方法以及ThreadLocal变量类型我们可以看出,AnimationHandler应该是每个线程独有一份的

接下来我们来看看AnimationHandler#addAnimationFrameCallback:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
private final ArrayList<AnimationFrameCallback> mAnimationCallbacks = new ArrayList<>();

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

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

private class MyFrameCallbackProvider implements AnimationFrameCallbackProvider {

final Choreographer mChoreographer = Choreographer.getInstance();

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

@Override
public void postCommitCallback(Runnable runnable) {
mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT, runnable, null);
}

@Override
public long getFrameTime() {
return mChoreographer.getFrameTime();
}

@Override
public long getFrameDelay() {
return Choreographer.getFrameDelay();
}

@Override
public void setFrameDelay(long delay) {
Choreographer.setFrameDelay(delay);
}
}

在该方法中我们可以看到,不考虑延迟的情况下,回调会被加入到mAnimationCallbacks列表中
如果是首次在AnimationHandler注册动画帧的回调,还会调用MyFrameCallbackProvider#postFrameCallback方法,即调用Choreographer#postFrameCallback方法

Choreographer源码

在跟踪接下来的代码之前,我们先来看看什么是Choreographer,他的中文意思是编舞者,官方的doc介绍如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
/**
* Coordinates the timing of animations, input and drawing.
* <p>
* The choreographer receives timing pulses (such as vertical synchronization)
* from the display subsystem then schedules work to occur as part of rendering
* the next display frame.
* </p><p>
* Applications typically interact with the choreographer indirectly using
* higher level abstractions in the animation framework or the view hierarchy.
* Here are some examples of things you can do using the higher-level APIs.
* </p>
* <ul>
* <li>To post an animation to be processed on a regular time basis synchronized with
* display frame rendering, use {@link android.animation.ValueAnimator#start}.</li>
* <li>To post a {@link Runnable} to be invoked once at the beginning of the next display
* frame, use {@link View#postOnAnimation}.</li>
* <li>To post a {@link Runnable} to be invoked once at the beginning of the next display
* frame after a delay, use {@link View#postOnAnimationDelayed}.</li>
* <li>To post a call to {@link View#invalidate()} to occur once at the beginning of the
* next display frame, use {@link View#postInvalidateOnAnimation()} or
* {@link View#postInvalidateOnAnimation(int, int, int, int)}.</li>
* <li>To ensure that the contents of a {@link View} scroll smoothly and are drawn in
* sync with display frame rendering, do nothing. This already happens automatically.
* {@link View#onDraw} will be called at the appropriate time.</li>
* </ul>
* <p>
* However, there are a few cases where you might want to use the functions of the
* choreographer directly in your application. Here are some examples.
* </p>
* <ul>
* <li>If your application does its rendering in a different thread, possibly using GL,
* or does not use the animation framework or view hierarchy at all
* and you want to ensure that it is appropriately synchronized with the display, then use
* {@link Choreographer#postFrameCallback}.</li>
* <li>... and that's about it.</li>
* </ul>
* <p>
* Each {@link Looper} thread has its own choreographer. Other threads can
* post callbacks to run on the choreographer but they will run on the {@link Looper}
* to which the choreographer belongs.
* </p>
*/
public final class Choreographer {

// Thread local storage for the SF choreographer.
private static final ThreadLocal<Choreographer> sSfThreadInstance =
new ThreadLocal<Choreographer>() {
@Override
protected Choreographer initialValue() {
Looper looper = Looper.myLooper();
if (looper == null) {
throw new IllegalStateException("The current thread must have a looper!");
}
return new Choreographer(looper, VSYNC_SOURCE_SURFACE_FLINGER);
}
};

从上面的doc我们可以得到以下信息:

  1. Choreographer是用来计算动画、触摸事件输入以及绘制的时间戳的,负责回调每帧的刷新
  2. 每个线程都有自己的Choreographer,Choreographer会在属于该线程的Looper上工作,我们必须在一个有Looper的线程上才能创建Choreographer

接下来我们来看看Choreographer#postFrameCallback方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
public void postFrameCallback(FrameCallback callback) {
postFrameCallbackDelayed(callback, 0);
}

public void postFrameCallbackDelayed(FrameCallback callback, long delayMillis) {
if (callback == null) {
throw new IllegalArgumentException("callback must not be null");
}

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

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

可以看到,在调用了postFrameCallback方法后,我们的回调被加入到了mCallbackQueues列表中
值得注意的是mCallbackQueues列表是个链表结构,内部的item按照dueTime时间从小到大排列
然后如果在没有延时的情况下,则调用了scheduleFrameLocked方法准备回调帧信号
如果在有延时的情况下,则发送了一条异步handler消息,延时后再回调帧信号
这里我们继续不考虑延时的状况,看一下scheduleFrameLocked方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
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);
}
}
}

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

通过boolean值的判断我们可以看出,回调帧信号方法每次只会执行一次
当我们使用了VSYNC(垂直同步信号)的情况下,最终会调用mDisplayEventReceiver变量的scheduleVsync方法
其他情况则会使用Handler发送异步消息来执行帧回调

接下来我们来看看scheduleVsync方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
  private final FrameDisplayEventReceiver mDisplayEventReceiver;

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

@FastNative
private static native void nativeScheduleVsync(long receiverPtr);

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

private final class FrameDisplayEventReceiver extends DisplayEventReceiver
implements Runnable {
...

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

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

可以看到scheduleVsync方法调用了native方法nativeScheduleVsync,这里我们就不再继续追踪了
根据代码的注释不难推断,当VSYNC同步回来时,会调用dispatchVsync方法
而dispatchVsync方法则会调用onVsync方法,然后通过发送异步消息来处理帧信号
由于Message里面设置的回调Runnable是FrameDisplayEventReceiver自己,因此最终消息会回调到run方法中,会调用doFrame方法

下面我们来看看Choreographer的doFrame方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
void doFrame(long frameTimeNanos, int frame) {
final long startNanos;
synchronized (mLock) {
...

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

...
}

doFrame方法比较复杂,我们这里省略一些卡顿检测与丢帧的处理
在开始与最后分别调用了AnimationUtils的lockAnimationClock与unlockAnimationClock方法,用于锁定当前的动画时间,这里我们也不深究,直接看一下如何在帧回调中处理动画、触摸事件输入以及绘制
无一例外,在处理动画、触摸事件输入以及绘制之前,都调用了FrameInfo#markXXXStart方法,这里我们看下:

1
2
3
4
5
6
7
8
9
10
11
public void markInputHandlingStart() {
mFrameInfo[HANDLE_INPUT_START] = System.nanoTime();
}

public void markAnimationsStart() {
mFrameInfo[ANIMATION_START] = System.nanoTime();
}

public void markPerformTraversalsStart() {
mFrameInfo[PERFORM_TRAVERSALS_START] = System.nanoTime();
}

只是存储了当前的时间戳,我们接下来看看doCallbacks方法怎么处理回调:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
void doCallbacks(int callbackType, long frameTimeNanos) {
CallbackRecord callbacks;
synchronized (mLock) {
// We use "now" to determine when callbacks become due because it's possible
// for earlier processing phases in a frame to post callbacks that should run
// in a following phase, such as an input event that causes an animation to start.
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) {
if (DEBUG_FRAMES) {
Log.d(TAG, "RunCallback: type=" + callbackType
+ ", action=" + c.action + ", token=" + c.token
+ ", latencyMillis=" + (SystemClock.uptimeMillis() - c.dueTime));
}
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);
}
}

public CallbackRecord extractDueCallbacksLocked(long now) {
CallbackRecord callbacks = mHead;
if (callbacks == null || callbacks.dueTime > now) {
return null;
}

CallbackRecord last = callbacks;
CallbackRecord next = last.next;
while (next != null) {
if (next.dueTime > now) {
last.next = null;
break;
}
last = next;
next = next.next;
}
mHead = next;
return callbacks;
}

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

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

可以看到该方法首先调用了FrameInfo#extractDueCallbacksLocked,取出特定类型的已经到达指定时间的链表
然后直接调用run方法,执行相应的帧回调操作
对于属性动画而言,这里的回调是AnimationHandler#mFrameCallback,因此会执行doFrame方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
doAnimationFrame(getProvider().getFrameTime());
if (mAnimationCallbacks.size() > 0) {
getProvider().postFrameCallback(this);
}
}
};

@Override
public long getFrameTime() {
return mChoreographer.getFrameTime();
}

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)) {
callback.doAnimationFrame(frameTime);
if (mCommitCallbacks.contains(callback)) {
getProvider().postCommitCallback(new Runnable() {
@Override
public void run() {
commitAnimationFrame(callback, getProvider().getFrameTime());
}
});
}
}
}
cleanUpList();
}

doFrame方法会调用doAnimationFrame方法来处理当前线程中所有属性动画的帧回调,并通过getFrameTime来获取Choreographer提供的当前帧时间
在doAnimationFrame方法中,找到所有到达执行时间的AnimationFrameCallback,调用其doAnimationFrame方法
由于属性动画ValueAnimator就实现了AnimationFrameCallback接口,这里我们直接看下ValueAnimator#doAnimationFrame:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
public final boolean doAnimationFrame(long frameTime) {
...

if (mLastFrameTime < 0) {
if (mSeekFraction >= 0) {
long seekTime = (long) (getScaledDuration() * mSeekFraction);
mStartTime = frameTime - seekTime;
mSeekFraction = -1;
}
mStartTimeCommitted = false; // allow start time to be compensated for jank
}
mLastFrameTime = frameTime;
// The frame time might be before the start time during the first frame of
// an animation. The "current time" must always be on or after the start
// time to avoid animating frames at negative time intervals. In practice, this
// is very rare and only happens when seeking backwards.
final long currentTime = Math.max(frameTime, mStartTime);
boolean finished = animateBasedOnTime(currentTime);

...
return finished;
}

boolean animateBasedOnTime(long currentTime) {
boolean done = false;
if (mRunning) {
...
animateValue(currentIterationFraction);
}
return done;
}

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

在doAnimationFrame方法中,这里我们省略了一系列各种开始停止暂停状态的处理,可以看到计算出时间后,调用了animateBasedOnTime方法执行动画
然后在animateBasedOnTime方法中,我们计算出来当前实际的进度currentIterationFraction,并传递给了animateValue执行动画
在animateValue方法中,通过插值器就算出属性变化的实际量之后,调用了onAnimationUpdate方法,执行动画更新的回调

至此,属性动画执行的流程至此结束

api15的ValueAnimator源码

我们上面分析的源码是api28的版本,可以看到ValueAnimator属性动画的执行流程实现是依赖于Choreographer来进行帧回调的
然而,我们经过api的接口查询可以发现,ValueAnimator属性动画是api11加入的,而Choreographer则是api16加入的
https://developer.android.com/reference/android/animation/ValueAnimator
https://developer.android.com/reference/android/view/Choreographer

那么在Choreographer加入之前,ValueAnimator属性动画的执行流程又是怎样的呢?

这里我们找到了api15的ValueAnimator源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
private void start(boolean playBackwards) {
if (Looper.myLooper() == null) {
throw new AndroidRuntimeException("Animators may only be run on Looper threads");
}
...
sPendingAnimations.get().add(this);
...
AnimationHandler animationHandler = sAnimationHandler.get();
if (animationHandler == null) {
animationHandler = new AnimationHandler();
sAnimationHandler.set(animationHandler);
}
animationHandler.sendEmptyMessage(ANIMATION_START);
}

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

// The per-thread set of animations to be started on the next animation frame
private static final ThreadLocal<ArrayList<ValueAnimator>> sPendingAnimations =
new ThreadLocal<ArrayList<ValueAnimator>>() {
@Override
protected ArrayList<ValueAnimator> initialValue() {
return new ArrayList<ValueAnimator>();
}
};

从start方法可以看到,对于线程必须有Looper的限制还是有的
不同的的地方在于,这里首先把自己加入到了sPendingAnimations线程独有的列表中,然后直接使用了AnimationHandler来发送了一条ANIMATION_START的消息
这里我们继续看下AnimationHandler的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
private static class AnimationHandler extends Handler {

@Override
public void handleMessage(Message msg) {
boolean callAgain = true;
ArrayList<ValueAnimator> animations = sAnimations.get();
ArrayList<ValueAnimator> delayedAnims = sDelayedAnims.get();
switch (msg.what) {
// TODO: should we avoid sending frame message when starting if we
// were already running?
case ANIMATION_START:
ArrayList<ValueAnimator> pendingAnimations = sPendingAnimations.get();
if (animations.size() > 0 || delayedAnims.size() > 0) {
callAgain = false;
}
// pendingAnims holds any animations that have requested to be started
// We're going to clear sPendingAnimations, but starting animation may
// cause more to be added to the pending list (for example, if one animation
// starting triggers another starting). So we loop until sPendingAnimations
// is empty.
while (pendingAnimations.size() > 0) {
ArrayList<ValueAnimator> pendingCopy =
(ArrayList<ValueAnimator>) pendingAnimations.clone();
pendingAnimations.clear();
int count = pendingCopy.size();
for (int i = 0; i < count; ++i) {
ValueAnimator anim = pendingCopy.get(i);
// If the animation has a startDelay, place it on the delayed list
if (anim.mStartDelay == 0) {
anim.startAnimation();
} else {
delayedAnims.add(anim);
}
}
}
// fall through to process first frame of new animations
...
}
}
}

AnimationHandler在接到ANIMATION_START的消息后,首先从sPendingAnimations列表中读取出所有等待执行的属性动画
然后调用了他们的startAnimation方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* Called internally to start an animation by adding it to the active animations list. Must be
* called on the UI thread.
*/
private void startAnimation() {
initAnimation();
sAnimations.get().add(this);
if (mStartDelay > 0 && mListeners != null) {
// Listeners were already notified in start() if startDelay is 0; this is
// just for delayed animations
ArrayList<AnimatorListener> tmpListeners =
(ArrayList<AnimatorListener>) mListeners.clone();
int numListeners = tmpListeners.size();
for (int i = 0; i < numListeners; ++i) {
tmpListeners.get(i).onAnimationStart(this);
}
}
}

可以看到startAnimation一个是把动画自身加入到sAnimations线程独有的列表中,另外就是处理了一些动画开始相关的回调
我们在这里并没有看到帧回调相关的代码,于是我们再回到AnimationHandler#handleMessage方法中寻找答案:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
private static class AnimationHandler extends Handler {

@Override
public void handleMessage(Message msg) {
boolean callAgain = true;
ArrayList<ValueAnimator> animations = sAnimations.get();
ArrayList<ValueAnimator> delayedAnims = sDelayedAnims.get();
switch (msg.what) {
case ANIMATION_START:
...
case ANIMATION_FRAME:
...

// Now process all active animations. The return value from animationFrame()
// tells the handler whether it should now be ended
int numAnims = animations.size();
int i = 0;
while (i < numAnims) {
ValueAnimator anim = animations.get(i);
if (anim.animationFrame(currentTime)) {
endingAnims.add(anim);
}
if (animations.size() == numAnims) {
++i;
} else {
// An animation might be canceled or ended by client code
// during the animation frame. Check to see if this happened by
// seeing whether the current index is the same as it was before
// calling animationFrame(). Another approach would be to copy
// animations to a temporary list and process that list instead,
// but that entails garbage and processing overhead that would
// be nice to avoid.
--numAnims;
endingAnims.remove(anim);
}
}
if (endingAnims.size() > 0) {
for (i = 0; i < endingAnims.size(); ++i) {
endingAnims.get(i).endAnimation();
}
endingAnims.clear();
}

// If there are still active or delayed animations, call the handler again
// after the frameDelay
if (callAgain && (!animations.isEmpty() || !delayedAnims.isEmpty())) {
sendEmptyMessageDelayed(ANIMATION_FRAME, Math.max(0, sFrameDelay -
(AnimationUtils.currentAnimationTimeMillis() - currentTime)));
}
break;
}
}
}

boolean animationFrame(long currentTime) {
boolean done = false;

...
switch (mPlayingState) {
case RUNNING:
case SEEKED:
...
animateValue(fraction);
break;
}

return done;
}

在handleMessage方法中,我们这次看下ANIMATION_FRAME类型的消息,可以看到处理这种类型的消息时,会调用animationFrame方法
而animationFrame方法又会计算出动画的进度,调用我们之前提到的animateValue方法
然后在最后由发送了一遍ANIMATION_FRAME类型的消息,开始了新一轮的消息处理
综上不难得知,ANIMATION_FRAME类型的消息就是帧回调信号的消息

值得注意的一点是,处理ANIMATION_START类型的消息时,handleMessage方法并不会break掉,因此第一个ANIMATION_FRAME类型的消息可以理解为是属性动画调用start后发出的

综上所述,api15的ValueAnimator动画执行依赖的是Handler发送消息实现的