【阅读源码--Android】动画之ValueAnimator--2
5. 值计算管线详解(一帧调用栈)
本节围绕一次 vsync 帧到达时,从AnimationHandler到最终计算出动画值的完整调用栈,结合前述状态字段和时间变量,逐函数解析。
5.1 调用栈总览
AnimationHandler.doAnimationFrame(frameTime) ← 遍历所有活跃 Animator │ └─ ValueAnimator.doAnimationFrame(frameTime) ← [1] 帧入口:时间与状态处理 │ ├─ startAnimation() ← [2] 首帧启动(条件触发) │ └─ animateBasedOnTime(currentTime) ← [3] 时间→总 fraction │ ├─ notifyListeners(ON_REPEAT) ← [4] 重复通知(条件触发) │ └─ getCurrentIterationFraction(fraction) ← [5] 总 fraction→迭代 fraction │ ├─ getCurrentIteration(fraction) ← [6] 拆分迭代号 │ └─ shouldPlayBackward(iteration) ← [7] 判断方向 │ └─ animateValue(iterFraction) ← [8] 核心值计算 │ ├─ mInterpolator.getInterpolation()← [9] 节奏变换 │ ├─ mValues[i].calculateValue() ← [10] 类型求值 │ │ │ └─ mKeyframes.getValue() ← [11] 关键帧插值 │ │ │ └─ mEvaluator.evaluate() ← [12] Evaluator 线性插值 │ └─ 通知 mUpdateListeners ← [13] 回调5.2 [1] doAnimationFrame(frameTime) — 帧入口
这是AnimationHandler遍历mAnimationCallbacks时对每个 Animator 调用的入口。负责处理所有与时间、状态相关的前置逻辑。
执行流程:
doAnimationFrame(frameTime):步骤 1:初始化 mStartTime(首帧)
if(mStartTime<0){// 首帧mStartTime=mReversing?frameTime// 反向播放:startDelay 放在末尾:frameTime+(long)(mStartDelay*resolveDurationScale());// 正向:startDelay 加到起始}mStartTime < 0(初始值-1)表示首次收到帧回调- 正向播放时,
mStartTime= 帧时间 + 缩放后的 startDelay,在此时间之前动画不计算值 - 反向播放时,
mStartTime= 帧时间,startDelay 逻辑在动画末尾处理
步骤 2:处理 pause/resume
if(mPaused){mPauseTime=frameTime;// 记录暂停时刻removeAnimationCallback();// 从 AnimationHandler 注销,不再收帧returnfalse;}elseif(mResumed){mResumed=false;if(mPauseTime>0){mStartTime+=(frameTime-mPauseTime);// 偏移 startTime,跳过暂停期间}}- 暂停时记录
mPauseTime并注销回调,动画"冻结" - 恢复时将
mStartTime向后偏移(frameTime - mPauseTime),等效于暂停期间时间不流逝
步骤 3:处理 startDelay 等待期
if(!mRunning){if(mStartTime>frameTime&&mSeekFraction==-1){returnfalse;// 还在 startDelay 中,跳过本帧}else{mRunning=true;startAnimation();// startDelay 结束,正式开始}}mRunning == false且mStartTime > frameTime:仍在延迟等待中,直接返回- 延迟结束后:设置
mRunning = true,调用startAnimation()通知监听器
步骤 4:处理 Seek(首帧)
if(mLastFrameTime<0){// 首帧(与上面的首帧不同,这是进入帧循环的首帧)if(mSeekFraction>=0){longseekTime=(long)(getScaledDuration()*mSeekFraction);mStartTime=frameTime-seekTime;// 将 startTime 前移,使 fraction 对齐到 seek 位置mSeekFraction=-1;}mStartTimeCommitted=false;// 允许后续 jank 补偿}mLastFrameTime=frameTime;步骤 5:计算值并判断结束
finallongcurrentTime=Math.max(frameTime,mStartTime);// 防止负时间(seek 回退时)booleanfinished=animateBasedOnTime(currentTime);if(finished){endAnimation();}returnfinished;5.3 [2] startAnimation() — 首帧启动
仅在 startDelay 结束、动画正式开始时调用一次。
privatevoidstartAnimation(){mAnimationEndRequested=false;initAnimation();// 初始化 PropertyValuesHolder(分配 Evaluator)mRunning=true;mOverallFraction=(mSeekFraction>=0)?mSeekFraction:0f;notifyStartListeners(mReversing);// 通知 onAnimationStart}initAnimation()调用PropertyValuesHolder.init(),为每个mValues[i]分配默认 Evaluator(Int/Float)并传递给 KeyframeSetmOverallFraction初始化为 seek 位置或 0
5.4 [3] animateBasedOnTime(currentTime) — 时间→总 fraction
这是从时间到 fraction 的核心转换,同时处理重复检测和结束判断。
booleananimateBasedOnTime(longcurrentTime){booleandone=false;if(mRunning){finallongscaledDuration=getScaledDuration();// ── 时间 → 总 fraction ──finalfloatfraction=scaledDuration>0?(float)(currentTime-mStartTime)/scaledDuration:1f;// 0 时长直接跳到结束finalfloatlastFraction=mOverallFraction;// ── 重复检测 ──finalbooleannewIteration=(int)fraction>(int)lastFraction;finalbooleanlastIterationFinished=(fraction>=mRepeatCount+1)&&(mRepeatCount!=INFINITE);if(scaledDuration==0){done=true;// 0 时长,直接结束}elseif(newIteration&&!lastIterationFinished){notifyListeners(AnimatorCaller.ON_REPEAT,false);// 进入新迭代}elseif(lastIterationFinished){done=true;// 所有迭代完成}mOverallFraction=clampFraction(fraction);// ── 总 fraction → 迭代 fraction → 值 ──floatcurrentIterationFraction=getCurrentIterationFraction(mOverallFraction,mReversing);animateValue(currentIterationFraction);}returndone;}关键变量说明:
| 变量 | 含义 | 示例 |
|---|---|---|
fraction | 总进度 = 已过时间 / 缩放时长 | 2.3 = 第 2 次重复,进度 30% |
lastFraction | 上一帧的总进度,用于检测迭代切换 | 0.9 → 1.1 说明进入了新迭代 |
mOverallFraction | 当前保存的总进度(clamp 后) | clamp 到[0, mRepeatCount+1] |
newIteration | (int)fraction > (int)lastFraction | 0.9→1.1:0 < 1= true |
lastIterationFinished | fraction >= mRepeatCount + 1 | repeatCount=2,fraction≥3 = 结束 |
5.5 [5][6][7] getCurrentIterationFraction — 总 fraction → 迭代 fraction
这三个函数协作将总 fraction(可能 >1)转换为[0,1]的当前迭代 fraction,同时处理 REVERSE 模式的方向交替。
getCurrentIterationFraction(fraction, inReverse)
privatefloatgetCurrentIterationFraction(floatfraction,booleaninReverse){fraction=clampFraction(fraction);// 限制到 [0, mRepeatCount+1]intiteration=getCurrentIteration(fraction);// 拆分迭代号floatcurrentFraction=fraction-iteration;// 迭代内进度 ∈ [0,1]returnshouldPlayBackward(iteration,inReverse)?1f-currentFraction// 反向:1→0:currentFraction;// 正向:0→1}getCurrentIteration(fraction)
privateintgetCurrentIteration(floatfraction){doubleiteration=Math.floor(fraction);// 特殊处理:fraction 恰好为整数时,视为上一个迭代的结束if(fraction==iteration&&fraction>0){iteration--;}return(int)iteration;}示例:fraction = 2.0→iteration = 1,currentFraction = 1.0(第 1 次迭代的末尾,而非第 2 次的开头)。
shouldPlayBackward(iteration, inReverse)
privatebooleanshouldPlayBackward(intiteration,booleaninReverse){if(iteration>0&&mRepeatMode==REVERSE&&(iteration<(mRepeatCount+1)||mRepeatCount==INFINITE)){if(inReverse){return(iteration%2)==0;// 反向启动时:偶数迭代反向}else{return(iteration%2)!=0;// 正向启动时:奇数迭代反向}}else{returninReverse;// 非逆序模式或第 0 次迭代}}5.6 Repeat 与 Reverse 原理
Repeat 机制
Repeat 通过总 fraction 超过 1来驱动。mOverallFraction可以大于 1,整数部分代表已完成的迭代数,小数部分代表当前迭代进度。
mOverallFraction = 2.35 │ ├─ iteration = 2 (已完成 2 次迭代,正在第 3 次) └─ iterFraction = 0.35 (当前迭代进度 35%)mRepeatCount = 0:不重复,fractionclamp 到[0, 1]mRepeatCount = 3:fractionclamp 到[0, 4],共播放 4 次mRepeatCount = INFINITE:不设上界
RESTART 模式:每次迭代都从 0→1,shouldPlayBackward始终返回inReverse(正向时为 false)。
REVERSE 模式:奇偶迭代方向交替:
| iteration | shouldPlayBackward(正向启动) | 实际方向 | fraction 变化 |
|---|---|---|---|
| 0 | false | 正向 | 0 → 1 |
| 1 | true | 反向 | 1 → 0 |
| 2 | false | 正向 | 0 → 1 |
| 3 | true | 反向 | 1 → 0 |
Reverse 机制
reverse()方法翻转动画方向,通过修改mReversing标志和mStartTime实现:
publicvoidreverse(){if(isPulsingInternal()){// 运行中:计算剩余时间,将 startTime 对齐到"从另一端开始"longcurrentTime=AnimationUtils.currentAnimationTimeMillis();longcurrentPlayTime=currentTime-mStartTime;longtimeLeft=getScaledDuration()-currentPlayTime;mStartTime=currentTime-timeLeft;// 使 fraction 翻转mStartTimeCommitted=true;mReversing=!mReversing;}elseif(mStarted){mReversing=!mReversing;end();// 已 start 但未运行,直接结束}else{start(true);// 未 start,以反向模式启动}}mReversing传入getCurrentIterationFraction的inReverse参数,影响shouldPlayBackward的计算,从而决定每个迭代的播放方向。
reverse + REVERSE repeatMode 的组合:mReversing = true时,shouldPlayBackward的奇偶判断取反,导致迭代的实际方向与mReversing = false时完全相反。
5.7 [8] animateValue(fraction) — 核心值计算
voidanimateValue(floatfraction){// [9] 节奏变换fraction=mInterpolator.getInterpolation(fraction);mCurrentFraction=fraction;// 保存插值后 fraction(供 getAnimatedFraction() 查询)// [10] 类型求值:对每个 PropertyValuesHolder 计算值for(inti=0;i<numValues;++i){mValues[i].calculateValue(fraction);}// [13] 通知 UpdateListenerif(mSeekFraction>=0||mStartListenersCalled){callOnList(mUpdateListeners,AnimatorCaller.ON_UPDATE,this,false);}}注意:传入animateValue的 fraction 已经是[0,1]范围的迭代 fraction。mInterpolator在此对其进行非线性映射,输出可能超出[0,1](如过冲、弹跳)。
5.8 [10][11][12] calculateValue → getValue → evaluate — 值计算链
PropertyValuesHolder.calculateValue(fraction) │ └─ mKeyframes.getValue(fraction) ← KeyframeSet 查找区间并插值 │ ├─ 定位区间 [prevKeyframe, nextKeyframe] │ ├─ 计算 intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction) │ ├─ 应用局部 Interpolator: intervalFraction = nextKf.getInterpolator().getInterpolation(intervalFraction) │ (若该 Keyframe 未设置 Interpolator,则使用线性) │ └─ mEvaluator.evaluate(intervalFraction, prevValue, nextValue) │ └─ result = startValue + fraction * (endValue - startValue) ← 线性插值IntKeyframeSet / FloatKeyframeSet 的优化:重写getIntValue()/getFloatValue(),直接使用原始类型计算,避免装箱开销。当mEvaluator == null时使用内置线性公式,否则调用 Evaluator。
最终:计算结果存入PropertyValuesHolder.mAnimatedValue,ObjectAnimator通过setAnimatedValue(target)将值设置到目标对象。
6. Seek 机制
通过setCurrentPlayTime()/setCurrentFraction()跳转到指定位置:
- 动画运行中:直接修改
mStartTime = currentTime - seekTime - 未运行时:设置
mSeekFraction,在首帧时应用(doAnimationFrame第1556-1559行)
7. Jank 补偿
首帧可能因调度延迟而产生 jank:
- 首帧设置
mStartTimeCommitted = false(第1561行) commitAnimationFrame(frameTime)被调用时,计算adjustment = frameTime - mLastFrameTime- 若
adjustment > 0,修正mStartTime += adjustment(第1363行) - 设置
mStartTimeCommitted = true,后续不再补偿
8. 事件通知
8.1 生命周期监听器(继承自Animator)
| 事件 | 时机 |
|---|---|
onAnimationStart | startAnimation()调用时(考虑 startDelay) |
onAnimationEnd | endAnimation()调用时 |
onAnimationCancel | cancel()调用时 |
onAnimationRepeat | 新迭代开始时 |
8.2 帧更新监听器
publicinterfaceAnimatorUpdateListener{voidonAnimationUpdate(ValueAnimatoranimation);}在animateValue()中,每次值计算完成后调用(第1650-1652行)。
9. AnimatorSet 集成
ValueAnimator提供了供AnimatorSet驱动的内部方法:
| 方法 | 用途 |
|---|---|
animateValuesInRange() | 在指定时间范围内计算动画值(支持跨迭代) |
animateSkipToEnds() | 跳过到起始/结束值 |
pulseAnimationFrame() | 由父 AnimatorSet 驱动帧(mSelfPulse = false时) |
startWithoutPulsing() | 启动但不自行注册帧回调 |
10. 线程模型
- 必须在拥有
Looper的线程调用start()/cancel()/end()/resume() - 所有帧回调在调用
start()的线程执行 currentAnimationTimeMillis()基于ThreadLocal,每个线程独立维护动画时钟
11. 工厂方法
| 方法 | 值类型 | 默认 Evaluator |
|---|---|---|
ofInt(int...) | int | IntEvaluator |
ofArgb(int...) | int (color) | ArgbEvaluator |
ofFloat(float...) | float | FloatEvaluator |
ofObject(TypeEvaluator, Object...) | 任意对象 | 自定义 |
ofPropertyValuesHolder(PropertyValuesHolder...) | 多属性并行 | 各自指定 |
12. Clone 机制
clone()执行深拷贝:
- 复制
mUpdateListeners列表 - 重置所有运行时状态(时间、fraction、标志位)
- 深拷贝
PropertyValuesHolder[]及其mValuesMap