之前有点忙,有一阵没写了;好,今天开始属性动画的第四部分,前面三节把属性动画的初始化的过程基本分析了一遍,今天就结合之前写的内容,将属性动画是如何进行"start"的,完整的分析一遍。
  如果没有看过之前的文章,建议看下之前的分析过程,以便可以更好的理解接下来的内容:
[Android属性动画源码分析(一)]
[Android属性动画源码分析(二)]
[Android属性动画源码分析(三)]

        animator.addListener(listener);//动画监听器        animator.setDuration(3000);//每次动画运行时间        animator.setInterpolator(new LinearInterpolator());//插值器        animator.setRepeatCount(ValueAnimator.INFINITE);//重复次数        animator.start();//动画开始

  前面四行的源码很简单,就不讲了,从start开始讲起,首先进入start的源码:

//ObjectAnimator.java    @Override    public void start() {        AnimationHandler.getInstance().autoCancelBasedOn(this);        ...        super.start();    }

  进入这个AnimationHandler看一下:
  官方文档对它的解释是:

//AnimatorHandler.java/** * This custom, static handler handles the timing pulse that is shared by all active * ValueAnimators. This approach ensures that the setting of animation values will happen on the * same thread that animations start on, and that all animations will share the same times for * calculating their values, which makes synchronizing animations possible. * * The handler uses the Choreographer by default for doing periodic callbacks. A custom * AnimationFrameCallbackProvider can be set on the handler to provide timing pulse that * may be independent of UI frame update. This could be useful in testing. * * @hide */

  该handler主要是用于处理所有活动的属性动画共享的“时间脉冲”,这个时间脉冲是通常所理解的属性动画的从开始到结束每个时间段的“值”,它还保证了一个动画的完整播放都是发生在同一个线程,同时,它还使用了一个关键的类“Choreographer(编舞者)”来处理周期的回调;另一个类AnimationFrameCallBackProvider用来给handler提供用于UI更新的时间脉冲。(这几个类先记下,后面再表)

//AnimationHandler.javapublic final static ThreadLocal sAnimatorHandler = new ThreadLocal<>();//这个handler的实例对象是一个ThreadLocal对象public static AnimationHandler getInstance() {        if (sAnimatorHandler.get() == null) {//为每一个线程创建一个handler对象,这样做是用于确保一个动画的开始和结束都在同一个线程            sAnimatorHandler.set(new AnimationHandler());        }        return sAnimatorHandler.get();    }
//AnimationHandler.javavoid autoCancelBasedOn(ObjectAnimator objectAnimator) {        for (int i = mAnimationCallbacks.size() - 1; i >= 0; i--) {//当前回调还未进行添加,//所以这个for循环不会走,如果走的话,代表停止之前正在播放的动画,具体内容大家可以自行研究            AnimationFrameCallback cb = mAnimationCallbacks.get(i);            if (cb == null) {                continue;            }            if (objectAnimator.shouldAutoCancel(cb)) {                ((Animator) mAnimationCallbacks.get(i)).cancel();            }        }    }

  在判断完是否应该取消之前播放的动画之后,我们就来到了super.start()方法:

//ValuesAnimator.javapublic void start() {        start(false);//这里传入了false,代表正序播放动画    }
//ValuesAnimator.java...        mReversing = playBackwards;        // Special case: reversing from seek-to-0 should act as if not seeked at all.        if (playBackwards && mSeekFraction != -1 && mSeekFraction != 0) {//第一个参数就是false,所以不会走这里            ...        }        mStarted = true;//设置当前状态为start状态        mPaused = false;        mRunning = false;        // Resets mLastFrameTime when start() is called, so that if the animation was running,        // calling start() would put the animation in the        // started-but-not-yet-reached-the-first-frame phase.        mLastFrameTime = 0;        AnimationHandler animationHandler = AnimationHandler.getInstance();//之前提到过的handler,这里进行了初始化(源码在上面)        animationHandler.addAnimationFrameCallback(this, (long) (mStartDelay * sDurationScale));//(1)将当前动画对象添加到handler中,(第二个long的参数看初始化的值可知为0)        if (mStartDelay == 0 || mSeekFraction >= 0) {//由初始化代码得知mStartDelay == 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.            startAnimation();//(2)            if (mSeekFraction == -1) {//初始化值是-1,这里会走                // No seek, start at play time 0. Note that the reason we are not using fraction 0                // is because for animations with 0 duration, we want to be consistent with pre-N                // behavior: skip to the final value immediately.                setCurrentPlayTime(0);(3)            } else {                //本次不会走这里                setCurrentFraction(mSeekFraction);            }        }

  初始化的时候主要的工作:(1)将当前动画给了AnimatonHandler;(2)调用了startAnimaton()方法;(3)调用了setCurrentPlayTime()方法,并且传入了参数0,接下来按顺序分析下调用的这三个方法:
