动画源码分析(基于Android 5.0)

想要实现动画无非想做一件事情,就是把动画的对象在合适的时间画出合适的状态,如何控制时间?就是通过补间动画或者属性动画设置动画的时长、起始时间、起始延迟这些属性来控制动画的时间。如何控制动画状态?就可以通过动画插值,插值方法getInterpolation(input),input就是当前动画运行时间的百分比,而函数返回值就是当前动画的进度百分比,通过动画进度和动画的起始状态和结束状态来控制动画对象的当前状态。无论是补间动画或者属性动画都是定义动画状态的起始状态、终止状态、动画多久、如何动画。

0x01 补间动画源码

先找到动画执行的入口View.startAnimation(animation),startAnimation执行了invalidate,触发了重绘刷新视图界面。

1
2
3
4
5
6
public void startAnimation(Animation animation) {
animation.setStartTime(Animation.START_ON_FIRST_FRAME);
setAnimation(animation);
invalidateParentCaches();
invalidate(true);
}

invalidate()触发执行View.draw(Canvas canvas, ViewGroup parent, long drawingTime)函数

  • 调用动画的 getTransformation 方法,得到当前时间点的矩阵
  • 将该矩阵设置成 Canvas 的当前矩阵
  • 调用 canvas 的 drawBitmap 方法,绘制屏幕
  • 判断 getTransformation 的返回值,若为真,调用 invalidate 方法,刷新屏幕进入下一桢;若为假,说明动画完成。
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
74
75
76
boolean draw(Canvas canvas, ViewGroup parent, long drawingTime) {
......
// 如果有需要执行的动画
final Animation a = getAnimation();
if (a != null) {
// 根据动画和当前时间计算Transformation,并返回是否需要再继续做动画
more = drawAnimation(parent, drawingTime, a, scalingRequired);
concatMatrix = a.willChangeTransformationMatrix();
if (concatMatrix) {
mPrivateFlags3 |= PFLAG3_VIEW_IS_ANIMATING_TRANSFORM;
}
// 得到动画的变换结果
transformToApply = parent.mChildTransformation;
} else {
......
}

......

float alpha = useDisplayListProperties ? 1 : getAlpha();
if (transformToApply != null || alpha < 1 || !hasIdentityMatrix() ||
(mPrivateFlags3 & PFLAG3_VIEW_IS_ANIMATING_ALPHA) == PFLAG3_VIEW_IS_ANIMATING_ALPHA) {
......
// 根据Transformation值变换canvs
if (transformToApply != null) {
if (concatMatrix) {
if (useDisplayListProperties) {
displayList.setAnimationMatrix(transformToApply.getMatrix());
} else {
// Undo the scroll translation, apply the transformation matrix,
// then redo the scroll translate to get the correct result.
canvas.translate(-transX, -transY);
canvas.concat(transformToApply.getMatrix());
canvas.translate(transX, transY);
}
parent.mGroupFlags |= ViewGroup.FLAG_CLEAR_TRANSFORMATION;
}

float transformAlpha = transformToApply.getAlpha();
if (transformAlpha < 1) {
alpha *= transformAlpha;
parent.mGroupFlags |= ViewGroup.FLAG_CLEAR_TRANSFORMATION;
}

if (!childHasIdentityMatrix && !useDisplayListProperties) {
canvas.translate(-transX, -transY);
canvas.concat(getMatrix());
canvas.translate(transX, transY);
}
}
......
}

if (a != null && !more) {
if (!hardwareAccelerated && !a.getFillAfter()) {
onSetAlpha(255);
}
parent.finishAnimatingView(this, a);
}

// 根据动画进度判断是否需要继续重绘
if (more && hardwareAccelerated) {
// invalidation is the trigger to recreate display lists, so if we're using
// display lists to render, force an invalidate to allow the animation to
// continue drawing another frame
parent.invalidate(true);
if (a.hasAlpha() && (mPrivateFlags & PFLAG_ALPHA_SET) == PFLAG_ALPHA_SET) {
// alpha animations should cause the child to recreate its display list
invalidate(true);
}
}

mRecreateDisplayList = false;

return more;
}

0x02 属性动画源码

onsync时序图:
choreographer onVsync时序图

从ObjectAnimator的start()方法开始,跳转到父类ValueAnimator的start()方法,为了减少篇幅,这里的代码就不贴出来了,详细的代码可以查看从ObjectAnimator.java。

1
2
3
4
5
6
7
8
@Override
public void start() {
// See if any of the current active/pending animators need to be canceled
// 这里先判断当前的动画、等待的动画和延迟执行的动画和this去比较,如果动画的目标对象相同并且属性相同就取消相同的动画。
......

super.start();
}

