Android进阶

Android动画原理分析

2019-01-28  本文已影响0人  htkeepmoving

前言

《Android开发艺术探索》第三章弹性滑动中有这么一段话:”如何实现弹性滑呢?实现方法有很多,但它们都有一个共同的思想:将一次大的滑动分成若干次小的滑动并在一个时间段内完成,其实现方式有很多种,Scroller、Handler.postDelayed()、Thread.sleep()等“;我们将这段话套用到Android动画上:将一次大的属性变化(如透明度从1到0)分成很多次小的属性变化并在一个时间段内完成(16.7ms内完成),从而实现透明度渐变动画效果,其实现方式是通过Choreographer来完成的;Choreographer是动画原理的一个核心,搞明白动画工作原理之前我们需要先了解Choreographer工作过程;
Choreographer工作过程简单描述如下:在Choreographer对象中有四条链表,分别保存着待处理的输入事件,待处理的动画事件,待处理的View绘制事件、待处理的post到Choreographer中事件,Android系统每隔16.7ms发出VSYNC信号,Choreographer.FrameDisplayEventReceiver收到信号后调用onVsync方法,最终会调用Choreographer.doFrame()方法,在doFrame方法中处理输入事件、动画事件、View绘制、post到Choreographer中的FrameCallback;我们可以使用Choreographer#postFrameCallback设置callback与Choreographer交互,设置的FrameCallCack(doFrame方法)会在下一个frame被渲染时触发(即下一个VSYNC到来时执行);Choreographer更多内容请参考Android Choreographer 源码分析
Android动画原理简单描述:将View的一次大的属性变化拆分为多次小的属性变化,在每次VSYNC信号到来时,根据当前时间和插值器来计算当前View属性的值,然后给View设置该属性值,直到动画执行完毕。其中Choreographer将动画拆分成一次次小的属性变化,Choreographer是动画的指挥者。理想情况下,属性刷新次数(动画拆分为多次小的属性变化) = 动画执行时间/16.7ms 。
我们通过属性动画工作流程来介绍Android动画工作原理,其它动画的工作过程也应该类似,感兴趣的可以阅读源码来了解其原理;本文主要围绕以下四个问题来讲解动画原理:
问题一:动画如何完成一次属性变化刷新?
问题二:动画如何被拆分成一次次小的属性变化?
问题三:动画如何跳出属性刷新的流程,从而结束动画?
问题四:动画监听器何时被调用?

属性动画简单例子

在讲解原理前,我们先来看一下属性的动画的一个简单例子:

public class AnimationActivity extends AppCompatActivity implements View.OnClickListener{

    private Button mButton;
    public static final String TAG = "AnimationTest";
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.animation_layout);
        mButton = (Button) findViewById(R.id.button);
        mButton.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        //ObjectAnimator extends ValueAnimator;
        ValueAnimator alphaAnimation = ObjectAnimator.ofFloat(mButton,"alpha",1.0f,0f);
        alphaAnimation.setDuration(100);
        alphaAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                //属性刷新
                Log.d(TAG, "onAnimationUpdate: " + animation.getAnimatedValue());
            }
        });
        alphaAnimation.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animation) {
                //动画开始
                Log.d(TAG, "onAnimationStart: ");
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                //动画结束
                Log.d(TAG, "onAnimationEnd: ");
            }

            @Override
            public void onAnimationCancel(Animator animation) {
                Log.d(TAG, "onAnimationCancel: ");
            }

            @Override
            public void onAnimationRepeat(Animator animation) {
                Log.d(TAG, "onAnimationRepeat: ");
            }
        });
        alphaAnimation.start();
    }
}

运行之后,我们点击Button,对Button开始做透明度动画,相关日志打印如下:

01-28 10:15:39.587 3867-3867/com.android.animation D/AnimationTest: onAnimationStart: 
01-28 10:15:39.587 3867-3867/com.android.animation D/AnimationTest: onAnimationUpdate: 1.0
01-28 10:15:39.597 3867-3867/com.android.animation D/AnimationTest: onAnimationUpdate: 1.0
01-28 10:15:39.607 3867-3867/com.android.animation D/AnimationTest: onAnimationUpdate: 0.93037105
01-28 10:15:39.627 3867-3867/com.android.animation D/AnimationTest: onAnimationUpdate: 0.75452065
01-28 10:15:39.647 3867-3867/com.android.animation D/AnimationTest: onAnimationUpdate: 0.5
01-28 10:15:39.657 3867-3867/com.android.animation D/AnimationTest: onAnimationUpdate: 0.24547923
01-28 10:15:39.677 3867-3867/com.android.animation D/AnimationTest: onAnimationUpdate: 0.061846733
01-28 10:15:39.697 3867-3867/com.android.animation D/AnimationTest: onAnimationUpdate: 0.0
01-28 10:15:39.697 3867-3867/com.android.animation D/AnimationTest: onAnimationEnd: 

