AnimatorSource

Animator类设计

插值器和估值器设计

参考doanimationframe

classDiagram

class TimeInterpolator {
  +getInterpolation(float input) float
}

class BaseInterpolator {
  -int mChangingConfiguration
}

class LinearInterpolator {
  +getInterpolation(float input) float
}
class AccelerateDecelerateInterpolator {
 +getInterpolation(float input) float
}

TimeInterpolator<|--Interpolator
Interpolator <|-- BaseInterpolator
BaseInterpolator <|-- LinearInterpolator
BaseInterpolator<|-- AccelerateDecelerateInterpolator

class Keyframes{
 setEvaluator(TypeEvaluator evaluator)
}
class FloatKeyframes{
 getFloatValue(float fraction) float
}
class KeyframeSet {
 TypeEvaluator mEvaluator
}
class FloatKeyframeSet {
 getFloatValue(float fraction) float
}
class TypeEvaluator~T~{
 +evaluate(float fraction, T startValue, T endValue) T
}
Keyframes<|--FloatKeyframes
Keyframes<|--KeyframeSet
KeyframeSet<|--FloatKeyframeSet
KeyframeSet--*TypeEvaluator~T~

图解

sequenceDiagram

AnimatorSet->>AnimatorSet: start

participant Animator
participant FloatPropertyValuesHolder

activate AnimatorSet

AnimatorSet->>AnimationHandler: handler.addAnimationFrameCallback

AnimationHandler->>AnimationFrameCallbackProvider: getProvider().postFrameCallback(mFrameCallback)

AnimationFrameCallbackProvider->>Choreographer: mChoreographer.postFrameCallback(callback);

AnimatorSet->>Animator: startWithoutPulsing
Note right of AnimatorSet: AinmatorSet启动时会接管pluseFrame,不调用Animator自生的frame回调
Animator->>Animator: start

Animator->>AnimationHandler: getAnimationHandler().addAnimationFrameCallback


Choreographer->>Choreographer: Choreographer.FrameCallback.doFrame
activate Choreographer

Choreographer->>AnimatorSet: 对每个mAnimationCallbacks(AnimatorSet或ValueAnimator实例),调用doAnimationFrame

AnimatorSet->>Animator: animateValue

Choreographer->>Animator: doAnimationFrame

Animator->>Animator: animateValue
activate Animator


Animator->>FloatPropertyValuesHolder: super.animateValue(fraction)计算更新mValues值

activate Animator

Note right of Animator: fraction = mInterpolator.getInterpolation(fraction)//先插值器
Note right of Animator: mValues[i].calculateValue(fraction)//后估值器

deactivate Animator

Animator->>FloatPropertyValuesHolder:mValues[i].setAnimatedValue(target);
Note right of FloatPropertyValuesHolder: jni或反射调用对应view的属性修改方法,优先jni,其次反射

deactivate Animator

Choreographer->>AnimationFrameCallbackProvider: getProvider().postFrameCallback(this);

deactivate Choreographer
deactivate AnimatorSet

AnimatorSet.start

@Override
 public void start() {
   start(false, true);
 }
private void start(boolean inReverse, boolean selfPulse) {
    if (Looper.myLooper() == null) {
        throw new AndroidRuntimeException("Animators may only be run on Looper threads");
    }
    initAnimation();
  
    // Now that all dependencies are set up, start the animations that should be started.
    boolean isEmptySet = isEmptySet(this);
    if (!isEmptySet) {
        startAnimation();//main
    }
  
    if (mListeners != null) {
        ArrayList<AnimatorListener> tmpListeners =
                (ArrayList<AnimatorListener>) mListeners.clone();
        int numListeners = tmpListeners.size();
        for (int i = 0; i < numListeners; ++i) {
            tmpListeners.get(i).onAnimationStart(this, inReverse);
        }
    }
}
    private void startAnimation() {
        addDummyListener();
        // Register animation callback
        addAnimationCallback(0);
        ......
        if (mReversing || mStartDelay == 0 || mSeekState.isActive()) {
            long playTime;
            // If no delay, we need to call start on the first animations to be consistent with old
            // behavior.
            if (mSeekState.isActive()) {
                mSeekState.updateSeekDirection(mReversing);
                playTime = mSeekState.getPlayTime();
            } else {
                playTime = 0;
            }
            int toId = findLatestEventIdForTime(playTime);
            handleAnimationEvents(-1, toId, playTime);//main
            for (int i = mPlayingSet.size() - 1; i >= 0; i--) {
                if (mPlayingSet.get(i).mEnded) {
                    mPlayingSet.remove(i);
                }
            }
            mLastEventId = toId;
        }
    }