ValueAnimator的start(boolean)方法只是做了两件事:
1.把当前的animator加入等待队列中,
2.启动animationHandler。AnimationHandler并不是一个Handler,而是Runnable。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
 private void start(boolean playBackwards) {
if (Looper.myLooper() == null) {
throw new AndroidRuntimeException("Animators may only be run on Looper threads");
}

......

AnimationHandler animationHandler = getOrCreateAnimationHandler();
animationHandler.mPendingAnimations.add(this);
if (mStartDelay == 0) {
// This sets the initial value of the animation, prior to actually starting it running
setCurrentPlayTime(0);
mPlayingState = STOPPED;
mRunning = true;
notifyStartListeners();
}
animationHandler.start();
}

将AnimationHandler对象传递给Choreographer(编舞者),Choreographer是和线程绑定的单例。告诉编舞者,我是一个CALLBACK_ANIMATION(动画类型),你自己看着办吧。

1
2
3
4
5
6
private void scheduleAnimation() {
if (!mAnimationScheduled) {
mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, this, null);
mAnimationScheduled = true;
}
}

编舞者根据callbackType(就是上面的CALLBACK_ANIMATION类型),把当前的action(就是animationHandler)加入相应类型的Callback队列等待被执行。并且安排下一帧的时间。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private void postCallbackDelayedInternal(int callbackType,
Object action, Object token, long delayMillis) {

synchronized (mLock) {
final long now = SystemClock.uptimeMillis();
final long dueTime = now + delayMillis;
// 加入相应类型的Callback队列等待被执行
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);
}
}
}

如果使用Vsync,通过FrameDisplayEventReceiver的scheduleVsync方法,申请一次VSYNC中断,这样就可以在中断处理的onVsync函数去进行绘制。如果不需要直接发送MSG_DO_FRAME消息。

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
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.
// 如果使用VSYNC就申请一次中断请求
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);
// 如果不是要VSYNC,就直接触发MSG_DO_FRAME
Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, nextFrameTime);
}
}
}

上面注册一个了reciver,在VSYNC发生的时候接受到消息,就会触发onVsync方法,执行当前的FrameDisplayEventReceiver Runnable。

1
2
3
4
5
6
7
8
9
10
11
12
public void onVsync(long timestampNanos, int builtInDisplayId, int 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);
}

我们看doFrame方法做了什么?调用doCallbacks方法,获取当前类型的等待序列中在当前时间的callback,得到action并执行run方法,在这里callback的action就是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
 void doFrame(long frameTimeNanos, int frame) {
......
// 优先级最高,和输入事件处理有关
doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
// 优先级其次,和Animation的处理有关
doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
// 优先级最低,和UI等控件绘制有关
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
}


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 = SystemClock.uptimeMillis();
callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(now);
if (callbacks == null) {
return;
}
mCallbacksRunning = true;
}
try {
for (CallbackRecord c = callbacks; c != null; c = c.next) {
// 在这里回调callback的action
c.run(frameTimeNanos);
}
}
......
}

终于回调了AnimationHandler的run方法,代码很多,起始无非就是让应该启动的animation启动起来,该延迟的放在延迟队列,正在做动画的调用doAnimationFrame方法,动画播放结束的就结束动画。当然如果还需要继续动画那就继续调用scheduleAnimation方法,告诉编舞者,还有呢,继续在下个帧时间执行我。当然所有时间控制是根据VSYNC回调上来的时间和每个动画的时间信息共同控制的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
 private void doAnimationFrame(long frameTime) {
......
for (int i = 0; i < numAnims; ++i) {
ValueAnimator anim = mTmpAnimations.get(i);
if (mAnimations.contains(anim) && anim.doAnimationFrame(frameTime)) {
mEndingAnims.add(anim);
}
}
......
// If there are still active or delayed animations, schedule a future call to
// onAnimate to process the next frame of the animations.
if (!mAnimations.isEmpty() || !mDelayedAnims.isEmpty()) {
scheduleAnimation();
}
}

0x03 动画源码总结

1.针对视图来说,Animation和Scroller相似,都是如何控制视图的重绘,它们都指示定义了在某一个特定时刻我应该画成什么样子。
2.Choreographer实际上是视图所有重绘进度的实际控制者,因为它在控制着时间节奏,告诉应用们什么时间可以画,视图不需要自己控制刷新时间。
3.回头看Animation的源码分析,视图的重绘最终是触发ViewRootImpl的invalidate函数,该函数将最终调用ViewRootImpl的scheduleTraversals。同样是通过mChoreographer只是类型变成了CALLBACK_TRAVERSAL,代码如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().postSyncBarrier();
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
}
}

4.Asynchronous Message和Synchronization Barrier我还没有理解,当你觉得自己明白了的时候,其实你还有很多不知道,当你觉得你不明白的时候,那是你真的不知道。不知道还是不知道!!

转载请标明出处病已blog https://ivonhoe.github.io/