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
@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;
}
}
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);
}
}
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实例
}
}
/**
* 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);
}
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);
}
/**
* 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));
}
}
//ValueAnimator
boolean pulseAnimationFrame(long frameTime) {
return doAnimationFrame(frameTime);
}
public final boolean doAnimationFrame(long frameTime) {
boolean finished = animateBasedOnTime(currentTime);
}
boolean animateBasedOnTime(long currentTime) {
animateValue(currentIterationFraction);
}
//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);//后估值器
}
}
//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}
/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
//nCallFloatMethod调用之后如果是本属性则会调用到这里
public void setTranslationX(float translationX) {
if (translationX != getTranslationX()) {
invalidateViewProperty(true, false);
mRenderNode.setTranslationX(translationX);
invalidateViewProperty(false, true);
invalidateParentIfNeededAndWasQuickRejected();
notifySubtreeAccessibilityStateChangedIfNeeded();
}
}
// 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);
}
}
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);
}
public void dispatchMessage(Message msg) {}
private static void handleCallback(Message message) {
message.callback.run();
}
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
doTraversal() {
performTraversals();