三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

【阅读源码--Android】动画之ValueAnimator--1

【阅读源码--Android】动画之ValueAnimator--1

引言

文档是指挥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_ANIMATIONvsync 后,遍历之前计算动画值,触发 View 重绘
View 遍历CALLBACK_TRAVERSAL动画帧之后measure / layout / draw
帧提交CALLBACK_COMMIT遍历之后jank 补偿,修正 startTime

commitAnimationFrame 的意义

首帧从doAnimationFrame(CALLBACK_ANIMATION)到实际绘制(CALLBACK_COMMIT)之间可能有显著延迟(因 measure/layout/draw 耗时)。如果不补偿,下一帧的animateBasedOnTime会认为已经过了很长时间,导致跳帧。commitAnimationFramemStartTime对齐到实际绘制时间,确保动画从正确的起点开始。

2.3 值计算管线

设计思想

ValueAnimator 的核心设计思想是将"时间"解耦为三层抽象,每一层职责单一、可独立替换:

  1. Time → Fraction:将真实流逝的时间(毫秒)归一化为[0, 1]的进度比例。这一层处理动画的时长、延迟、重复和反向播放,但不关心值的具体类型。
  2. Fraction → Interpolated Fraction:通过TimeInterpolator对原始 fraction 进行非线性映射,控制动画的"节奏"(加速、减速、弹跳等),但不关心最终值是什么。
  3. 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) : iterFractionREVERSE 模式下奇偶迭代方向交替
节奏变换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 (所有状态重置)

状态字段

字段含义
mStartedstart()已调用,可能还在 startDelay 中
mRunning首帧已到达,正在实际计算动画值
mPaused暂停状态
mInitializedPropertyValuesHolder已初始化
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表示使用全局值)
← 返回列表