Android属性动画工作原理

Android提供了属性动画这个强大的框架来帮助开发者实现比较复杂的动画。

网上已经有很多帖子为大家介绍属性动画的使用,先给大家推荐一篇写的比较好的。http://

www.2cto.com/kf/201401/270169.html

本文不讲使用方法,是从源码的角度来分析属性动画框架的工作原理。
先引出几个关键的类:ObjectAnimator,ValueAnimator,PropertyValuesHolder和Choreographer。我们从ObjectAnimator的使用方式开始分析:
ObjectAnimator.ofInt(mButton, width, 500).setDuration(5000).start()。先看ofint方法,其实就是返回了一个确定了作用对象,作用属性和属性数值的objectanimator对象。执行这个动画是start开始的。ObjectAnimator直接调用的父类ValueAnimator的super.start()。
private 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;
        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();
}


最后调用了animationHandler.start();这里的animationHandler不是一个handler而是一个Runnable,重点来了,看这个AnimationHandler中有一个mChoreographer,这个是控制器哦。Start之后调用的是
private void scheduleAnimation() {
            if (!mAnimationScheduled) {
                mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, this, null);
                mAnimationScheduled = true;
            }
}

 
这个方法什么作用呢?它是向choreographer中post了一个回调,并将自己传过去了,好了,剩下的工作就在choreographer中进行,先来看看这个类的结构,重要的东西有一个threadlocal的instance,看来是一个单例模式,并且应该是能支持到多线程操作的。(PS后面会有针对当前是否为UI thread的判断处理)还有FrameHandler(有handler肯定就有looper啊,没错这里也是有的,这个handler主要就是用来处理一些msg的,以及控制执行此帧的时间间隔)和一个mCallbackQueues(很明显是一个callback的队列)。
继续来看看postcallback以后的工作,会调到下面这个方法。
private void postCallbackDelayedInternal(int callbackType,
            Object action, Object token, long delayMillis) {
        if (DEBUG) {
            Log.d(TAG, "PostCallback: type="   callbackType
                      ", action="   action   ", token="   token
                      ", delayMillis="   delayMillis);
        }

        synchronized (mLock) {
            final long now = SystemClock.uptimeMillis();
            final long dueTime = now   delayMillis;
            mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
            //这里就可以看到了,当valueanimator设置回调时都会保存在这个队列当中,等待需要处理时再从里面取出来
            if (dueTime <= now) {
                scheduleFrameLocked(now);
            } else {
                Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
                msg.arg1 = callbackType;
                msg.setAsynchronous(true);
                mHandler.sendMessageAtTime(msg, dueTime);
                //执行do frame是有间隔的哟,有一个默认的间隔是常量DEFAULT_FRAME_DELAY = 10
            }
        }
    }

 
前面分支判断之后最终都会调用到下面这个方法,而在这个方法里面就一个难点,scheduleVsyncLocked(),这个东西是android 4.1之后加入的关键技术vsync timing和triple buffer,用于提高UI的流畅度。这个就是显示技术上的优化,不影响动画过程的分析。我们继续看普通的情况,特就是向handler中send了一个msg_do_frame的消息。
 private void scheduleFrameLocked(long now) {
        if (!mFrameScheduled) {
            mFrameScheduled = true;
            if (USE_VSYNC) {
                if (DEBUG) {
                    Log.d(TAG, "Scheduling next frame on vsync.");
                }

                // If running on the Looper thread, then schedule the vsync immediately,
                // otherwise post a message to schedule the vsync from the UI thread
                // as soon as possible.
                if (isRunningOnLooperThreadLocked()) {
                    scheduleVsyncLocked();
                } else {
                    Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
                    msg.setAsynchronous(true);
                    mHandler.sendMessageAtFrontOfQueue(msg);
                }
            } else {
                final long nextFrameTime = Math.max(
                        mLastFrameTimeNanos / NANOS_PER_MS   sFrameDelay, now);
                if (DEBUG) {
                    Log.d(TAG, "Scheduling next frame in "   (nextFrameTime - now)   " ms.");
                }
                Message msg = mHandler.obtainMessage(MSG_DO_FRAME);
                msg.setAsynchronous(true);
                mHandler.sendMessageAtTime(msg, nextFrameTime);
            }
        }
    }

    private final class FrameHandler extends Handler {
        public FrameHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_DO_FRAME:
                    doFrame(System.nanoTime(), 0);
                    break;
                case MSG_DO_SCHEDULE_VSYNC:
                    doScheduleVsync();
                    break;
                case MSG_DO_SCHEDULE_CALLBACK:
                    doScheduleCallback(msg.arg1);
                    break;
            }
        }
    }

    void doFrame(long frameTimeNanos, int frame) {
doCallbacks(Choreographer.CALLBACK_ANIMATION, 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 = SystemClock.uptimeMillis();
            callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(now);
            if (callbacks == null) {
                return;
            }
            mCallbacksRunning = true;
        }
        try {
            for (CallbackRecord c = callbacks; c != null; c = c.next) {
                if (DEBUG) {
                    Log.d(TAG, "RunCallback: type="   callbackType
                              ", action="   c.action   ", token="   c.token
                              ", latencyMillis="   (SystemClock.uptimeMillis() - c.dueTime));
                }
                c.run(frameTimeNanos);
//执行到这里终于转到正题上来了,执行了前面animationHandler的run方法。
            }
        } finally {
            synchronized (mLock) {
                mCallbacksRunning = false;
                do {
                    final CallbackRecord next = callbacks.next;
                    recycleCallbackLocked(callbacks);
                    callbacks = next;
                } while (callbacks != null);
            }
        }
    }



