Android动画实现原理浅析

Android中的动画使用注意事项

基本上现如今市面上所有的app,都会使用到动画效果,动画可以很好的提升用户的交互体验。

Android中的动画大概可以分成两个发展阶段:在Android3.0之前,Framework层提供了android.view.animation,可以实现View,ViewGroup的各种动画效果,比如AlphaAnimation,RotateAnimation,ScaleAnimation,TranslateAnimation,就可以分别实现透明度,旋转,缩放,位移等动画效果。当然,也可以通过AnimationSet将多个动画联合使用,以达到更复杂的效果。

为什么在Android3.0之后,就不推荐使用animation的动画了呢,是因为它大概有以下几点缺点:

  1. animation的动画,只能实现View的简单的动画效果,复杂的,比如颜色过渡无法实现;
  2. animation只能操作View及其子类的可视化动画效果,但是,有可能一些非view类型的随时间轴变化的效果无法实现(layout,position的变化);
  3. animation只是简单的将view的视图信息发生了改变以达到视觉上的动画效果,但是view自身的属性没有发生根本的变化。

为了弥补以上的缺点和不足,在Android3.0以上,Framework又提供了另一套动画框架:android.animation.animator。

  1. Animation在动画实现的机制上与Animator是完全不同的。一句话概括就是:Animation实在每次绘制之前,通过将整块画布矩阵变换,从而实现视觉坐标系的移动、变换,但实际上其view内部记录的坐标信息以及其他属性始终没有发生变化。
  2. Animator其实在动画实现上更加直接,简单一些。他是计算动画节点上对应的value,然后将此value通过不同的方式作用到view的属性参数上。(以下会有更详细的Animator机制的介绍)

两者的优劣对比:

  1. 版本兼容性:Animation全版本支持。Animator只能支持Android3.0以上。
  2. 使用范围:Animation只能操作view。Animator不仅支持view动画,还可以操作多种属性的变化,任何实现相关方法的对象,都可以使用Animator来达到属性变化的过程。
  3. 实现效率:Animation通过画布矩阵变换处理view的动画效果。Animator则是通过对象属性set,get方法的反射调用,来达到所操作对象相关属性的变化的。因此,从效率上来说,Animation的效率可能稍高一些,而Animator由于在动画过程中要使用反射,效率稍差一些。
  4. 使用效果:Animation因为只是画布的矩阵变化,view的属性,位置信息并没有发生变化,所以,动画之后view的点击事件等处理都会非常麻烦。Animator因为是直接操作view的属性,在做动画的同时,view的属性也相应的发生了变化,所以不用担心view的属性与视觉上不一致的问题。

随着Android系统版本不断的提升,使用3.0以下系统的手机将会越来越少,时间效率。所以也就没有必要单独去深入源码分析Animation的实现原理了。而Animator是高版本一直支持的框架,因此,着重分析一下在可预见的很长一段时间内Android一直会使用的Animator的实现原理吧。

1,继承关系

Animator类层级

2, Animator的工作流程

Animator类层级

1,ObjectAnimator.ofXXX(Object target, String propertyName, TYPE… values)

1
2
3
4
5
6
public static ObjectAnimator ofXXX(Object target, String propertyName, XXX... values) {
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
anim.setFloatValues(values);
return anim;
}
使用target创建ObjectAnimator,并使用propertyName创建PropertyValueHolder,将values值保存在PropertyValuesHolder中。

setDuration等省略

