我来用通俗易懂的语言,结合生活中的例子,给你讲讲Android动画框架的实现原理。
想象一下,我们想让一个方块在屏幕上从左边滑到右边。
1. 动画的本质:快速翻页的连环画
动画的核心原理,其实就像我们小时候玩的"连环画"或者"翻书动画"。你快速翻动书页,每一页上的图画都只比前一页稍微变动一点点,最终就看到了一个会动的画面。 在Android屏幕上,这个"翻书"的速度非常快,通常每秒翻60次(也就是60帧),这样人眼就感觉不到一帧一帧的跳跃,而是看到了连续流畅的运动。
2. 早期的"假"动画:视图动画 (View Animation)
想象你有一个方块,你想让它动起来。早期的Android动画(叫"视图动画"或"补间动画")是怎么做的呢?
它就像在方块的玻璃投影上做手脚。
- 你有一个方块的"真身"在屏幕左边,它是静止不动的。
- 动画系统生成一个投影。
- 然后,它以极快的速度(每秒60次)不断地改变这个投影的位置:第一帧投影在左边,第二帧稍微往右一点,第三帧再往右一点......
- 屏幕上你看到的是这个会动的投影,感觉方块在动。
问题来了: 如果你点击这个"动起来"的方块,你会发现它没反应!因为你点的是投影,而方块的"真身"还在屏幕的左边老地方待着呢。这就是"幽灵点击"问题,因为它只改了绘制效果,没改真实属性。
3. 现代的"真"动画:属性动画 (Property Animation)
后来的Android系统(3.0版本之后),觉得这种"假"动画不行,它引入了"属性动画"。这次,我们不再改变投影,而是直接改变方块的"真实属性"。 这就像我们请了两个"动画师"来帮忙:
a. 动画值计算师 (ValueAnimator):
这个动画师非常聪明,它只做一件事:在规定时间内,根据你设定的规则,帮你计算出一个个"中间值"。

- 你告诉它:我要从"位置0"动到"位置100",用1秒钟。
- 它会说:好的,0.01秒的时候我在"位置10",0.02秒的时候我在"位置20",以此类推......
- 它就像一个专业的"数学家",只负责计算这些中间值,它不关心这些值是用来移动方块的,还是用来改变颜色的,它只管算。
- 动画速度控制器 (Interpolator / 插值器): 这个计算师还有一个助手,叫"动画速度控制器"。你告诉它,你是想让动画匀速(像跑步机一样),还是先慢后快(像汽车启动),还是先快后慢(像电梯停下)。这个控制器会根据你的要求,调整计算师算值的"节奏"。
- 匀速(线性插值器):1秒跑100米,0.5秒就跑50米。
- 加速(加速插值器):0.5秒可能只跑了20米,但后0.5秒跑了80米。
b. 对象属性操纵师 (ObjectAnimator):
这个动画师是动画值计算师
的进阶版,它更厉害,它知道怎么把"动画值计算师"算出来的值,直接应用到对象的"真实属性"上。

- 你告诉它:我要让"对象"的"X轴位置"动起来。
- 它会监听"动画值计算师"算出来的每一个中间值。
- 每当"动画值计算师"算出一个新值,比如"位置90",
对象属性操纵师
就会立刻找到该对象,并真正地把它在X轴上移动到"位置90"。 - 这次,该对象的"真身"是真的动了!所以你点击它,它就在新位置响应了。
4. 动画的"节拍器":Choreographer (节奏大师)
所有这些动画师都需要一个统一的"节拍器",确保大家步调一致,而且动画在最恰当的时机显示在屏幕上。这个"节拍器"就是Choreographer
,我们可以叫它"节奏大师"。

