Android属性动画工作原理

来源:互联网 发布:银行 数据挖掘 编辑:程序博客网 时间:2024/05/01 07:42
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 这个帖子的分析,说得很透彻。


0 0
原创粉丝点击