动画原理分析

问题一:动画如何完成一次属性变化刷新?
下面我们从动画的入口alphaAnimation.start()开始分析动画原理,先来看动画如何完成一次属性刷新:

    private void start(boolean playBackwards) {
        //设置相关标志位
        mStarted = true;
        mPaused = false;
        mRunning = false;
        mAnimationEndRequested = false;
        //最关键的方法,动画循环执行在该方法中实现
        AnimationHandler animationHandler = AnimationHandler.getInstance();
        animationHandler.addAnimationFrameCallback(this, (long) (mStartDelay * sDurationScale));

        if (mStartDelay == 0 || mSeekFraction >= 0) {
            // If there's no start delay, init the animation and notify start listeners right away
            // to be consistent with the previous behavior. Otherwise, postpone this until the first
            // frame after the start delay.
            //初始化相关属性,onAnimationStart在该方法中被调用;
            startAnimation();
        }
    }

以上最核心的方法是AnimationHandler.addAnimationFrameCallback()。下面我们来分析该方法:

AnimationHandler
    //final AnimationFrameCallback callback就是ValueAnimation
    //public class ValueAnimator extends Animator implements AnimationHandler.AnimationFrameCallback
    public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {
        if (mAnimationCallbacks.size() == 0) {
            //动画开始执行时,mAnimationCallbacks.size()为0.会调用getProvider().postFrameCallback方法;
            getProvider().postFrameCallback(mFrameCallback);
        }
        if (!mAnimationCallbacks.contains(callback)) {
            //将ValueAnimator添加在mAnimationCallbacks列表;
            mAnimationCallbacks.add(callback);
        }
    }

下面我们来看getProvider().postFrameCallback(mFrameCallback);
AnimationHandler.getProvider()方法、MyFrameCallbackProvider、mFrameCallback代码如下:

private AnimationFrameCallbackProvider getProvider() {
        if (mProvider == null) {
            mProvider = new MyFrameCallbackProvider();
        }
        return mProvider;
    }
    
     /**
     * 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);
        }

        @Override
        public void postCommitCallback(Runnable runnable) {
            mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT, runnable, null);
        }

        @Override
        public long getFrameTime() {
            //获取上一次VSYNC信号时间;
            return mChoreographer.getFrameTime();
        }
    }
    
    private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
        @Override
        public void doFrame(long frameTimeNanos) {
            //Choreographer收到VSYNC都会调用该方法,正常情况下每16.7ms就会调用一次doFrame
            doAnimationFrame(getProvider().getFrameTime());
            if (mAnimationCallbacks.size() > 0) {
                //再将自己post到编舞者中,以便下次收到VSYNC时,继续调用doFrame来刷新动画;
                getProvider().postFrameCallback(this);
            }
        }
    };

在执行完getProvider().postFrameCallback(mFrameCallback)后,下一次收到VYSNC信号时,就会调用mFrameCallback.doFrame方法,doFrame方法主要做了两件重要的工作:
(1)调用doAnimationFrame刷新View的相关属性;
(2)将自己再次post到Choreographer中,这样在下一次VSYNC到来时继续执行doFrame,完成下一次属性刷新;
关键点:动画属性一次次刷新就是通过mFrameCallback来实现的;
下面我们来看doAnimationFrame(getProvider().getFrameTime())方法:

    private void doAnimationFrame(long frameTime) {
        for (int i = 0; i < size; i++) {
            //callback就是我们添加到mAnimationCallbacks列表中的ValueAnimator
            final AnimationFrameCallback callback = mAnimationCallbacks.get(i);
            if (isCallbackDue(callback, currentTime)) {
                //执行ValueAnimator.doAnimationFrame;
                callback.doAnimationFrame(frameTime);
                if (mCommitCallbacks.contains(callback)) {
                    getProvider().postCommitCallback(new Runnable() {
                        @Override
                        public void run() {
                            //每一次刷新刷新之后会调用,目前没有看到会执行该方法;
                            commitAnimationFrame(callback, getProvider().getFrameTime());
                        }
                    });
                }
            }
        }
        cleanUpList();
    }

AnimationFrameCallback callback = mAnimationCallbacks.get(i);该callback就是valueAnimator,即我们在调用AnimationHandler.addAnimationFrameCallback中将ValueAnimator添加在mAnimationCallbacks列表;
下面我们来看ValueAnimator.doAnimationFrame(frameTime)方法,在该方法最终会刷新View的属性;

 /**
     * Processes a frame of the animation, adjusting the start time if needed.
     *  处理每一帧中的刷新
     * @param frameTime The frame time.
     * @return true if the animation has ended.
     * @hide
     */
    public final void doAnimationFrame(long frameTime) {
        //在animateBasedOnTime刷新View属性,并判断动画是否执行完毕;
        boolean finished = animateBasedOnTime(currentTime);
        if (finished) {
            //如果动画结束执行endAnimation
            endAnimation();
        }
    }