现在回过头去看看animationHandler的run中做了一些什么事情,
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);
                    }
                }
            }
            // Next, process animations currently sitting on the delayed queue, adding
            // them to the active animations if they are ready
            int numDelayedAnims = mDelayedAnims.size();
            for (int i = 0; i < numDelayedAnims;   i) {
                ValueAnimator anim = mDelayedAnims.get(i);
                if (anim.delayedAnimationFrame(frameTime)) {
                    mReadyAnims.add(anim);
                }
            }
            int numReadyAnims = mReadyAnims.size();
            if (numReadyAnims > 0) {
                for (int i = 0; i < numReadyAnims;   i) {
                    ValueAnimator anim = mReadyAnims.get(i);
                    anim.startAnimation(this);
                    anim.mRunning = true;
                    mDelayedAnims.remove(anim);
                }
                mReadyAnims.clear();
            }

            // Now process all active animations. The return value from animationFrame()
            // tells the handler whether it should now be ended
            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);
                }
            }
            mTmpAnimations.clear();
            if (mEndingAnims.size() > 0) {
                for (int i = 0; i < mEndingAnims.size();   i) {
                    mEndingAnims.get(i).endAnimation(this);
                }
                mEndingAnims.clear();
            }

            // If there are still active or delayed animations, schedule a future call to
            // onAnimate to process the next frame of the animations.
            if (!mAnimations.isEmpty() || !mDelayedAnims.isEmpty()) {
                scheduleAnimation();
            }
        }


这一段代码很多,前面都是一些判断,有延时的就绪的结束的等等,但是重点是anim.doAnimationFrame(frameTime)和最后判断启动下一帧的过程。下一帧就是重复之前分析的过程。下面来看看doAnimationFrame这是执行设置属性变化的地方。doAnimationFrame最终会调用到animateValue,这里面的堆栈调用一目了然就不详细说了,这个方法在valueanimator中有一个,但是在子类objectanimator中重写了这个方法并同时执行了super的方法。放在一起看比较清楚。
 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);
            }
        }
    }

    void animateValue(float fraction) {
//执行的就是上述操作
        super.animateValue(fraction);
        int numValues = mValues.length;
        for (int i = 0; i < numValues;   i) {
            mValues[i].setAnimatedValue(mTarget);
        }
    }


mValues这个是父类中的成员变量,它就是之前设置的属性值的容器PropertyValuesHolder,mValues[i].calculateValue(fraction);这个方法中是根据最终属性值和进度计算当前属性值的过程(里面还会涉及到一个Interpolator插值器,这个东西一控制你当前动画执行的效果),mValues[i].setAnimatedValue(mTarget);这个显然就是使得属性值在target对象上生效的过程咯。后面的就不详细说了,其实就是使用放射机制对目标对象指向set属性和get属性(所以啊,这里对目标对象执行对应属性的变化必须确保改变的属性有set和get方法,并且与你设想的效果要是一致的,具体的使用可以看看文章开头推荐的那个帖子 )。

整个属性动画的执行过程就是如此,没有什么难的,但是给大家引出了一个难点,就是vsync timing和triple buffer的显示机制,这个可以参考 http://blog.csdn.net/innost/article/details/8272867 这个帖子的分析,说得很透彻。


你可能感兴趣的:(Android属性动画工作原理)