【阅读源码--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_ANIMATION vsync 后,遍历之前 计算动画值,触发 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) : 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 表示使用全局值)
相关推荐
知行合一。。。3 小时前
DeepAgents--01--Agent和OpenClaw的相关概念
android·数据库
汪海游龙4 小时前
告别 Play Console 手动上传:从模拟器截图到自动发版的完整流水线
android·ci/cd·github
weixin_440784114 小时前
【IntentSeivice实现原理】
android·java·开发语言·intentservice
delta_hell4 小时前
【阅读源码--Android】动画之ValueAnimator--2
android·源码·valueanimator
delta_hell4 小时前
【阅读源码--Android】动画之辅助类
android·源码·animator
消失的旧时光-19438 小时前
第 2 篇:Android 控制屏为什么与机器人主控使用 TCP 长连接?
android·tcp/ip·机器人
小Ti客栈9 小时前
MySQL查询原理:从Server到InnoDB
android·mysql·adb
纪念 22910 小时前
MySQL(题目讲解二)
android·数据库·mysql
你听得到1110 小时前
排查 App 问题:别只盯着报错,把前后发生的事情串起来
android·前端·flutter