private void addAnimationCallback(long delay) {
    if (!mSelfPulse) {
        return;
    }
    AnimationHandler handler = AnimationHandler.getInstance();
    handler.addAnimationFrameCallback(this, delay);
}
@Override
public boolean doAnimationFrame(long frameTime) {//callback
   // Pump a frame to the on-going animators
   for (int i = 0; i < mPlayingSet.size(); i++) {
        Node node = mPlayingSet.get(i);
        if (!node.mEnded) {
            pulseFrame(node, getPlayTimeForNode(unscaledPlayTime, node));//main
        }
    }
  
    if (finished) {
       endAnimation();
       return true;
   }
}

animationHandler.addAnimationCallback

public final static ThreadLocal<AnimationHandler> sAnimatorHandler = new ThreadLocal<>();
public static AnimationHandler getInstance() {
    if (sAnimatorHandler.get() == null) {
        sAnimatorHandler.set(new AnimationHandler());
    }
    return sAnimatorHandler.get();
}
private AnimationFrameCallbackProvider mProvider;
/**
 * Register to get a callback on the next frame after the delay.
 */
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));
    }
}
interface AnimationFrameCallbackProvider {
  void postFrameCallback(Choreographer.FrameCallback callback);
 void postCommitCallback(Runnable runnable);
 long getFrameTime();
 long getFrameDelay();
 void setFrameDelay(long delay);
}

/**
 * Default provider of timing pulse that uses Choreographer for frame callbacks.
 */
 private class MyFrameCallbackProvider implements AnimationFrameCallbackProvider {
 final Choreographer mChoreographer = Choreographer.getInstance();

@Override
 public void postFrameCallback(Choreographer.FrameCallback callback) {
   mChoreographer.postFrameCallback(callback);
 }
 }

mFrameCallback.doFrame

private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
   @Override
   public void doFrame(long frameTimeNanos) {
     doAnimationFrame(getProvider().getFrameTime());
     if (mAnimationCallbacks.size() > 0) {
       getProvider().postFrameCallback(this);
     }
   }
 };
/** Callbacks that receives notifications for animation timing and frame commit timing.*/
interface AnimationFrameCallback {
    boolean doAnimationFrame(long frameTime);
    void commitAnimationFrame(long frameTime);
}

private void doAnimationFrame(long frameTime) {
  for (int i = 0; i < size; i++) {
     final AnimationFrameCallback callback = mAnimationCallbacks.get(i);
     callback.doAnimationFrame(frameTime);//callback为AnimatorSet或ValueAnimator实例
  }
}

Animator.start

/**
 * When playing forward, we call start() at the animation's scheduled start time, and make sure
 * to pump a frame at the animation's scheduled end time.
 *
 * When playing in reverse, we should reverse the animation when we hit animation's end event,
 * and expect the animation to end at the its delay ended event, rather than start event.
 */