下面我们继续来看animateBasedOnTime()方法:

    boolean animateBasedOnTime(long currentTime) {
        //done表示动画是否执行完毕;
        boolean done = false;
        //在ValueAnimator.startAnimation中将mRuning置为true;
        if (mRunning) {
            final long scaledDuration = getScaledDuration();
            final float fraction = scaledDuration > 0 ?
                    (float)(currentTime - mStartTime) / scaledDuration : 1f;
            final float lastFraction = mOverallFraction;
            final boolean newIteration = (int) fraction > (int) lastFraction;
            //根据当前时间currentTime来判断动画是否执行完毕;
            final boolean lastIterationFinished = (fraction >= mRepeatCount + 1) &&
                    (mRepeatCount != INFINITE);
            if (scaledDuration == 0) {
                ...
            } else if (newIteration && !lastIterationFinished) {
                ...
            } else if (lastIterationFinished) {
                //动画执行完毕;
                done = true;
            }
            //计算当前View属性值,然后设置View相关属性;
            animateValue(currentIterationFraction);
        }
        return done;
    }

我们继续来看ObjectAnimator和ValueAnimator的animateValue方法,ObjectAnimator继承ValueAnimator;

ObjectAnimator:
    void animateValue(float fraction) {
        //getTarget()返回的就是ValueAnimator alphaAnimation = ObjectAnimator.ofFloat(mButton,"alpha",1.0f,0f)中的mButton,
        final Object target = getTarget();
        //调用ValueAnimator.animateValue,主要是计算当前动画的进度
        super.animateValue(fraction);
        int numValues = mValues.length;
        for (int i = 0; i < numValues; ++i) {
            //调用PropertyValuesHolder.setAnimatedValue设置mButton在该帧的透明度,即super.animateValue(fraction)计算出的当前透明度
            mValues[i].setAnimatedValue(target);
        }
    }
VauleAnimator:
    void animateValue(float fraction) {
        //根据插值器计算当前属性动画的进度
        fraction = mInterpolator.getInterpolation(fraction);
        mCurrentFraction = fraction;
        int numValues = mValues.length;
        for (int i = 0; i < numValues; ++i) {
            //将当前进度存放到PropertyValuesHolder.mAnimatedValue,
            //在AnimatorUpdateListener.onAnimationUpdate方法中可以通过ValueAnimator.getAnimatedValue获取动画进度
            mValues[i].calculateValue(fraction);
        }
        if (mUpdateListeners != null) {
            int numListeners = mUpdateListeners.size();
            for (int i = 0; i < numListeners; ++i) {
                //回调AnimatorUpdateListener.onAnimationUpdate方法
                mUpdateListeners.get(i).onAnimationUpdate(this);
            }
        }
    }

animateValue方法主要工作:根据插值器计算mButton在该帧时的透明度,然后调用PropertyValuesHolder.setAnimatedValue设置mButton的透明度,从而完成该帧透明度变化;每一帧更新一次透明度,最终完成整个透明度变化过程;
下面我们来看PropertyValuesHolder.setAnimatedValue(Object target)代码,其中target就是ValueAnimator alphaAnimation = ObjectAnimator.ofFloat(mButton,"alpha",1.0f,0f)中的mButton,该方法主要就是设置当前View的属性值,执行该方法最终会调用View.setAlpha()来设置mButton的透明度,在该帧中mButton的透明度就完成了更新。

/**
     * Internal function to set the value on the target object, using the setter set up
     * earlier on this PropertyValuesHolder object. This function is called by ObjectAnimator
     * to handle turning the value calculated by ValueAnimator into a value set on the object
     * according to the name of the property.
     * @param target The target object on which the value is set
     *  通过注释可以看出,该方法主要就是设置当前View的属性值,执行完该方法后,在该帧中mButton的透明度就完成了更新
     */
    void setAnimatedValue(Object target) {
        // 如果有属性,通过set方法来更新属性值
        if (mProperty != null) {
            //getAnimatedValue()就是返回VauleAnimator.animateValue中计算出的当前进度值
            mProperty.set(target, getAnimatedValue());
        }
        // 是否通过反射调用属性的setter方法来更新属性值
        if (mSetter != null) {
            try {
                //getAnimatedValue()就是返回VauleAnimator.animateValue中计算出的当前进度值    
                mTmpValueArray[0] = getAnimatedValue();
                mSetter.invoke(target, mTmpValueArray);
            }
        }
    }

