美文网首页
属性动画源码分析-start方法

属性动画源码分析-start方法

作者: 凌烟醉卧 | 来源:发表于2020-02-23 17:17 被阅读0次

    属性动画的启动我们是通过如下来完成的:

      objectAnimator.start();//动画开始时从start方法开始的
    

    start()核心代码如下:

    AnimationHandler.getInstance().autoCancelBasedOn(this);
    super.start();
    

    start()方法主要做了两件事,一个是检测如果动画已经执行,则停止动画;另一方面调用了父类的start()方法。

    ObjectAnimator的父类是ValueAnimator,它的start()方法如下:

    start(false);
    

    我们接着看下ValueAnimator的这个方法,核心代码如下:

    //开启动画的线程要有Looper对象=》动画只能在主线程中运行
    if (Looper.myLooper() == null) {
       throw new AndroidRuntimeException("Animators may only be run on Looper threads");
    }
    addAnimationCallback(0);
    if (mStartDelay == 0 || mSeekFraction >= 0 || mReversing) {
       startAnimation(); //如果没有启动延迟,就立即启动动画   
       if (mSeekFraction == -1) {  
           setCurrentPlayTime(0);
       } else {
           setCurrentFraction(mSeekFraction);
       }
    }
    

    这里主要看addAnimationCallback(0);

        private void addAnimationCallback(long delay) {
            if (!mSelfPulse) {
                return;
            }
            //添加一个AnimationFrameCallback,ValueAnimator实现了AnimationFrameCallback接口
            getAnimationHandler().addAnimationFrameCallback(this, delay);
        }
    

    流程走到这里,需要看两个方法:

    • getAnimationHandler()
      看下getAnimationHandler()这个方法:
     public AnimationHandler getAnimationHandler() {
            return AnimationHandler.getInstance();//getInstance()这个方法
    }
    

    AnimationHandler不是Handler,而是AnimationHandler用ThreadLocal保证每个线程只有一个实例。做到线程中单例,“getInstance()”方法以及实现如下:

    public final static ThreadLocal<AnimationHandler> sAnimatorHandler = new ThreadLocal<>();
    private boolean mListDirty = false;
    
    public static AnimationHandler getInstance() {
       if (sAnimatorHandler.get() == null) {
          sAnimatorHandler.set(new AnimationHandler());
       }
       return sAnimatorHandler.get();
    }
    
    • addAnimationFrameCallback
      addAnimationFrameCallback的核心代码:
    public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {
      if (mAnimationCallbacks.size() == 0) {
         getProvider().postFrameCallback(mFrameCallback);
      }
    }
    

    AnimationFrameCallbackValueAnimator的父类,getProvider()返回的是MyFrameCallbackProvider,我们来看下postFrameCallback这个方法,通过不断的调用跳转,我们来到了ChoreographerpostCallbackDelayedInternal这个方法:

    private void postCallbackDelayedInternal(int callbackType,
        Object action, Object token, long delayMillis) {//这个action就是callback
    
       synchronized (mLock) {
          final long now = SystemClock.uptimeMillis();
          final long dueTime = now + delayMillis;
          mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);
          if (dueTime <= now) {
             //没有延迟,直接开始调度
             scheduleFrameLocked(now);
          } else {
             //有延迟,就通过handler延时发送一个message
             Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
             msg.arg1 = callbackType;
             msg.setAsynchronous(true);
             mHandler.sendMessageAtTime(msg, dueTime);
          }
        }
    }
    

    看下scheduleFrameLocked(now);这个方法:,核心代码如下:

    if (USE_VSYNC) {//为动画和绘图启动/禁用垂直同步信号
       //如果是在主线程就直接调度,否则就发送一个消息到主线程
       if (isRunningOnLooperThreadLocked()) {
           scheduleVsyncLocked();
       } else {
           Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC);
           msg.setAsynchronous(true);
           mHandler.sendMessageAtFrontOfQueue(msg);
       }      
    } 
    

    然后看下这个方法scheduleVsyncLocked,通过不断的跳转,我们可以看到在DisplayEventReceiver这个类中的scheduleVsync方法如下:

    public void scheduleVsync() {
       if (mReceiverPtr == 0) {
           Log.w(TAG, "Attempted to schedule a vertical sync pulse but the display event "
                        + "receiver has already been disposed.");
       } else {
           nativeScheduleVsync(mReceiverPtr);
       }
    }
    

    nativeScheduleVsync方法其实是一个native方法,这个方法其实是向native层注册成为观察者,用来接收屏幕刷新信号。

    private static native void nativeScheduleVsync(long receiverPtr);
    

    总结:start方法中检测如果动画已经执行,则停止动画,并且限定了动画只能在主线程中运行,动画如果在主线程中运行,那么将会判断有没有启动延迟,如果没有就立即执行动画,通过AnimationHandlerThreadLocal保证每个线程只有一个实例。做到线程中单例。执行动画后,会为动画和绘图启动垂直同步信号,通过向native曾注册成为观察者,用来接收屏幕刷新信号。

    相关文章

      网友评论

          本文标题:属性动画源码分析-start方法

          本文链接:https://www.haomeiwen.com/subject/fgwjqhtx.html