private void handleAnimationEvents(int startId, int latestId, long playTime) {
    if (mReversing) {
      ......
    } else {
        for (int i = startId + 1; i <= latestId; i++) {
            AnimationEvent event = mEvents.get(i);
            Node node = event.mNode;
            if (event.mEvent == AnimationEvent.ANIMATION_START) {
                mPlayingSet.add(event.mNode);
                if (node.mAnimation.isStarted()) {
                    // If the animation has already been started before its due time (i.e.
                    // the child animator is being manipulated outside of the AnimatorSet), we
                    // need to cancel the animation to reset the internal state (e.g. frame
                    // time tracking) and remove the self pulsing callbacks
                    node.mAnimation.cancel();
                }
                node.mEnded = false;
                node.mAnimation.startWithoutPulsing(false);
                pulseFrame(node, 0);
            } else if (event.mEvent == AnimationEvent.ANIMATION_END && !node.mEnded) {
                // start event:
                pulseFrame(node, getPlayTimeForNode(playTime, node));
            }
        }
    }
}
/**
 * Internal use only.
 * This call starts the animation in regular or reverse direction without requiring them to
 * register frame callbacks. The caller will be responsible for all the subsequent animation
 * pulses. Specifically, the caller needs to call doAnimationFrame(...) for the animation on
 * every frame.
 *
 * @param inReverse whether the animation should play in reverse direction
 */
void startWithoutPulsing(boolean inReverse) {
    if (inReverse) {
        reverse();
    } else {
        start();
    }
}
@Override
 public void start() {
   start(false);
 }
private void start(boolean playBackwards) {
    if (Looper.myLooper() == null) {
        throw new AndroidRuntimeException("Animators may only be run on Looper threads");
    }
    addAnimationCallback(0);
}

addAnimationCallback

private void addAnimationCallback(long delay) {
    if (!mSelfPulse) {
        return;
    }
    getAnimationHandler().addAnimationFrameCallback(this, delay);
}

public final boolean doAnimationFrame(long frameTime) {
      boolean finished = animateBasedOnTime(currentTime);//1
      if (finished) {
          endAnimation();//2
        }
      return finished;
}

boolean animateBasedOnTime(long currentTime) {
  animateValue(currentIterationFraction);//main
}

private void endAnimation() {
    removeAnimationCallback();
}
private void removeAnimationCallback() {
    getAnimationHandler().removeCallback(this);
}

pulseFrame

/**
 * This method pulses frames into child animations. It scales the input animation play time
 * with the duration scale and pass that to the child animation via pulseAnimationFrame(long).
 *
 * @param node child animator node
 * @param animPlayTime unscaled play time (including start delay) for the child animator
 */
private void pulseFrame(Node node, long animPlayTime) {
    if (!node.mEnded) {
        float durationScale = ValueAnimator.getDurationScale();
        durationScale = durationScale == 0  ? 1 : durationScale;
        node.mEnded = node.mAnimation.pulseAnimationFrame(
                (long) (animPlayTime * durationScale));
    }
}

doAnimationFrame

//ValueAnimator
boolean pulseAnimationFrame(long frameTime) {
    return doAnimationFrame(frameTime); 
}
public final boolean doAnimationFrame(long frameTime) {
   boolean finished = animateBasedOnTime(currentTime);
}
boolean animateBasedOnTime(long currentTime) {
    animateValue(currentIterationFraction);
}
animateValue
//ObjectAnimator
@Override
 void animateValue(float fraction) {
   final Object target = getTarget();
   super.animateValue(fraction);
   int numValues = mValues.length;
   for (int i = 0; i < numValues; ++i) {
     mValues[i].setAnimatedValue(target);
   }
 }
