注意Animator没有自己的thread, 相反,使用start 它的thread, 只有一个可以自己去thread的人 post 的 只是handler * The animation started by calling this method will be run on the thread that called * this method. This thread should have a Looper on it. Also, if the animation will animate * properties of objects in the view hierarchy, then the calling thread should be the UI * thread for that view hierarchyprivate void start(boolean playBackwards) { if (Looper.myLooper() == null) { throw new AndroidRuntimeException("Animators may only be run on Looper threads"); } mPlayingBackwards = playBackwards; mCurrentIteration = 0; mPlayingState = STOPPED; mStarted = true; mStartedDelay = false; mPaused = false; AnimationHandler animationHandler = getOrCreateAnimationHandler(); animationHandler.mPendingAnimations.add(this); if (mStartDelay == 0) { // This sets the initial value of the animation, prior to actually starting it running setCurrentPlayTime(0); mPlayingState = STOPPED; mRunning = true; notifyStartListeners(); } animationHandler.start();}private static AnimationHandler getOrCreateAnimationHandler() { AnimationHandler handler = sAnimationHandler.get(); if (handler == null) { handler = new AnimationHandler(); sAnimationHandler.set(handler); } return handler;} protected static ThreadLocal<AnimationHandler> sAnimationHandler = new ThreadLocal<AnimationHandler>();protected static class AnimationHandler implements Runnable { ... private AnimationHandler() { mChoreographer = Choreographer.getInstance(); } ... public void start() { scheduleAnimation(); } ... private void scheduleAnimation() { if (!mAnimationScheduled) { mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, this, null); mAnimationScheduled = true; } } ... // Called by the Choreographer. @Override public void run() { mAnimationScheduled = false; doAnimationFrame(mChoreographer.getFrameTime()); } ... private void doAnimationFrame(long frameTime) { // mPendingAnimations holds any animations that have requested to be started // We're going to clear mPendingAnimations, but starting animation may // cause more to be added to the pending list (for example, if one animation // starting triggers another starting). So we loop until mPendingAnimations // is empty. while (mPendingAnimations.size() > 0) { ArrayList<ValueAnimator> pendingCopy = (ArrayList<ValueAnimator>) mPendingAnimations.clone(); mPendingAnimations.clear(); int count = pendingCopy.size(); for (int i = 0; i < count; ++i) { ValueAnimator anim = pendingCopy.get(i); // If the animation has a startDelay, place it on the delayed list if (anim.mStartDelay == 0) { anim.startAnimation(this); } else { mDelayedAnims.add(anim); } } } ... int numAnims = mAnimations.size(); for (int i = 0; i < numAnims; ++i) { mTmpAnimations.add(mAnimations.get(i)); } for (int i = 0; i < numAnims; ++i) { ValueAnimator anim = mTmpAnimations.get(i); if (mAnimations.contains(anim) && anim.doAnimationFrame(frameTime)) { mEndingAnims.add(anim); } } ...if (!mAnimations.isEmpty() || !mDelayedAnims.isEmpty()) { scheduleAnimation(); } }}...private void startAnimation(AnimationHandler handler) { if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, getNameForTrace(), System.identityHashCode(this)); } initAnimation(); handler.mAnimations.add(this); if (mStartDelay > 0 && mListeners != null) { // Listeners were already notified in start() if startDelay is 0; this is // just for delayed animations notifyStartListeners(); }}/** * Processes a frame of the animation, adjusting the start time if needed. * * @param frameTime The frame time. * @return true if the animation has ended. */final boolean doAnimationFrame(long frameTime) { if (mPlayingState == STOPPED) { mPlayingState = RUNNING; if (mSeekTime < 0) { mStartTime = frameTime; } else { mStartTime = frameTime - mSeekTime; // Now that we're playing, reset the seek time mSeekTime = -1; } } if (mPaused) { if (mPauseTime < 0) { mPauseTime = frameTime; } return false; } else if (mResumed) { mResumed = false; if (mPauseTime > 0) { // Offset by the duration that the animation was paused mStartTime += (frameTime - mPauseTime); } } // The frame time might be before the start time during the first frame of // an animation. The "current time" must always be on or after the start // time to avoid animating frames at negative time intervals. In practice, this // is very rare and only happens when seeking backwards. final long currentTime = Math.max(frameTime, mStartTime); return animationFrame(currentTime);}/** * This internal function processes a single animation frame for a given animation. The * currentTime parameter is the timing pulse sent by the handler, used to calculate the * elapsed duration, and therefore * the elapsed fraction, of the animation. The return value indicates whether the animation * should be ended (which happens when the elapsed time of the animation exceeds the * animation's duration, including the repeatCount). * * @param currentTime The current time, as tracked by the static timing handler * @return true if the animation's duration, including any repetitions due to * <code>repeatCount</code>, has been exceeded and the animation should be ended. */boolean animationFrame(long currentTime) { boolean done = false; switch (mPlayingState) { case RUNNING: case SEEKED: float fraction = mDuration > 0 ? (float)(currentTime - mStartTime) / mDuration : 1f; if (fraction >= 1f) { if (mCurrentIteration < mRepeatCount || mRepeatCount == INFINITE) { // Time to repeat if (mListeners != null) { int numListeners = mListeners.size(); for (int i = 0; i < numListeners; ++i) { mListeners.get(i).onAnimationRepeat(this); } } if (mRepeatMode == REVERSE) { mPlayingBackwards = !mPlayingBackwards; } mCurrentIteration += (int)fraction; fraction = fraction % 1f; mStartTime += mDuration; } else { done = true; fraction = Math.min(fraction, 1.0f); } } if (mPlayingBackwards) { fraction = 1f - fraction; } animateValue(fraction); break; } return done;}/** * This method is called with the elapsed fraction of the animation during every * animation frame. This function turns the elapsed fraction into an interpolated fraction * and then into an animated value (from the evaluator. The function is called mostly during * animation updates, but it is also called when the <code>end()</code> * function is called, to set the final value on the property. * * <p>Overrides of this method must call the superclass to perform the calculation * of the animated value.</p> * * @param fraction The elapsed fraction of the animation. */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); } }}
1.ValueAnimator->start():
Runnnable获得Threadlocal(animationHandler),一些关于本animator的信息封存在里面,然后animationHandler.start()
2.AnimationHandler->start()->scheduleAnimation()->mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, this, null):
<CALLBACK_ANIMATION TASK, CALBACK在Choreographer中的优先级较低INPUT, 高于CALBACK_TRAVERSAL,因此,可以认为android的每一帧
动画是优先运行的。所有动画处理完毕后,轮到measure了 layout draw>
把你的schedule给choreographer,让choreographer在合适的时间回调自己。(注意,choreographer的回调是在UI中运行的-thread)
3.运行时: AnimationHandler->doAnimationFrame(mChoreographer.getFrameTime())
->anim.startAnimation(this)首先,animatort还没有真正的start 在start的同时,调用其start回调 ->已经在run的animator: anim.doAnimationFrame(frameTime), 这个函数是一系列的调整值,并调用相应的update回调。 -> if (! -> if (!mAnimations.isEmpty() || !mDelayedAnims.isEmpty()) {
scheduleAnimation();
} 如果有没有run完成的animator,那么继续把你的schedule给choreographer,等待下一次回调.
顺便看看Aniamtion的原来理论上,Animation不同于Animator,Animation通过在View的draw()中调用drawanimation()来实现效果.
注意一点:
2.x系列不支持Animator,因为它是后来引入的,但可以通过nineoldandroids库模拟实现。原理很差,只有2.x没有choreographer,所以调度会有差异.