以上流程完成了mButton透明度的一次更新,同时也解释了动画是如何完成一次属性变化的刷新。
问题二:动画如何被拆分成一次次小的属性变化?
该问题也可以简单理解如何循环更新mButton透明度呢?我们再来回顾AnimationHandler.addAnimationFrameCallback方法中:

public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {
        if (mAnimationCallbacks.size() == 0) {
            getProvider().postFrameCallback(mFrameCallback);
        }
    }
    
    private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {
        @Override
        public void doFrame(long frameTimeNanos) {
            //在doAnimationFrame更新mButton在当前帧中的透明度
            doAnimationFrame(getProvider().getFrameTime());
            //mAnimationCallbacks列表中存放正在执行的动画,动画执行完毕会将对应的AnimationFrameCallback remove掉
            if (mAnimationCallbacks.size() > 0) {
                //将mFrameCallback自身再次post到Choreographer在下一次VSYNC信号到来时,再次调用doFrame来更新mButton的透明度
                //如果动画一直没有执行完毕,会一直循环执行doFrame
                getProvider().postFrameCallback(this);
            }
        }
    };

在mFrameCallback中完成多次动画的拆分,并在每次收到VSYNC信号时,更新View的属性,连续不停的更新View属性,直到动画结束。
问题三:动画如何跳出属性刷新的流程,从而结束动画?
该问题也可以理解为动画是如何跳出mFrameCallback循环,在mFrameCallback中会判断mAnimationCallback列表中是否还有AnimationFrameCallback,如果mAnimationCallback列表中没有AnimationFrameCallback,那么透明度变化在下一次VSYNC到来时就不会再继续执行从而结束动画,那么我们找到mAnimationCallbacks列表啥时候将AnimationFrameCallback remove掉,就代表动画结束;主要流程如下:

ValueAnimator:
    public final void doAnimationFrame(long frameTime) {
        //animateBasedOnTime根据当前时间计算动画是否应该结束
        //如果动画应该结束,返回true;
        boolean finished = animateBasedOnTime(currentTime);
        if (finished) {
            //调用endAnimation结束动画
            endAnimation();
        }
    }
    
    private void endAnimation() {
        AnimationHandler handler = AnimationHandler.getInstance();
        //在AnimationHandler将ValueAnimator remove掉;
        handler.removeCallback(this);
        if ((mStarted || mRunning) && mListeners != null) {
            for (int i = 0; i < numListeners; ++i) {
                //调用AnimatorListener.onAnimationEnd方法通知应用
                tmpListeners.get(i).onAnimationEnd(this);
            }
        }
    }
AnimationHandler:
    public void removeCallback(AnimationFrameCallback callback) {
        int id = mAnimationCallbacks.indexOf(callback);
        if (id >= 0) {
            //将ValueAnimator remove调用,这样在下一此VSYNC到来时就不会继续执行动画
            mAnimationCallbacks.set(id, null);
        }
    }

问题四:动画监听器何时被调用?
即以下几个方法是在何时回调的?
AnimatorListener.onAnimationStart(); //在动画开始时调用
AnimatorUpdateListener.onAnimationUpdate();//在每一次属性刷新时调用
AnimatorListener.onAnimationEnd();//动画结束时调用
通过以上代码中注释我们可以找到以下调用关系:

ValueAnimator.start() > startAnimation() > notifyStartListeners > AnimatorListener.onAnimationStart()
ValueAnimator.doAnimationFrame() > animateBasedOnTime() > AnimatorListener.onAnimationUpdate();
ValueAnimator.doAnimationFrame() > endAnimation() > AnimatorListener.onAnimationEnd();

最后,我们再来回顾一下动画原理的简单描述:将View的一次大的属性变化拆分为多次小的属性变化,在每次VSYNC信号到来时,根据当前时间和插值器来计算当前View属性的值,然后设置给View设置该属性值,直到动画执行完毕,动画的拆分是由Choreographer来完成的。
以上就是动画执行的主要过程,希望对你理解Android动画有所帮助。

参考资料

《Android开发艺术探索》
《深入理解Android卷三》第四章Android动画原理简介
Android动画原理分析

上一篇下一篇

猜你喜欢

热点阅读