//ValueAnimator
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);//后估值器
   }
}
setAnimatedValue
//PropertyValuesHolder
static class FloatPropertyValuesHolder extends PropertyValuesHolder {
Keyframes.FloatKeyframes mFloatKeyframes;

@Override
 void calculateValue(float fraction) {
   mFloatAnimatedValue = mFloatKeyframes.getFloatValue(fraction);
 }

@Override
 void setAnimatedValue(Object target) {
 //使用jni或反射调用对应view的属性修改方法,优先jni,其次反射
 if (mJniSetter != 0) {
    //jni调用之后,会调用对应view进行属性设置,如View.setTranslationX
    nCallFloatMethod(target, mJniSetter, mFloatAnimatedValue);
    return;
 }
   
native static private void nCallFloatMethod(Object target, long methodID, float arg);

/frameworks/base/core/jni/android_animation_PropertyValuesHolder.cpp

142static const JNINativeMethod gMethods[] = {
143    {   "nGetIntMethod", "(Ljava/lang/Class;Ljava/lang/String;)J",
144            (void*)android_animation_PropertyValuesHolder_getIntMethod },
145    {   "nGetFloatMethod", "(Ljava/lang/Class;Ljava/lang/String;)J",
146            (void*)android_animation_PropertyValuesHolder_getFloatMethod },
167};

41static jlong android_animation_PropertyValuesHolder_getFloatMethod(
42        JNIEnv* env, jclass pvhClass, jclass targetClass, jstring methodName)
43{
44    const char *nativeString = env->GetStringUTFChars(methodName, 0);
45    jmethodID mid = env->GetMethodID(targetClass, nativeString, "(F)V");
46    env->ReleaseStringUTFChars(methodName, nativeString);
47    return reinterpret_cast<jlong>(mid);
48}
nCallFloatMethod
 /frameworks/base/tools/layoutlib/bridge/src/android/animation/PropertyValuesHolder_Delegate.java

@LayoutlibDelegate
 /*package*/ static void nCallFloatMethod(Object target, long methodID, float arg) {
   callMethod(target, methodID, arg);
 }

private static void callMethod(Object target, long methodID, Object... args) {
   Method method = ID_TO_METHOD.get(methodID);
   assert method != null;
   try {
     method.setAccessible(true);
     method.invoke(target, args);
   } catch (IllegalAccessException | InvocationTargetException e) {
     Bridge.getLog().error(null, "Unable to update property during animation", e, null);
   }
 }
View.setTranslationX
//View
//nCallFloatMethod调用之后如果是本属性则会调用到这里
public void setTranslationX(float translationX) {
   if (translationX != getTranslationX()) {
     invalidateViewProperty(true, false);
     mRenderNode.setTranslationX(translationX);
     invalidateViewProperty(false, true);
 
     invalidateParentIfNeededAndWasQuickRejected();
     notifySubtreeAccessibilityStateChangedIfNeeded();
   }
 }

其他

Choreographer

// The display event receiver can only be accessed by the looper thread to which
 // it is attached. We take care to ensure that we post message to the looper
 // if appropriate when interacting with the display event receiver.
 private final FrameDisplayEventReceiver mDisplayEventReceiver;

private final class FrameDisplayEventReceiver extends DisplayEventReceiver
     implements Runnable {
     @Override
     public void onVsync(long timestampNanos, int builtInDisplayId, int frame) {
            Message msg = Message.obtain(mHandler, this);//call run method
            msg.setAsynchronous(true);
            mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
     }
}

@Override
 public void run() {
   mHavePendingVsync = false;
   doFrame(mTimestampNanos, mFrame);
 }

void doFrame(long frameTimeNanos, int frame) {
 mFrameInfo.markInputHandlingStart();
 doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);

 mFrameInfo.markAnimationsStart();
 doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);

 mFrameInfo.markPerformTraversalsStart();
 doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);
}

void doCallbacks(int callbackType, long frameTimeNanos) {
CallbackRecord callbacks;
 synchronized (mLock) {
   // We use "now" to determine when callbacks become due because it's possible
   // for earlier processing phases in a frame to post callbacks that should run
   // in a following phase, such as an input event that causes an animation to start.
   final long now = System.nanoTime();
   callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(
       now / TimeUtils.NANOS_PER_MS);

  for (CallbackRecord c = callbacks; c != null; c = c.next) {
    c.run(frameTimeNanos);
  }
 }

CallbackRecord

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();
   }
 }

public interface FrameCallback {
  public void doFrame(long frameTimeNanos);
}

Handler

public void dispatchMessage(Message msg) {}
private static void handleCallback(Message message) {
   message.callback.run();
 }

ViewRootImpl

final class TraversalRunnable implements Runnable {
   @Override
   public void run() {
     doTraversal();
   }
 }

doTraversal() {
performTraversals();