当前位置: 首页 > 图灵资讯 > 技术篇> android ValueAnimator 源码track

android ValueAnimator 源码track

来源:图灵教育
时间:2023-05-24 09:21:32

注意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,所以调度会有差异.