属性动画的启动分析
在本文中,我们会分析属性动画如何启动的而且和Andoid 黄油计划有什么关系
我们看看 ObjectAnimator.start()
1 | public void start() { |
AnimationHandler.getInstance().autoCancelBasedOn(this) cancel 相同Target和相同属性的动画
AnimationHandler 实例在线程局部单例。autoCancelBasedOn(this)会遍历 AnimationHandler实例持有的所有未完成的 ValueAnimator实例,cancel 掉符合条件的动画。
紧接着 super.start() 调用了ValueAnimator.start()
1 |
|
调用了带参数的 start方法
1 | private void start(boolean playBackwards) { |
我们略去与主流程无关的逻辑代码。这个方法标记了动画的状态,如
1 | mStarted = true; |
然后这个方法结束啦,有没有很疑惑?有没有很懵逼?动画怎么执行的?什么时候执行的?
要解答这问题,我们还是要分析 这两行不起眼的代码
1 | AnimationHandler animationHandler = AnimationHandler.getInstance(); |
第一行在当前线程获取 AnimationHandler实例;
第二行注册了一个callback。
1 | /** |
这两行代码 做了一件事情:保证一个ValueAnimator对象只向Provider注册一次 mFrameCallback
瞅瞅 getProvider是啥?
1 | private AnimationFrameCallbackProvider getProvider() { |
创建了一个 MyFrameCallbackProvider实例, MyFrameCallbackProvider 继承 AnimationFrameCallbackProvider
1 | public interface AnimationFrameCallbackProvider { |
AnimationFrameCallbackProvider接口定义了一些回调接口,按照注释说明主要作用是 提高 ValueAnimator的可测性,通过这个接口隔离,我们可以自定义 定时脉冲,而不用使用系统默认的 Choreographer,这样我们可以在测试中使用任意的时间间隔的定时脉冲.既然可以方便测试,那肯定有API来更改Provider 吧?
果不其然,我们猜对了。
1 | public void setProvider(AnimationFrameCallbackProvider provider) { |
ValueAnimator提供了一个 setProvider 通过自定义的Provider 提供我们想要的任意时间间隔的回调,来更新动画。
明白了 接口AnimationFrameCallbackProvider的作用,也知道了一个新的名词Choreographer,它就是 Android 黄油计划 的核心。使用vsync(垂直同步)来协调View的绘制和动画的执行间隔。关于 Choreographer 在文章最后会做进一步解释
我们知道了默认情况下系统使用Choreographer,我们可以简单的认为 它是一个与 绘制和动画有关的 消息处理器 。
继续我们的 代码 AnimationHandler.addAnimationFrameCallback
1 | public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) { |
执行了 getProvider().postFrameCallback(mFrameCallback) 通过上面的分析我们知道 getProvider() 得到的是一个MyFrameCallbackProvider
1 | /** |
注释说明系统默认使用的 Choreographer做定时脉冲来协调 frame 的更新 。
MyFrameCallbackProvider.postFrameCallback()方法调用了mChoreographer.postFrameCallback(callback) 这里说明一下 Choreographer 实例也是线程局部单例的。从这些信息中我们知道了动画可以在子线程中执行的(注意:这不意味着可以在子线程更新UI),但是这个子线程必须有Looper。
接着分析 Choreographer.postFrameCallback方法
1 | public void postFrameCallback(FrameCallback callback) { |
默认情况下USE_VSYNC为true,当然设备厂商也可以设置为 false。如果 为false 会以 10 ms 为间隔计算下一次 doFrame 的时间,然后使用Handler来处理。
我们看看 scheduleVsyncLocked() 这行代码做了何事?
1 | private void scheduleVsyncLocked() { |
mDisplayEventReceiver 当USE_VSYNC为true 时 实例化成 FrameDisplayEventReceiver对象 ,它主要是和 native层 做交互,协调 vsync 信号。
mDisplayEventReceiver.scheduleVsync() 请求当下一帧开始时同步vsync信号。
当vsync 信号来时 回调 JNI 层会回调DisplayEventReceiver.dispatchVsync方法
1 | // Called from native code. |
FrameDisplayEventReceiver实现了doVsync方法
1 | private final class FrameDisplayEventReceiver extends DisplayEventReceiver |
主要实现:根据JNI层的 timestampNanos纳秒值计算成毫秒,通过 Handler 执行
Runable,即执行了doFrame(mTimestampNanos, mFrame);这行代码。
1 | void doFrame(long frameTimeNanos, int frame) { |
与动画相关的关键代码:
1 | mFrameInfo.markAnimationsStart(); |
以下是 doCallbacks的实现,有没有感觉即将看见胜利的曙光?
1 | void doCallbacks(int callbackType, long frameTimeNanos) { |
所以该方法最核心的功能是找到需要执行的 CallbackRecord链表,然后循环执行 它们的 run方法。
1 | private static final class CallbackRecord { |
在前面 注册回调postFrameCallbackDelayed时 token 是 FRAME_CALLBACK_TOKEN 所以执行 run 方法中的第一个if 分支((FrameCallback)action).doFrame(frameTimeNanos);
回调 FrameCallback.doFrame(frameTimeNanos) 方法。
下面是 Choreographer.FrameCallback 的实现
1 | private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() { |
看到doAnimationFrame(getProvider().getFrameTime());这一行代码,我们已经确认,我们的动画逻辑即将开始了。
我们先搁置doAnimationFrame这行代码,先分析完 doFrame 回调。
1 | if (mAnimationCallbacks.size() > 0) { |
这里如果 mAnimationCallbacks.size >0会再次将该 callback 注册给 Choreographer。为什么还要注册一次呢?之前不是注册过一次了吗?难道Choreographer把这个callback 释放了。
我们回到Choreographer.doCallbacks方法。
有一段 final 括号体中的代码我们没有分析
1 | finally { |
这里调用了recycleCallbackLocked(callbacks);来回收整个链表中的节点记录。
1 | private void recycleCallbackLocked(CallbackRecord callback) { |
当 mAnimationCallbacks.size >0时 需要再次添加回调,以便来获取下一帧的回调。
当动画 paused 或 end 后 mAnimationCallbacks 会相应的remove callback。
小结1
到目前为止 我们知道了当调用 ObjectAnimator.start()后:
- 取消之前的动画对相同属性,相同target的动画,防止出现多个动画同时更新 Target 的属性,出现错乱。不过这一行为默认是关闭的,设置
ObjectAnimator.setAutoCancel(true)来打开; - 执行
ValueAnimator.start()方法; AnimationHandler.addAnimationFrameCallback向Choreographer注册Choreographer.FrameCallback回调,通过该回调获得渲染时间脉冲的回调;- 通过系统的
vsync垂直同步信号来协调 cpu,gpu 和渲染的时序; Choreographer获得vsync信号后 根据 当前帧的纳秒来查找哪些Choreographer.FrameCallback会被执行。- 执行
AnimationHandler.doAnimationFrame()方法,开始真正的动画逻辑。
动画属性更新
刚才我们分析到 AnimationHandler.doAnimationFrame() 方法,现在看看这个方法的功能是什么?
1 | private void doAnimationFrame(long frameTime) { |
该方法主要作用是遍历所有的 AnimationFrameCallback。 这个AnimationFrameCallback 是什么?什么时候添加到mAnimationCallbacks的?
刚才分析Choreographer.FrameCallback.doFrame时提到过,在动画paused 或 end 时会将AnimationFrameCallback 从mAnimationCallbacks中移除,如果mAnimationCallbacks的size 为0 就不再向Choreographer注册 callback。也就代表没有动画要被执行了。
其实 AnimationFrameCallback就是 ObjectAnimator或ValueAnimaor本身。我们看看 ValueAnimator的定义:
1 | public class ValueAnimator extends Animator implements AnimationHandler.AnimationFrameCallback { |
所以 这些AnimationFrameCallback就是那些待执行的 属性动画。
接下来看看isCallbackDue(callback, currentTime)这个 if 判断:
1 | private boolean isCallbackDue(AnimationFrameCallback callback, long currentTime) { |
根据 当前的的 纳秒时间 判断 动画是否需要执行,因为有些动画做了delay可能在当前的frame 窗口不需要执行。callback.doAnimationFrame(frameTime); 这行代码 开始回调 ValueAnimator.doAnimationFrame方法。然后判断if (mCommitCallbacks.contains(callback)) 为 true的话会再次向Choreographer注册一个Runnable的callback,当下一个 frame 时间到来时 执行 Runnable。
我们关注重点代码callback.doAnimationFrame(frameTime) 也即是ValueAnimator.doAnimationFrame(frameTime)
1 | public final void doAnimationFrame(long frameTime) { |
当时第一帧动画时 会调整到下一个 frame 窗口执行。如果这个动画是 delay 动画,会执行startAnimation()初始化动画,标记动画正在 mRunning,并且 对PropertyValuesHolder执行初始化操作–主要就是初始化估值器。
下面分析 boolean finished = animateBasedOnTime(currentTime);
返回值 finished标记是否动画已经执行完毕。如果最后一个关键帧(Keyframe)执行完毕,这里返回true,会执行endAnimation()做一些状态位复位和动画结束回调等等。
1 | boolean animateBasedOnTime(long currentTime) { |
currentTime是Choreographer发出的计时脉冲时间,纳秒计时。
根据 currentTime 计算 fraction系数,即动画时间流逝比。
然后执行animateValue(currentIterationFraction) 计算动画的在当前时间比例下属性动画的值,如果是 ObjectAnimator还会降属性值设置该Target。animateValue方法被 ObjectAnimator 重载了。我们先分析ValueAnimator. animateValue(fraction) 然后再分析ObjectAnimator. animateValue(fraction)
1 | void animateValue(float fraction) { |
参数 fraction这个时间流逝比系数 是线性的。通过mInterpolator.getInterpolation()计算出我们想要的fraction。然后使用这个系数计算 PropertyValuesHolder.calculateValue(fraction)。
计算完 属性值后 执行 mUpdateListeners的更新操作。到目前为止我们终于知道我们经常使用的 AnimatorUpdateListener.onAnimationUpdate()何时执行的了。
我们再分析 被 ObjectAnimator重载后的animateValue(fraction)方法
1 | void animateValue(float fraction) { |
主要功能是 通过调用 super.animateValue()计算属性的值。然后调用PropertyValuesHolder.setAnimatedValue(Object)来更新属性值到对应的Target上。
深入理解Android属性动画的实现-1 这篇文章中介绍过 PropertyValuesHolder具有更新属性的能力,也持有关键帧的引用。
1 | void setAnimatedValue(Object target) { |
通过 Property更新属性值。如果之前是通过 propertyName来初始化的动画,这里通过 mSetter来反射调用 set 方法,更新属性值。
深入理解Android属性动画的实现-1 这篇文章还介绍了如何通过fraction计算动画的属性值。这里就不在赘述。
总结
到目前位置我们已经分析完 属性动画的创建—> 属性动画的启动 –> 属性的计算和更新。
这里我们再回顾一下:
ObjectAnimator.start()开始启动动画;- 向
Choreographer注册callback; Choreographer获得vsync垂直同步信号量后回调Choreographer.FrameCallback.doFrame()执行逻辑进入到AnimationHandler中;AnimationHandler持有AnimationFrameCallback也即是ValueAnimator,然后执行ValueAnimator.doAnimationFrame(time);ValueAnimator.animateBasedOnTime(time)执行,通过TimeInterpolator计算最终的 时间流逝比fraction,然后调用PropertyValuesHolder.calculateValue(fraction)计算属性的值,并回调AnimatorUpdateListener.onAnimationUpdate()方法。PropertyValuesHolder调用Keyframes.getIntValue(fraction),这中间又使用到估值器TypeEvaluator和Keyframe最终结算处我们需要的属性值。- 然后
ObjectAnimator调用PropertyValuesHolder.setAnimatedValue(target)来更新target的属性值。
附加知识点
文章中提到过Choreographer和Android黄油计划。其实在本文的流程分析中已经简单分析了 Choreographer在动画类型上的执行流程:
- 创建
DisplayEventReceiver的子类FrameDisplayEventReceiver来与JNI 层交互。JNI层的vsync信号量通过callback 这个类的dispatchVsync方法来告诉应用层 可以开始新的一帧的渲染了。 Choreographer接收到vsync信号后执行doFrame(frameTimeNanos,frame)方法,对三个支持的类型CALLBACK_INPUT,CALLBACK_ANIMATION,CALLBACK_TRAVERSAL做相应的回调。
需要说明下: _源码中还有一个类型CALLBACK_COMMIT主要处理注册 commit Runnable的需求,即延迟一个frame 的需求。因为它是一种业务辅助,不像其它三种,有明显的业务支持。所以在本文中倾向说三种支持类型,请知悉_。- 回调进入
Choreographer.FrameCallback.doFrame(timeNanos) - 然后进入到业务层,例如 :
CALLBACK_ANIMATION类型进入到AnimationHandler.mFrameCallback;CALLBACK_TRAVERSAL类型进入到ViewRootImpl执行scheduleTraversals()进而执行了View.layout,View.measure,View.draw方法开启View的渲染操作;CALLBACK_INPUT这个类型相比前两个特殊一些,因为输入事件由另一个引擎负责。让输入引擎产生输入事件后不是立刻在 视图层产生响应。而是要等待下一个vsync垂直同步信号,跟着统一的时钟脉冲来响应。所以 在ViewRootImpl中会使用到mChoreographer.postCallback(Choreographer.CALLBACK_INPUT,mConsumedBatchedInputRunnable, null)。具体这里不再展开描述。
简言之,Choreographer就是一个消息处理器,根据 vsync垂直同步信号 来处理三种支持类型的回调。
至于 Android黄油计划(Project Butter) Choreographer只是其中一个重要的特性。还有其他方便的优化。例如 引入了三重缓冲和 JNI层的vsync。至于 vsync的好处以及和 该计划之前 的Android的渲染相比 请参考Android 4.4 Graphic系统详解(2) VSYNC的生成 这篇优质文章。在这里也对该篇文章的愿作者致敬。