(1)

    //AnimatonHandler.java    /**     * Register to get a callback on the next frame after the delay.     */    public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {        if (mAnimationCallbacks.size() == 0) {//这里的size是0,所以这里会走            getProvider().postFrameCallback(mFrameCallback);        }        if (!mAnimationCallbacks.contains(callback)) {当前的list还无数据,所以这里会进入            mAnimationCallbacks.add(callback);//这里添加的是ObjectAnimator对象        }        if (delay > 0) {//当前delay是0            mDelayedCallbackStartTime.put(callback, (SystemClock.uptimeMillis() + delay));        }    }

  这个mFrameCallback是谁呢,我们看下它的定义:

private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {        @Override        public void doFrame(long frameTimeNanos) {            doAnimationFrame(getProvider().getFrameTime());            if (mAnimationCallbacks.size() > 0) {                getProvider().postFrameCallback(this);            }        }    };

  是一个Choreographer#FrameCallBack类型,这个mFrameCallback是怎么运作的呢,在分析完后面的内容在继续分析,这里先标记下。
  我们在看下getProvider().postFrameCallBack(mFrameCallback);的内容

    //AnimatonHandler.java    private AnimationFrameCallbackProvider getProvider() {        if (mProvider == null) {            mProvider = new MyFrameCallbackProvider();        }        return mProvider;    }
    //MyFrameCallbackProvider.java     /**     * 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() {            return mChoreographer.getFrameTime();        }        @Override        public long getFrameDelay() {            return Choreographer.getFrameDelay();        }        @Override        public void setFrameDelay(long delay) {            Choreographer.setFrameDelay(delay);        }    }

  由上面可以看到,MyFrameCallBackProvider相当于Choregrapher的代理类,所以接下来这块的分析我们直接进入Choregrapher类去查看,关于这个类的相关机制,鉴于篇幅有限,我们就踩在巨人的肩膀上,可以参考这篇文章:Android消息机制Looper与VSync的传播 ,我们只看相关动画的部分:

//Choreographer.javapublic void postFrameCallback(FrameCallback callback) {        postFrameCallbackDelayed(callback, 0);    }

  上边的方法传入了回调对象,延迟时间为0,进入postFrameCallbackDelayed:

//Choreographer.javapublic void postFrameCallbackDelayed(FrameCallback callback, long delayMillis) {        ...\\省略一些判断过程        }        postCallbackDelayedInternal(CALLBACK_ANIMATION,                callback, FRAME_CALLBACK_TOKEN, delayMillis);    }

  在上边的方法里,最后会调用postCallBackDelayedInternal方法进行最终处理,callBacktype是CALLBACK_ANIMATION,action就是回调对象,token是FRAME_CALLBACK_TOKEN,delayMillis是0:

//Choreographer.javaprivate void postCallbackDelayedInternal(int callbackType,            Object action, Object token, long delayMillis) {        ...        synchronized (mLock) {            final long now = SystemClock.uptimeMillis();            final long dueTime = now + delayMillis;//这里delayMillis = 0           mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);//回调存入了CALLBACK_ANIMATION队列中            if (dueTime <= now) {                scheduleFrameLocked(now);//由于dueTime == now,这里将被执行,进入垂直同步过程            } else {                ...            }        }    }

  由上面的代码可知,我们将回调对象最终存入了mCallBackQueues[CALLBACK_ANIMATION]中(这是一个存放各个类型的数组,每个数组元素是一个CallbackQueue用于存放不同类型的回调:

//Choreographer.javaprivate final CallbackQueue[] mCallbackQueues;

  由之前的代码分析可知,我们播放的动画是CALLBACK_ANIMATION类型,addCallbackLocked方法我们将在下面进行分析)然后编舞者将会执行一次垂直同步(具体参见相关文章,这里不展开),一系列的过程后,会调用Choregrapher中 CallBackRecord对象的run方法,最终进入doFrame方法:

//Choreographer.javavoid doFrame(long frameTimeNanos, int frame) {       ...        try {            ...            AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS);            mFrameInfo.markInputHandlingStart();            doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos);            mFrameInfo.markAnimationsStart();            doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos);            mFrameInfo.markPerformTraversalsStart();            doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos);            doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos);        } finally {            AnimationUtils.unlockAnimationClock();            Trace.traceEnd(Trace.TRACE_TAG_VIEW);        }        ...    }

(补充,根据VSYNC机制,每次接收到垂直同步信号时,都会经过一系列的分发执行到doFrame方法)
  这里只列出一些关键部分,通过这部分可以看出,doFrame方法调用了doCallBacks方法用来处理不同类型的回调,由于不涉及其他三种类型,只看传入的类型是Choreographer.CALLBACK_ANIMATION,doCallBacks是如何处理的

