博主最近看了下属性动画执行流程相关的源码,在此写篇博客记录一下
ValueAnimator源码
我们先来看下ValueAnimator#start方法:
这里的源码版本是api28
1 |
|
从上面的方法中,我们可以得到以下几个信息:
- 我们必须在一个拥有Looper的线程中启动属性动画
- 通过ValueAnimator#addAnimationCallback设置动画每帧的回调
接下来我们来看看ValueAnimator#addAnimationCallback:
1 | private void addAnimationCallback(long delay) { |
ValueAnimator#addAnimationCallback方法调用了AnimationHandler#addAnimationFrameCallback方法,把自己注册为动画帧的回调
通过AnimationHandler#getInstance方法以及ThreadLocal变量类型我们可以看出,AnimationHandler应该是每个线程独有一份的
接下来我们来看看AnimationHandler#addAnimationFrameCallback:
1 | private final ArrayList<AnimationFrameCallback> mAnimationCallbacks = new ArrayList<>(); |
在该方法中我们可以看到,不考虑延迟的情况下,回调会被加入到mAnimationCallbacks列表中
如果是首次在AnimationHandler注册动画帧的回调,还会调用MyFrameCallbackProvider#postFrameCallback方法,即调用Choreographer#postFrameCallback方法
Choreographer源码
在跟踪接下来的代码之前,我们先来看看什么是Choreographer,他的中文意思是编舞者,官方的doc介绍如下:
1 | /** |
从上面的doc我们可以得到以下信息:
- Choreographer是用来计算动画、触摸事件输入以及绘制的时间戳的,负责回调每帧的刷新
- 每个线程都有自己的Choreographer,Choreographer会在属于该线程的Looper上工作,我们必须在一个有Looper的线程上才能创建Choreographer
接下来我们来看看Choreographer#postFrameCallback方法:
1 | public void postFrameCallback(FrameCallback callback) { |
可以看到,在调用了postFrameCallback方法后,我们的回调被加入到了mCallbackQueues列表中
值得注意的是mCallbackQueues列表是个链表结构,内部的item按照dueTime时间从小到大排列
然后如果在没有延时的情况下,则调用了scheduleFrameLocked方法准备回调帧信号
如果在有延时的情况下,则发送了一条异步handler消息,延时后再回调帧信号
这里我们继续不考虑延时的状况,看一下scheduleFrameLocked方法:
1 | private void scheduleFrameLocked(long now) { |
通过boolean值的判断我们可以看出,回调帧信号方法每次只会执行一次
当我们使用了VSYNC(垂直同步信号)的情况下,最终会调用mDisplayEventReceiver变量的scheduleVsync方法
其他情况则会使用Handler发送异步消息来执行帧回调
接下来我们来看看scheduleVsync方法:
1 | private final FrameDisplayEventReceiver mDisplayEventReceiver; |
可以看到scheduleVsync方法调用了native方法nativeScheduleVsync,这里我们就不再继续追踪了
根据代码的注释不难推断,当VSYNC同步回来时,会调用dispatchVsync方法
而dispatchVsync方法则会调用onVsync方法,然后通过发送异步消息来处理帧信号
由于Message里面设置的回调Runnable是FrameDisplayEventReceiver自己,因此最终消息会回调到run方法中,会调用doFrame方法
下面我们来看看Choreographer的doFrame方法:
1 | void doFrame(long frameTimeNanos, int frame) { |
doFrame方法比较复杂,我们这里省略一些卡顿检测与丢帧的处理
在开始与最后分别调用了AnimationUtils的lockAnimationClock与unlockAnimationClock方法,用于锁定当前的动画时间,这里我们也不深究,直接看一下如何在帧回调中处理动画、触摸事件输入以及绘制
无一例外,在处理动画、触摸事件输入以及绘制之前,都调用了FrameInfo#markXXXStart方法,这里我们看下:
1 | public void markInputHandlingStart() { |
只是存储了当前的时间戳,我们接下来看看doCallbacks方法怎么处理回调:
1 | void doCallbacks(int callbackType, long frameTimeNanos) { |
可以看到该方法首先调用了FrameInfo#extractDueCallbacksLocked,取出特定类型的已经到达指定时间的链表
然后直接调用run方法,执行相应的帧回调操作
对于属性动画而言,这里的回调是AnimationHandler#mFrameCallback,因此会执行doFrame方法:
1 | private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() { |
doFrame方法会调用doAnimationFrame方法来处理当前线程中所有属性动画的帧回调,并通过getFrameTime来获取Choreographer提供的当前帧时间
在doAnimationFrame方法中,找到所有到达执行时间的AnimationFrameCallback,调用其doAnimationFrame方法
由于属性动画ValueAnimator就实现了AnimationFrameCallback接口,这里我们直接看下ValueAnimator#doAnimationFrame:
1 | public final boolean doAnimationFrame(long frameTime) { |
在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 | private void start(boolean playBackwards) { |
从start方法可以看到,对于线程必须有Looper的限制还是有的
不同的的地方在于,这里首先把自己加入到了sPendingAnimations线程独有的列表中,然后直接使用了AnimationHandler来发送了一条ANIMATION_START的消息
这里我们继续看下AnimationHandler的代码:
1 | private static class AnimationHandler extends Handler { |
AnimationHandler在接到ANIMATION_START的消息后,首先从sPendingAnimations列表中读取出所有等待执行的属性动画
然后调用了他们的startAnimation方法:
1 | /** |
可以看到startAnimation一个是把动画自身加入到sAnimations线程独有的列表中,另外就是处理了一些动画开始相关的回调
我们在这里并没有看到帧回调相关的代码,于是我们再回到AnimationHandler#handleMessage方法中寻找答案:
1 | private static class AnimationHandler extends Handler { |
在handleMessage方法中,我们这次看下ANIMATION_FRAME类型的消息,可以看到处理这种类型的消息时,会调用animationFrame方法
而animationFrame方法又会计算出动画的进度,调用我们之前提到的animateValue方法
然后在最后由发送了一遍ANIMATION_FRAME类型的消息,开始了新一轮的消息处理
综上不难得知,ANIMATION_FRAME类型的消息就是帧回调信号的消息
值得注意的一点是,处理ANIMATION_START类型的消息时,handleMessage方法并不会break掉,因此第一个ANIMATION_FRAME类型的消息可以理解为是属性动画调用start后发出的
综上所述,api15的ValueAnimator动画执行依赖的是Handler发送消息实现的