- 你的手机屏幕有刷新频率,通常每秒60次。可以想象成每隔1/60秒,屏幕就会问:"有没有新的画面要画?"
Choreographer
就像一个经验丰富的舞台总监,它会监听屏幕的"刷新信号"(这个信号非常精确,叫Vsync,垂直同步信号)。- 每当屏幕发出"我要刷新了"的信号时,
Choreographer
就会立刻通知所有正在运行的动画:"快!现在是刷新时间,赶紧计算你们的下一帧!" - 这样,所有的动画计算和UI更新都会和屏幕的刷新频率完美同步,避免了画面撕裂(画面上下半部分不同步的丑陋现象),让动画看起来极其流畅。
5. 动画的"画图工人":GPU (图形处理器) 和 硬件加速
最后,计算出来的动画效果,最终要画到屏幕上。这个"画图工人"就是你手机里的GPU(图形处理器)。
- 在早期,画图都是CPU在干,又慢又累。
- 现在Android默认开启了硬件加速。这意味着,当你的方块改变位置、大小、透明度时,Android会把这些"画图指令"打包,直接发给GPU去处理。
- GPU专门干这个,速度飞快。而且,它还会把方块的初始样子先"刻"下来(叫"显示列表"),下次只是移动方块,它就不需要重新画一遍,直接把已经"刻"好的方块移动到新位置就行了,效率极高。
总结:
- 动画本质:快速播放一系列微小变化的画面。
- 视图动画:只改"投影",不改"真身",有"幽灵点击"的问题。
- 属性动画 :
- ValueAnimator(动画值计算师) :负责计算动画的中间值,不关心具体是啥,只管算。有Interpolator(动画速度控制器)调节速度。
- ObjectAnimator(对象属性操纵师) :把
ValueAnimator
算出的值,真正应用到对象的实际属性上(比如方块的X轴位置)。
- Choreographer(节奏大师):统一的"节拍器",监听屏幕刷新信号,确保动画与屏幕刷新同步,流畅不撕裂。
- 硬件加速 + GPU(画图工人):将画图任务交给专业的GPU,并利用显示列表等技术,大大提高动画渲染效率,让动画丝滑流畅。
属性动画的启动过程
1. ValueAnimator.start()
或 ObjectAnimator.start()
:启动动画。
2. 注册 Choreographer.FrameCallback
:动画内部(ValueAnimator
)将一个FrameCallback
注册到Choreographer
。
Choreographer
是动画和UI渲染的心跳(节奏大师),它将所有UI更新与Vsync信号同步。
模拟源码分析:
当你调用ValueAnimator.start()
时,它最终会注册一个FrameCallback
到Choreographer
。
java
// 模拟 ValueAnimator 内部注册 FrameCallback 的逻辑
public void start() {
// ...
// 获取 Choreographer 实例
Choreographer choreographer = Choreographer.getInstance();
// 注册一个动画帧回调
choreographer.postFrameCallback(mAnimationCallback); // mAnimationCallback 是 ValueAnimator 内部的 FrameCallback 实例
// ...
}
// 模拟 ValueAnimator 内部 mAnimationCallback 的实现
private final FrameCallback mAnimationCallback = new FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
// 在这里进行动画的计算和更新
doAnimationFrame(frameTimeNanos);
// 如果动画还在进行,继续注册下一帧回调
if (mRunning) {
Choreographer.getInstance().postFrameCallback(this);
}
}
};
Choreographer
内部机制简述:
Choreographer
内部有一个Handler
(mHandler
) 和一个Looper
。 当Vsync信号到来时(由DisplayManagerService或其他底层服务通知),它会向mHandler
发送一个消息。当这个消息被处理时,mHandler
会遍历所有已注册的FrameCallback
并调用它们的doFrame()
方法。
3. Vsync 信号到来:显示器发出Vsync信号,Choreographer
收到通知。
4. Choreographer
回调 doFrame()
:Choreographer
执行所有已注册的FrameCallback
的doFrame()
方法。
5. ValueAnimator.doAnimationFrame()
执行:
- 计算动画播放时间。
- 通过
mInterpolator.getInterpolation()
计算插值进度。 - 通过
mEvaluator.evaluate()
计算当前动画值。
ValueAnimator
是属性动画的基石(动画值计算师),它负责在给定的时间和范围内计算出动画的当前值。
模拟源码分析 (doAnimationFrame
核心逻辑):
java
// ValueAnimator.java (简化版)
void doAnimationFrame(long frameTimeNanos) {
if (mStartTime < 0) { // 第一次回调
mStartTime = frameTimeNanos;
}
// 计算动画已经播放的时间
mCurrentPlayTime = (frameTimeNanos - mStartTime) / 1_000_000L; // 转换为毫秒
// 计算动画的进度 (0.0 to 1.0)
float fraction = 0f;
if (mDuration > 0) {
fraction = (float)mCurrentPlayTime / mDuration;
fraction = Math.min(fraction, 1.0f); // 确保不超过1.0
}
// 应用时间插值器 (Interpolator)
fraction = mInterpolator.getInterpolation(fraction);
// 应用类型估值器 (Evaluator) 计算最终动画值
mAnimatedValue = mEvaluator.evaluate(fraction, mStartValue, mEndValue);
// 通知所有注册的更新监听器
notifyUpdateListeners();
// 检查动画是否结束
if (mCurrentPlayTime >= mDuration) {
endAnimation(); // 结束动画,停止注册下一帧回调
}
}
private void notifyUpdateListeners() {
for (AnimatorUpdateListener listener : mUpdateListeners) {
listener.onAnimationUpdate(this); // 回调监听器,ValueAnimator 自身作为参数
}
}
6. ObjectAnimator
应用属性:
如果使用ObjectAnimator
,它会使用反射(或更高效的Property
机制)调用目标对象的setter方法,将计算出的动画值应用到指定属性上。 例如,view.setAlpha(newAlphaValue);
ObjectAnimator
- 将动画值应用到属性(对象属性操纵师); ObjectAnimator
继承自 ValueAnimator
,它在 ValueAnimator
计算出值的基础上,通过反射(或直接调用setter)将值应用到目标对象的特定属性上。
模拟源码分析 (ObjectAnimator
如何利用 ValueAnimator
):
ObjectAnimator
重写了 ValueAnimator
的 animateValue()
方法(或者更准确地说,ValueAnimator
在调用notifyUpdateListeners()
后,ObjectAnimator
会在内部处理属性设置)。
java
// ObjectAnimator.java (简化版)
// ObjectAnimator 内部通常会有一个内部的 AnimatorUpdateListener
// 这个监听器会在 ValueAnimator 计算出新值后被回调
// 构造函数或 setupProperty 期间
public ObjectAnimator(Object target, String propertyName) {
mTarget = target;
mPropertyName = propertyName;
// ... 内部会尝试通过反射找到 setter 方法或 Property 对象
}
// 假设 ObjectAnimator 内部有一个类似这样的监听器
// 在 ValueAnimator 的 notifyUpdateListeners() 之后被调用
private void applyAnimatedValue() {
// 这里的 getAnimatedValue() 是 ValueAnimator 的方法,获取当前计算出的动画值
Object value = getAnimatedValue();
// 使用反射或其他方式设置属性值
// 以下是伪代码,实际实现会更复杂,会缓存 Method 对象等
try {
if (mSetterMethod != null) { // 如果找到了 setter 方法
mSetterMethod.invoke(mTarget, value);
} else if (mProperty != null) { // 如果是 Property 对象
mProperty.set(mTarget, value);
} else {
// Log.e("ObjectAnimator", "No setter or Property found for " + mPropertyName);
}
} catch (Exception e) {
// 处理反射异常
}
}
7. View.invalidate()
:被改变属性的View(例如setAlpha
内部)会调用invalidate()
方法,标记自身为"脏"。
当 ObjectAnimator
调用了View.setX()
、View.setAlpha()
等方法更新了View的属性后,这些setter方法内部通常会调用 invalidate()
或 requestLayout()
。
invalidate()
方法并不会立即重绘View。它会标记View为"脏",并发送一个请求到UI线程的ViewRootImpl
。
8. ViewRootImpl
的渲染循环:在下一个Vsync周期,ViewRootImpl
的FrameCallback
被调用,它检测到"脏"View。
9. View 重新绘制:ViewRootImpl
执行绘制操作,调用受影响View的draw()
方法。
ViewRootImpl
的渲染循环:
ViewRootImpl
是连接View层级和窗口管理器(Window Manager)的关键类。- 它内部也会注册一个
Choreographer.FrameCallback
。 - 当Vsync信号到来时,
ViewRootImpl
的FrameCallback
会被调用。 - 在这个回调中,
ViewRootImpl
会检查是否有"脏"的View需要重绘。 - 如果有,它会执行
draw()
方法,遍历View树,调用需要重绘的View的draw()
方法。 - 最终,绘制指令会被发送到图形系统(Skia/OpenGL ES),并由GPU进行渲染。
10. 硬件加速渲染:绘制指令(可能通过更新DisplayList)被发送到GPU进行渲染,最终显示在屏幕上。
在硬件加速开启的情况下,View的draw()
方法会将绘制指令记录到DisplayList
中。后续的动画更新如果只是改变了View的变换(如位置、透明度),而不需要重新执行所有复杂的绘制指令,GPU可以直接根据更新后的变换矩阵重放DisplayList
,大大提高了渲染效率。
11. 循环:如果动画未结束,ValueAnimator
会再次注册FrameCallback
到Choreographer
,等待下一个Vsync,重复以上步骤。