//Choreographer.javavoid doCallbacks(int callbackType, long frameTimeNanos) {        CallbackRecord callbacks;        synchronized (mLock) {            final long now = System.nanoTime();            callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked(                    now / TimeUtils.NANOS_PER_MS);//取出回调方法            if (callbacks == null) {                return;            }            mCallbacksRunning = true;           ...        try {            ...            for (CallbackRecord c = callbacks; c != null; c = c.next) {                if (DEBUG_FRAMES) {                    Log.d(TAG, "RunCallback: type=" + callbackType                            + ", action=" + c.action + ", token=" + c.token                            + ", latencyMillis=" + (SystemClock.uptimeMillis() - c.dueTime));                }                c.run(frameTimeNanos);//执行回调            }        } finally {            synchronized (mLock) {                mCallbacksRunning = false;                do {//这里recycle了相关类型的回调                    final CallbackRecord next = callbacks.next;                    recycleCallbackLocked(callbacks);                    callbacks = next;                } while (callbacks != null);            }            ...        }    }

  上边的代码主要做了两件事,取出相应类型的“回调队列”(其实是个链表),执行队列中每个回调的run方法;
先说第一件事,前面说过要分析一下addCallbackLocked方法,正好在这里和extractDueCallbacksLocked一起分析:
这两个方法都是CallbackQueue里的方法:

mCallbackQueues[CALLBACK_ANIMATION].addCallbackLocked(now, callback, token);//这里只是列出之前的调用及传参
//Choreographer#CallbackQueuepublic void addCallbackLocked(long dueTime, Object action, Object token) {            CallbackRecord callback = obtainCallbackLocked(dueTime, action, token);            CallbackRecord entry = mHead;            if (entry == null) {                mHead = callback;                return;            }            if (dueTime < entry.dueTime) {                callback.next = entry;                mHead = callback;                return;            }            while (entry.next != null) {                if (dueTime < entry.next.dueTime) {                    callback.next = entry.next;                    break;                }                entry = entry.next;            }            entry.next = callback;        }

  上边的代码就是按照dueTime的顺序将通过obtainCallbackLocked生成的CallbackRecord放到链表的合适位置进行存储(CallbackRecord就是一个链表的存储结构);

//Choreographer#CallbackQueueprivate CallbackRecord obtainCallbackLocked(long dueTime, Object action, Object token) {        CallbackRecord callback = mCallbackPool;        if (callback == null) {            callback = new CallbackRecord();        } else {            mCallbackPool = callback.next;            callback.next = null;        }        callback.dueTime = dueTime;        callback.action = action;//注意,动画本身的callback放到了这里        callback.token = token;        return callback;    }

  上边就是生成一个CallbackRecord的过程,动画播放的callback放到了CallbackRecord的action中。
  接下来看看取出的时候是怎么做的:

//Choreographer#CallbackQueuepublic CallbackRecord extractDueCallbacksLocked(long now) {            CallbackRecord callbacks = mHead;            if (callbacks == null || callbacks.dueTime > now) {                return null;            }            CallbackRecord last = callbacks;            CallbackRecord next = last.next;            while (next != null) {                if (next.dueTime > now) {                    last.next = null;                    break;                }                last = next;                next = next.next;            }            mHead = next;            return callbacks;        }

  总体来说,就是将链表头部返回,并且移除了当前时间之后的元素。
  上边一系列方法看完以后就拿到了callbacks,通过for循环对这个链表遍历执行每个CallbackRecord的run方法:

//Choreographer#CallbackRecordpublic void run(long frameTimeNanos) {            if (token == FRAME_CALLBACK_TOKEN) {                ((FrameCallback)action).doFrame(frameTimeNanos);            } else {                ((Runnable)action).run();            }        }

  由前边的代码可知,这里的token就是FRAME_CALLBACK_TOKEN,所以将执行doFrame方法,还记得这个action是谁吗,没错,就是前面说要在后面分析的mFrameCallback!
  到这里,getProvider().postFrameCallback(mFrameCallback);的过程就分析完了,总结起来就是,我们的这次animation创建了一个AnimationFrameCallbackProvider,在AnimationHander里添加了一个FrameCallback类型的回调(如果之前没有的话),这个FrameCallback将通过provider发送给Choregrapher(编舞者),被编舞者封装为CallbackRecord类型的内容,在一次垂直同步信号分发时,这个callbackRecord将会被调用,并最终调用FrameCallback的doFrame方法,同时,本次animation也添加到了AnimationHander的回调列表中,接下来的一篇文章我们将开始分析mFrameCallback的doFrame方法。

更多相关文章

  1. Android(安卓)虚拟机Dalvik、Android各种java包功能、Android相
  2. android通过webservice连接SQL数据库(一)服务器端
  3. Android(安卓)mediaplayer 播放本地音乐文件
  4. Android创建和使用数据库
  5. Android智能指针SP WP使用方法介绍
  6. FirefoxOS 系统进程初步分析 底层系统继承自 android
  7. Android(安卓)-- AppWidget 高级篇
  8. 浅谈Java中Collections.sort对List排序的两种方法
  9. Python list sort方法的具体使用

随机推荐

  1. android获取屏幕长宽,status bar高度,设置
  2. android 图片倒影处理
  3. Android(安卓)返回键
  4. android Vibrator 使用
  5. Android通过Socket(TCP/IP)与PC通讯
  6. Android(安卓)Notification 例子
  7. Android(安卓)ListView
  8. Android图形解锁的绘制
  9. Android(安卓)Studio 疑难问题解决方法总
  10. 执行 Robotium 测试脚本抛出 NullPointer