2,ObjectAnimator.start()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ValueAnimator
private void start(boolean playBackwards) {
if (Looper.myLooper() == null) {
throw new AndroidRuntimeException("Animators may only be run on Looper threads");
}
AnimationHandler animationHandler = AnimationHandler.getInstance();
animationHandler.addAnimationFrameCallback(this, (long) (mStartDelay * sDurationScale);
if (mStartDelay == 0 || mSeekFraction >= 0) {
startAnimation();
}
}
1. 首先判断当前运行的动画的线程是否有looper,因为Animator的运行依赖looper(稍后介绍)。
2. 将当前Animator(ObjectAnimator)本身注册给AnimationHandler
3. 最后,设置一些动画标志位,完成PropertyValueHolder的一些初始化动作。

3,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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
/**
* Register to get a callback on the next frame after the delay.
*/
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));
}
}
AnimationHandler将创建的ObjectAnimator回调放入到一个ArrayList中mAnimationCallbacks,稍后在通知动画更新时,就是用此ArrayList,迭代遍历所有注册的ObjectAnimator,通知他们去更新自己的属性值。
此处的Provider为MyFrameCallbackProvider
/**
* Default provider of timing pulse that uses Choreographer for frame callbacks.
*/
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);
}
}
Choreographer会在每次系统渲染Frame的时候,接收到一个脉冲信号,用以准备下一帧将要绘制内容的预备工作,可以在在Choreography注册监听,Choreographer.FrameCallback,在接到时间脉冲信号用于通知对此感兴趣的监听者。但是这个FrameCallback有个特别:在一个脉冲信号达到之后,这个callback就会失效,因此,如果你需要持续的监听,则需要在每次脉冲信号到达,完成自定义动作之后,再次将Callback注册一下,以便下次顺利接受回调。
AnimationHanlder中定义了一个Choreographer.FrameCallback:
private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
//doAnimationFrame(getProvider().getFrameTime());//稍后展开
if (mAnimationCallbacks.size() > 0) {
getProvider().postFrameCallback(this);
}
}
};

因此AnimationHandler.addAnimationFrameCallback中的写法:
if (mAnimationCallbacks.size() == 0) {
getProvider().postFrameCallback(mFrameCallback);
}
if (!mAnimationCallbacks.contains(callback)) {
mAnimationCallbacks.add(callback);
}
意思就是统一使用一个Choreography.FrameCallback回调,如果mAnimationCallbacks不为空,表明已经有动画正在发生或者即将发生(延迟了),也就表明已经向Choreography注册了监听,就不需要再次注册,只需要将自己加入到FrameCallback的监听回调行列中就OK了。

4.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
postFrameCallback-->postCallbackDelayedInternal-->scheduleFrameLocked-->scheduleVsyncLocked-->mDisplayEventReceiver.scheduleVsync-->nativeScheduleVsync
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);
}
}
void doFrame(long frameTimeNanos, int frame) {
...
mFrameInfo.markAnimationsStart();
doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
...
}
void doCallbacks(int callbackType, long frameTimeNanos) {
for (CallbackRecord c = callbacks; c != null; c = c.next) {
...
c.run(frameTimeNanos);
}
}
在AnimationHanlder调用Choreography.postFrameCallback的时候,FrameCallback封装成CallbackRecord,
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();
}
}
}

5. AnimationHandler.mFrameCallback

4步骤中,此刻token == FRAME_CALLBACK_TOKEN,执行if中的动作,调用的就是AnimationHandler中定义的Choreographer.FrameCallback:上面已经说过,但是上述中省略了最重要的一步:

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
private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
doAnimationFrame(getProvider().getFrameTime());
if (mAnimationCallbacks.size() > 0) {
getProvider().postFrameCallback(this);
}
}
};
private void doAnimationFrame(long frameTime) {
int size = mAnimationCallbacks.size();
long currentTime = SystemClock.uptimeMillis();
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();
}

6. ValueAnimator.doAnimationFrame

