引言
文档是指挥AI写的,我觉得比较重要的地方都补充了些内容,读源码系列目的就是学习和总结,所以内容比较偏粗线条概括性的,除非是特别细的需要注意的点,大部分都是提纲性的。AI分析总结并写出文档并不能代替读代码,因为不读代码并不会发现有意思的地方,但是AI写文档和画图确实比人快,所以后续文章都会让AI来写。
1. 概述
ValueAnimator 是 Android 属性动画系统的核心时序引擎。它本身不操作任何对象属性,仅负责在给定时长内计算动画值,并通过回调通知调用者。我觉得time->fraction->value的解耦抽象是个很好的点,在这个基础上可以做到很好的通用性。
2. 核心架构
2.1 Animator 包整体架构
android.animation 包
│
├── Animator (abstract, implements Cloneable)
│ │ 动画基类,定义生命周期接口(start/cancel/end/pause/resume)
│ │ 管理 AnimatorListener / AnimatorPauseListener
│ │
│ ├── ValueAnimator
│ │ │ 核心时序引擎,计算动画值但不操作属性
│ │ │ 持有 PropertyValuesHolder[]、TimeInterpolator
│ │ │ 实现 AnimationHandler.AnimationFrameCallback
│ │ │
│ │ └── ObjectAnimator
│ │ 在 ValueAnimator 基础上自动将动画值设置到目标对象的属性
│ │ 通过 PropertyValuesHolder 的 setter/getter 反射或 Property 接口赋值
│ │ 支持自动取消同目标同属性的动画
│ │
│ ├── AnimatorSet
│ │ 组合多个 Animator,支持 together(并行)和 sequentially(顺序)两种编排
│ │ 通过 pulseAnimationFrame() 驱动子 Animator,不依赖 Choreographer 直接调度
│ │
│ ├── TimeAnimator
│ │ 简单封装,每帧通过 TimeListener 回调当前时间,不计算值
│ │
│ ├── StateListAnimator
│ │ 根据 View 状态切换不同的 Animator
│ │
│ └── RevealAnimator
│ 圆形揭示动画(ViewAnimationUtils.createCircularReveal)
│
├── AnimationHandler
│ │ 帧调度中枢(ThreadLocal 单例)
│ │ 管理 mAnimationCallbacks 列表(所有活跃 Animator)
│ │ 管理 mCommitCallbacks 列表(需要 jank 补偿的 Animator)
│ │ 通过 AnimationFrameCallbackProvider 驱动(默认使用 Choreographer)
│ │ 支持后台暂停无限动画(省电)
│ │
│ ├── AnimationFrameCallback (interface)
│ │ doAnimationFrame(frameTime) ← 帧计算入口
│ │ commitAnimationFrame(frameTime) ← 帧提交入口(jank 补偿)
│ │
│ └── AnimationFrameCallbackProvider (interface)
│ postFrameCallback() / postCommitCallback()
│ getFrameTime() / getFrameDelay() / setFrameDelay()
│ 默认实现 MyFrameCallbackProvider → 封装 Choreographer
│
├── 值计算体系
│ ├── PropertyValuesHolder 持有属性名 + Keyframes + Evaluator + Converter
│ │ ├── IntPropertyValuesHolder int 特化(JNI setter 加速)
│ │ ├── FloatPropertyValuesHolder float 特化(JNI setter 加速)
│ │ ├── MultiFloatValuesHolder 多参数 float setter
│ │ └── MultiIntValuesHolder 多参数 int setter
│ │
│ ├── Keyframes (interface)
│ │ └── KeyframeSet 默认实现
│ │ ├── IntKeyframeSet int 特化(避免装箱)
│ │ ├── FloatKeyframeSet float 特化(避免装箱)
│ │ └── PathKeyframes Path 特化
│ │
│ ├── Keyframe (abstract)
│ │ ├── IntKeyframe
│ │ ├── FloatKeyframe
│ │ └── ObjectKeyframe
│ │
│ ├── TypeEvaluator<T> (interface) fraction → 动画值
│ │ ├── IntEvaluator / FloatEvaluator
│ │ ├── ArgbEvaluator 颜色(sRGB 线性空间插值)
│ │ ├── PointFEvaluator / RectEvaluator
│ │ └── FloatArrayEvaluator / IntArrayEvaluator
│ │
│ └── TypeConverter<T,V> (interface) 动画值 → setter 参数类型转换
│ └── BidirectionalTypeConverter 双向转换(支持从目标对象反向获取值)
│
├── TimeInterpolator (interface) 原始 fraction → 插值 fraction
│ (实现在 android.view.animation 包)
│ LinearInterpolator / AccelerateInterpolator / DecelerateInterpolator
│ AccelerateDecelerateInterpolator / BounceInterpolator / OvershootInterpolator
│ AnticipateInterpolator / AnticipateOvershootInterpolator / CycleInterpolator
│ PathInterpolator
│
└── 辅助类
├── AnimatorListenerAdapter 空实现适配器
├── AnimatorInflater 从 XML 加载 Animator
└── AnimatorConstantState 缓存 ConstantState,避免重复加载
2.2 帧驱动机制(Choreographer → Animator)
┌─────────────────────────────────────────────────────────────────────┐
│ Vsync 信号 (硬件) │
└──────────────────────────────┬──────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────────┐
│ Choreographer │
│ │
│ 收到 vsync 后,按回调类型顺序执行: │
│ │
│ CALLBACK_ANIMATION ──→ AnimationHandler.mFrameCallback.doFrame() │
│ CALLBACK_INSETS_ANIMATION │
│ CALLBACK_TRAVERSAL ──→ View 绘制流程(measure/layout/draw) │
│ CALLBACK_COMMIT ─────→ AnimationHandler 的 commit 回调 │
└──────────────────────────────┬──────────────────────────────────────┘
│
┌──────────────────────┴──────────────────────┐
│ │
▼ CALLBACK_ANIMATION ▼ CALLBACK_COMMIT
│ (帧开始,遍历之前) │ (遍历之后,帧提交)
│ │
┌───────┴─────────────────────────────────┐ ┌────────┴────────────────────────────┐
│ AnimationHandler.doAnimationFrame() │ │ AnimationHandler.commitAnimationFrame()│
└─────────────────────────────────────────┘
关键时序说明:
| 阶段 | Choreographer 回调类型 | 时机 | 作用 |
|---|---|---|---|
| 动画帧计算 | CALLBACK_ANIMATION |
vsync 后,遍历之前 | 计算动画值,触发 View 重绘 |
| View 遍历 | CALLBACK_TRAVERSAL |
动画帧之后 | measure / layout / draw |
| 帧提交 | CALLBACK_COMMIT |
遍历之后 | jank 补偿,修正 startTime |
commitAnimationFrame 的意义:
首帧从 doAnimationFrame(CALLBACK_ANIMATION)到实际绘制(CALLBACK_COMMIT)之间可能有显著延迟(因 measure/layout/draw 耗时)。如果不补偿,下一帧的 animateBasedOnTime 会认为已经过了很长时间,导致跳帧。commitAnimationFrame 将 mStartTime 对齐到实际绘制时间,确保动画从正确的起点开始。
2.3 值计算管线
设计思想
ValueAnimator 的核心设计思想是将"时间"解耦为三层抽象,每一层职责单一、可独立替换:
- Time → Fraction :将真实流逝的时间(毫秒)归一化为
[0, 1]的进度比例。这一层处理动画的时长、延迟、重复和反向播放,但不关心值的具体类型。 - Fraction → Interpolated Fraction :通过
TimeInterpolator对原始 fraction 进行非线性映射,控制动画的"节奏"(加速、减速、弹跳等),但不关心最终值是什么。 - Interpolated Fraction → Value :通过
TypeEvaluator将插值后的 fraction 转换为实际的动画值(float、int、color、自定义对象等),完成从抽象进度到具体值的映射。
这种分层设计使得三者可以正交组合:同一个 TimeInterpolator 可用于任何类型的动画,同一个 TypeEvaluator 也可搭配不同的 TimeInterpolator。
Time、Fraction 与 Value 的关系
真实时间 (ms)
│
│ elapsed = currentTime - mStartTime
│ fraction = elapsed / scaledDuration
│
▼
原始 fraction ∈ [0, mRepeatCount + 1] ← 时间→进度:归一化
│ (>1 表示进入重复迭代)
│
│ getCurrentIteration() → 拆分为 iteration + iterationFraction
│ shouldPlayBackward() → 可能取 1 - iterationFraction
│
▼
当前迭代 fraction ∈ [0, 1] ← 处理重复与反向
│
│ mInterpolator.getInterpolation(fraction)
│
▼
插值 fraction ∈ ℝ(可超出 [0,1]) ← 节奏变换
│ (如过冲、弹跳会超出范围)
│
│ mEvaluator.evaluate(fraction, startValue, endValue)
│ 或 KeyframeSet.getValue(fraction)
│
▼
动画值 (float / int / color / Object) ← 进度→值:类型求值
关键公式:
| 阶段 | 公式 | 说明 |
|---|---|---|
| 时间→进度 | fraction = (currentTime - mStartTime) / scaledDuration |
归一化为进度,>1 表示重复 |
| 进度→迭代 | iteration = floor(fraction),iterFraction = fraction - iteration |
拆分出当前迭代及迭代内进度 |
| 迭代方向 | shouldPlayBackward ? (1 - iterFraction) : iterFraction |
REVERSE 模式下奇偶迭代方向交替 |
| 节奏变换 | interpolated = interpolator.getInterpolation(fraction) |
非线性映射,输出可超出 [0,1] |
| 进度→值 | value = evaluator.evaluate(interpolated, start, end) |
线性插值:start + t * (end - start) |
多关键帧时的值计算 :当使用多个 Keyframe 时,KeyframeSet.getValue() 先定位 fraction 落入的区间 [prevKf, nextKf],计算区间内比例 intervalFraction,再应用该区间的局部 Interpolator,最后由 Evaluator 在两个 Keyframe 值之间插值。这相当于将整个动画分段,每段独立插值。
管线流程
时间脉冲 (frameTime)
│
▼
doAnimationFrame(frameTime) ← 帧调度入口
│
▼
animateBasedOnTime(currentTime) ← 计算总 fraction
│
▼
getCurrentIterationFraction() ← 计算当前迭代 fraction(处理 repeat/reverse)
│
▼
animateValue(fraction) ← 核心计算
│
├─ mInterpolator.getInterpolation(fraction) ← 时间插值(节奏控制)
│
├─ mValues[i].calculateValue(fraction) ← 类型求值(值计算)
│
└─ 通知 mUpdateListeners ← 回调调用者
2.4 关键组件职责
| 组件 | 职责 |
|---|---|
AnimationHandler |
帧调度,通过 vsync/Handler 驱动 doAnimationFrame() |
TimeInterpolator |
时间映射:原始 fraction → 插值 fraction(控制快慢节奏) |
TypeEvaluator |
值映射:插值 fraction → 实际动画值(float/int/color/object) |
PropertyValuesHolder |
持有一组动画值,内部封装 evaluator,支持多属性并行动画 |
3. 状态机
start()
│
▼
┌──── STARTED (mStarted=true, mRunning=false)
│ │
│ (startDelay > 0 时等待)
│ │
│ ▼ 首帧到达
│ RUNNING (mRunning=true)
│ │
│ ┌─────┼─────┐
│ │ │ │
│ pause() │ cancel()/end()
│ │ │ │
│ ▼ │ ▼
│ PAUSED │ END
│ │ │
│ resume() │
│ │ │
└─────┘ │
│
▼
IDLE (所有状态重置)
状态字段
| 字段 | 含义 |
|---|---|
mStarted |
start() 已调用,可能还在 startDelay 中 |
mRunning |
首帧已到达,正在实际计算动画值 |
mPaused |
暂停状态 |
mInitialized |
PropertyValuesHolder 已初始化 |
mAnimationEndRequested |
已请求结束(防止重复结束) |
4. 时间管理
4.1 关键时间字段
| 字段 | 含义 |
|---|---|
mStartTime |
动画基准起始时间(含 startDelay),用于计算 elapsed time |
mLastFrameTime |
上一帧时间,< 0 表示尚未进入帧循环 |
mFirstFrameTime |
首帧时间 |
mPauseTime |
暂停时刻,用于 resume 时偏移 mStartTime |
mStartTimeCommitted |
起始时间是否已固定(首帧后通过 commitAnimationFrame 修正 jank) |
4.2 Duration Scale
两级缩放机制,影响 duration 和 startDelay:
resolveDurationScale() = mDurationScale >= 0 ? mDurationScale : sDurationScale
getScaledDuration() = mDuration * resolveDurationScale()
sDurationScale:系统全局缩放(开发者选项/省电模式)mDurationScale:单实例覆盖(overrideDurationScale,-1f表示使用全局值)