【疑问1】ObjectAnimator如何通过"translationX"这样实现的动画
【疑问2】ValueAnimator在INFINITE模式下,为什么cancel和end会失效(误会?其他未知bug?)
【1】ObjectAnimator.ofFloat(...)着手
先附上创建ObjectAnimator的源码
public static ObjectAnimator ofFloat(Object target, String propertyName, float... values) {
//->(1)
ObjectAnimator anim = new ObjectAnimator(target, propertyName);
anim.setFloatValues(values);
return anim;
}
(1)
private ObjectAnimator(Object target, String propertyName) {
//->(2)
setTarget(target);
//->(3)
setPropertyName(propertyName);
}
(2)
public void setTarget(@Nullable Object target) {
final Object oldTarget = getTarget();
if (oldTarget != target) {
if (isStarted()) {
cancel();
}
mTarget = target == null ? null : new WeakReference
代码(2)setTarget是设置目标View
代码(3)setPropertyName是设置具体属性
设置完 目标View 和 目标属性 后略过其他,直接分析start
public void start() {
AnimationHandler.getInstance().autoCancelBasedOn(this);
if (DBG) {
Log.d(LOG_TAG, "Anim target, duration: " + getTarget() + ", " + getDuration());
for (int i = 0; i < mValues.length; ++i) {
PropertyValuesHolder pvh = mValues[i];
Log.d(LOG_TAG, " Values[" + i + "]: " +
pvh.getPropertyName() + ", " + pvh.mKeyframes.getValue(0) + ", " +
pvh.mKeyframes.getValue(1));
}
}
super.start();
}
ObjectAnimator是ValueAnimator的子类,super.start()就是ValueAnimator的start()
public void start() {
start(false);
}
//playBackwards是false,表示不reverse执行动画
private void start(boolean playBackwards) {
if (Looper.myLooper() == null) {
throw new AndroidRuntimeException("Animators may only be run on Looper threads");
}
mReversing = playBackwards;
mSelfPulse = !mSuppressSelfPulseRequested;
// Special case: reversing from seek-to-0 should act as if not seeked at all.
if (playBackwards && mSeekFraction != -1 && mSeekFraction != 0) {
if (mRepeatCount == INFINITE) {
// Calculate the fraction of the current iteration.
float fraction = (float) (mSeekFraction - Math.floor(mSeekFraction));
mSeekFraction = 1 - fraction;
} else {
mSeekFraction = 1 + mRepeatCount - mSeekFraction;
}
}
(1)
mStarted = true;
mPaused = false;
mRunning = false;
mAnimationEndRequested = false;
// Resets mLastFrameTime when start() is called, so that if the animation was running,
// calling start() would put the animation in the
// started-but-not-yet-reached-the-first-frame phase.
mLastFrameTime = -1;
mFirstFrameTime = -1;
mStartTime = -1;
(2)
addAnimationCallback(0);
(3)
if (mStartDelay == 0 || mSeekFraction >= 0 || mReversing) {
// If there's no start delay, init the animation and notify start listeners right away
// to be consistent with the previous behavior. Otherwise, postpone this until the first
// frame after the start delay.
startAnimation();
if (mSeekFraction == -1) {
// No seek, start at play time 0. Note that the reason we are not using fraction 0
// is because for animations with 0 duration, we want to be consistent with pre-N
// behavior: skip to the final value immediately.
setCurrentPlayTime(0);
} else {
setCurrentFraction(mSeekFraction);
}
}
}
(1)此时mStarted为true,mRunning为false。说明属性动画的Start状态先于Running状态,符合后面的注释描述。
(2)addAnimationHandler中涉及到Choreographer,以此实现动画。
private void addAnimationCallback(long delay) {
if (!mSelfPulse) {
return;
}
getAnimationHandler().addAnimationFrameCallback(this, delay);
}
//每个线程持有一个单例,基于ThreadLocal实现的
public AnimationHandler getAnimationHandler() {
return AnimationHandler.getInstance();
}
public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {
if (mAnimationCallbacks.size() == 0) {
getProvider().postFrameCallback(mFrameCallback);
}
if (!mAnimationCallbacks.contains(callback)) {
mAnimationCallbacks.add(callback);
}
if (delay > 0) {
mDelayedCallbackStartTime.put(callback, (SystemClock.uptimeMillis() + delay));
}
}
第一个start,会执行代码: getProvider().postFrameCallback(mFrameCallback);
追踪代码,发现getProvider()会得到MyFrameCallbackProvider的实例类,从这里就涉及到Choreographer了
private class MyFrameCallbackProvider implements AnimationFrameCallbackProvider {
final Choreographer mChoreographer = Choreographer.getInstance();
@Override
public void postFrameCallback(Choreographer.FrameCallback callback) {
mChoreographer.postFrameCallback(callback);
}
...
}
这里不分析Choreographer,记住callback是mFrameCallback。后续流程会将mFrameCallback加入Choreographer中的mCallbackQueues,并开始监听下一个Vsync信号(当信号来到时就会处理动画逻辑)。追踪代码的过程中会有这么一行代码,后面分析会涉及到。
postCallbackDelayedInternal(CALLBACK_ANIMATION,callback, FRAME_CALLBACK_TOKEN, delayMillis);
private final class FrameDisplayEventReceiver extends DisplayEventReceiver
implements Runnable {
private boolean mHavePendingVsync;
private long mTimestampNanos;
private int mFrame;
public FrameDisplayEventReceiver(Looper looper, int vsyncSource) {
super(looper, vsyncSource);
}
@Override
public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
...
mTimestampNanos = timestampNanos;
mFrame = frame;
//利用同步屏障,立即执行run()方法
Message msg = Message.obtain(mHandler, this);
msg.setAsynchronous(true);
mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
}
@Override
public void run() {
mHavePendingVsync = false;
doFrame(mTimestampNanos, mFrame);
}
}
接收信号,走到onVsync中,并通过Handler执行到run() -> doFrame(...)方法。在doFrame中,会调用doCallbacks(...)
void doFrame(long frameTimeNanos, int frame) {
final long startNanos;
...
try {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame");
AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);
mFrameInfo.markInputHandlingStart();
doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);
mFrameInfo.markAnimationsStart();
doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);
mFrameInfo.markPerformTraversalsStart();
doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);
} finally {
AnimationUtils.unlockAnimationClock();
Trace.traceEnd(Trace.TRACE_TAG_VIEW);
}
...
}
void doCallbacks(int callbackType, long frameTimeNanos) {
CallbackRecord callbacks;
...
try {
Trace.traceBegin(Trace.TRACE_TAG_VIEW, CALLBACK_TRACE_TITLES[callbackType]);
for (CallbackRecord c = callbacks; c != null; c = c.next) {
if (DEBUG_FRAMES) {
Log.d(TAG, "RunCallback: type=" + callbackType
+ ", action=" + c.action + ", token=" + c.token
+ ", latencyMillis=" + (SystemClock.uptimeMillis() - c.dueTime));
}
c.run(frameTimeNanos);
}
}
...
}
private static final class CallbackRecord {
public CallbackRecord next;
public long dueTime;
public Object action; // Runnable or FrameCallback
public Object token;
public void run(long frameTimeNanos) {
if (token == FRAME_CALLBACK_TOKEN) {
((FrameCallback)action).doFrame(frameTimeNanos);
} else {
((Runnable)action).run();
}
}
}
doCallbacks(...)又会进一步执行mCallbackQueues中的“callback”。对于ObjectAnimator,我们本次仅关注Choreographer.CALLBACK_ANIMATION类型的callback。类似于Handler的Message,根据时间判断,获取run的callback并按顺序执行。上面说到会涉及到这段代码,这里就用到了
postCallbackDelayedInternal(CALLBACK_ANIMATION,callback, FRAME_CALLBACK_TOKEN, delayMillis);
因为FRAME_CALLBACK_TOKEN,所以最终会回归到之前的mFrameCallback
private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
doAnimationFrame(getProvider().getFrameTime());
if (mAnimationCallbacks.size() > 0) {
getProvider().postFrameCallback(this);
}
}
};
doAnimationFrame中执行一些动画状态相关的处理,并通过boolean finished = animateBasedOnTime(currentTime);计算属性值,所以属性动画是通过ANIMATION类型的callback不断计算属性值。
doAnimationFrame执行完后,根据mAnimationCallbacks判断是否还有需要继续执行的属性动画,如果有,则继续利用getProvider().postFrameCallback(this); 循环监听Vsync信号更新界面。而
这里涉及到一点,就是只有当mAnimationCallbacks的size为0时,才会将callback加入Choreographer,后续的都是在复用之前的同一个Vsync信号。
为了让动画真正执行,肯定需要不断计算更新view的属性,并触发绘制。回到最开始,ValueAnimator的start(...)方法中,接着addAnimationCallback往后执行。
private void start(boolean playBackwards) {
...
addAnimationCallback(0);
if (mStartDelay == 0 || mSeekFraction >= 0 || mReversing) {
// If there's no start delay, init the animation and notify start listeners right away
// to be consistent with the previous behavior. Otherwise, postpone this until the first
// frame after the start delay.
startAnimation();
if (mSeekFraction == -1) {
// No seek, start at play time 0. Note that the reason we are not using fraction 0
// is because for animations with 0 duration, we want to be consistent with pre-N
// behavior: skip to the final value immediately.
setCurrentPlayTime(0);
} else {
setCurrentFraction(mSeekFraction);
}
}
}
mStartDelay是否延迟
mSeekFraction执行百分比
mReversing动画是否反转执行
对于默认情况 最开始 mSeekFraction为-1,mReversing为false,而mStartDelay为为0,所以会走进逻辑。但是当我们设置delay延迟后,就走不进来了?这时可以看看doAnimationFrame会发现里面有段逻辑是专门处理这种情况。当然还会有各种各样的情况,等遇到时,再补上分析的代码吧。接着分析调用流程 startAnimation() -> initAnimation() -> notifyStartListeners()
//ObjectAnimator.java
void initAnimation() {
if (!mInitialized) {
// mValueType may change due to setter/getter setup; do this before calling super.init(),
// which uses mValueType to set up the default type evaluator.
final Object target = getTarget();
if (target != null) {
final int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
//设置getter,setter方法
mValues[i].setupSetterAndGetter(target);
}
}
super.initAnimation();
}
}
//ValueAnimator.java
void initAnimation() {
if (!mInitialized) {
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].init();
}
mInitialized = true;
}
}
mValues是创建ObjectAnimator示例时生成的 PropertyValuesHolder[] 数组,那么往下追踪PropertyValuesHolder的init()方法。
void init() {
if (mEvaluator == null) {
// We already handle int and float automatically, but not their Object
// equivalents
mEvaluator = (mValueType == Integer.class) ? sIntEvaluator :
(mValueType == Float.class) ? sFloatEvaluator :
null;
}
if (mEvaluator != null) {
// KeyframeSet knows how to evaluate the common types - only give it a custom
// evaluator if one has been set on this class
mKeyframes.setEvaluator(mEvaluator);
}
}
就是为每个属性设置估值器,暂不深入。再之后就是调用setCurrentPlayTime,其实内部也是调用setCurrentFraction
public void setCurrentPlayTime(long playTime) {
float fraction = mDuration > 0 ? (float) playTime / mDuration : 1;
setCurrentFraction(fraction);
}
public void setCurrentFraction(float fraction) {
initAnimation();
fraction = clampFraction(fraction);
mStartTimeCommitted = true; // do not allow start time to be compensated for jank
if (isPulsingInternal()) {
long seekTime = (long) (getScaledDuration() * fraction);
long currentTime = AnimationUtils.currentAnimationTimeMillis();
// Only modify the start time when the animation is running. Seek fraction will ensure
// non-running animations skip to the correct start time.
mStartTime = currentTime - seekTime;
} else {
// If the animation loop hasn't started, or during start delay, the startTime will be
// adjusted once the delay has passed based on seek fraction.
mSeekFraction = fraction;
}
mOverallFraction = fraction;
final float currentIterationFraction = getCurrentIterationFraction(fraction, mReversing);
animateValue(currentIterationFraction);
}
计算fraction信息,调用animateValue
//ObjectAnimator.java
void animateValue(float fraction) {
final Object target = getTarget();
if (mTarget != null && target == null) {
// We lost the target reference, cancel and clean up. Note: we allow null target if the
/// target has never been set.
cancel();
return;
}
super.animateValue(fraction);
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
//实际就是调用对应的setter方法
mValues[i].setAnimatedValue(target);
}
}
//ValueAnimator.java
void animateValue(float fraction) {
fraction = mInterpolator.getInterpolation(fraction);
mCurrentFraction = fraction;
int numValues = mValues.length;
for (int i = 0; i < numValues; ++i) {
mValues[i].calculateValue(fraction);
}
if (mUpdateListeners != null) {
int numListeners = mUpdateListeners.size();
for (int i = 0; i < numListeners; ++i) {
mUpdateListeners.get(i).onAnimationUpdate(this);
}
}
}
对于ObjectAnimator,mValues不为空,mUpdatelisteners为空,所以关键代码是 mValues[i].calculateValue(fraction);
对于ValueAnimator,使用时会addUpdateListener,所以mUpdateListeners不为空。
所以对于ObjectAnimator.ofFloat(...),对于每个属性,调用calculateValue计算属性值,再调用setAnimatedValue调用setter方法。在setter方法中又会调用invalidate触发重新绘制。到这里ObjectAnimator的绘制流程大致就分析完了,其实这里还欠缺生成ObjectAnimator示例的分析。
【疑问2】上述分析其实已经涉及到ValueAnimator动画的一些流程。那么分析cancel和end应该就可以得到想要的结果。
public void cancel() {
...
endAnimation();
}
public void end() {
...
endAnimation();
}
关键要分析endAnimation方法
private void endAnimation() {
if (mAnimationEndRequested) {
return;
}
removeAnimationCallback();
mAnimationEndRequested = true;
mPaused = false;
boolean notify = (mStarted || mRunning) && mListeners != null;
if (notify && !mRunning) {
// If it's not yet running, then start listeners weren't called. Call them now.
notifyStartListeners();
}
mRunning = false;
mStarted = false;
mStartListenersCalled = false;
mLastFrameTime = -1;
mFirstFrameTime = -1;
mStartTime = -1;
if (notify && mListeners != null) {
ArrayList tmpListeners =
(ArrayList) mListeners.clone();
int numListeners = tmpListeners.size();
for (int i = 0; i < numListeners; ++i) {
tmpListeners.get(i).onAnimationEnd(this, mReversing);
}
}
// mReversing needs to be reset *after* notifying the listeners for the end callbacks.
mReversing = false;
if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) {
Trace.asyncTraceEnd(Trace.TRACE_TAG_VIEW, getNameForTrace(),
System.identityHashCode(this));
}
}
其实就是调用Listeners回调,并且重置标志位等一些基本操作。关键是removeCallback,也就是将当前ValueAnimator从列表中移除,那么后续Choreographer处理时,就不会再执行动画。有意思的事情是,当我分析完这部分代码。再次运行代码验证时,疑问2的问题竟然不再复现了。。。