5步骤中callback为AnimationFrameCallback,ValueAnimator实现此接口,因此是调用的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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
public final void doAnimationFrame(long frameTime) {
...
final long currentTime = Math.max(frameTime, mStartTime);
boolean finished = animateBasedOnTime(currentTime);
if (finished) {
endAnimation();
}
}
boolean animateBasedOnTime(long currentTime) {
boolean done = false;
if (mRunning) {
...
mOverallFraction = clampFraction(fraction);
float currentIterationFraction = getCurrentIterationFraction(mOverallFraction);
animateValue(currentIterationFraction);
}
return done;
}
void animateValue(float fraction) {
final Object target = getTarget();
if (mTarget != null && target == null) {
cancel();
return;
}
super.animateValue(fraction);
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].setAnimatedValue(target);
}
}
super:ValueAnimator.animateValue:
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);//Evalutor就是在这一步完成数值目标转换的
}
if (mUpdateListeners != null) {
int numListeners = mUpdateListeners.size();
for (int i = 0; i < numListeners; ++i) {
mUpdateListeners.get(i).onAnimationUpdate(this);
}
}
}
mValues为之前存放value值的PropertyValueHolder,由value的类型,又划分出FloatValuesHolder,IntValuesHolder,MultiFloatValuesHolder等等,在holder中计算当前的value值,然后调用Object对应的方法,更新属性:
例如FloatValuesHolder:
@Override
void setAnimatedValue(Object target) {
...
if (mJniSetter != 0) {
nCallFloatMethod(target, mJniSetter, mFloatAnimatedValue);
return;
}
if (mSetter != null) {
try {
mTmpValueArray[0] = mFloatAnimatedValue;
mSetter.invoke(target, mTmpValueArray);
} catch (InvocationTargetException e) {
Log.e("PropertyValuesHolder", e.toString());
} catch (IllegalAccessException e) {
Log.e("PropertyValuesHolder", e.toString());
}
}
}

7.循环流程,直到timeout

以上步骤为一个动画在一帧中的完整流程,只要动画尚未完成,Animator就不会将自己从AnimationHandler中的监听List中删除,则Choreography.FrameCallback就会持续的注册,监听回调完成后续帧中动画变化。

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
AnimationHandler:
private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
doAnimationFrame(getProvider().getFrameTime());
if (mAnimationCallbacks.size() > 0) {
getProvider().postFrameCallback(this);
}
}
};
ValueAnimator:
private void endAnimation() {
if (mAnimationEndRequested) {
return;
}
AnimationHandler handler = AnimationHandler.getInstance();
handler.removeCallback(this);
mAnimationEndRequested = true;
mPaused = false;
...
mRunning = false;
mStarted = false;
mStartListenersCalled = false;
mReversing = false;
mLastFrameTime = 0;
...
}

3, 小结:

  1. 使用多个Animator,AnimatorSet,或者PropertyValueHolder在性能上并没有明显的差异,他们之间的关系大体如下所示:
    多个Animator的关系

注意到:总体调用对象的次数没有发生变化。

  1. 如果多个Animator都是操作同一个View的UI属性,则在每一个PropertyValueHolder每次被更新并调用View对应的方法时,就会多次调用view.invalidate和requestlayout方法。这样就会执行很多次非必要的方法,这是就可以考虑使用ViewPropertyAnimator:
    1
    2
    textview.animate().x(500).y(500).setDuration(5000).setInterpolator(new BounceInterpolator());
    ViewPropertyAnimator不能直接创建实例对象,只能通过view.animate()来获得针对此View的实例。

通过查看ViewPropertyAnimator的实现,其实在其内部也是使用的ValueAnimator来实现的view的动画效果,但是呢,ViewPropertyAnimator针对多个Animator调用时的缺点,进行了优化,以提高性能(将需要使用动画的属性,放到一个Map里面,等待下一次的帧更新动作,然后在帧更新时,使用View内部存储的RenderNode节点,将各个属性值设置上去,然后调用View.invalidateViewProperty()(简化版的invalidate操作));

此处可结合在搜狗浏览器V4.5优化网页加载进度条动画效果在天猫等个别重定向甚多的网站时,由于错误的使用ValueAnimator造成invalidate,requestlayout调用次数太多,从而影响了MainHandler其他message的及时响应,造成ANR的问题。。。

但是使用中应该注意,ViewPropertyAnimator不能在外部持有其实例,以便以后反复使用,必须每次通过View.animate来获得实例。

  1. 接下来就要分析一下在支持硬件加速的机器上,针对动画开启硬件加速的一些功能。