属性动画原理-动画的启动

2022-06-09  本文已影响0人  毛先森

前言

Android 的动画分为帧动画,补间动画,属性动画,这些概念都是老生常谈了,那它们底层的逻辑到底是怎样的呢,我们这次透过表面看本质,到底内部是怎么处理动画的

简单的动画大概有移动/缩放/旋转等几种类型,属性动画中是怎样处理这些的呢?

我们化繁为简,从动画的启动开始入手,了解 Choreographer 是怎样转换 VSYNC 信号的

提出问题

  1. VSYNC 是什么 ?
  2. Choreographer 在动画 start() 中,实现了什么功能

源码分析

从 ObjectAnimator.ofInt(mButton, "width", 500).setDuration (5000).start() 入手

    public static ObjectAnimator ofInt(Object target, String propertyName, int... values) {
        // 这里只是创建 ObjectAnimator 对象
        ObjectAnimator anim = new ObjectAnimator(target, propertyName);
        anim.setIntValues(values);
        return anim;
    }
    
    private ObjectAnimator(Object target, String propertyName) {
        //设置动画的目前 View
        setTarget(target);
        setPropertyName(propertyName);
    }
    
    public void setTarget(@Nullable Object target) {
        final Object oldTarget = getTarget();
        if (oldTarget != target) {
            // 取消之前的
            if (isStarted()) {
                cancel();
            }
            // 重新设置 targetView ,这里使用了弱引用
            mTarget = target == null ? null : new WeakReference<Object>(target);
            mInitialized = false;
        }
    }
    
    public void setPropertyName(@NonNull String propertyName) {
        if (mValues != null) {
           // 移除掉HashMap的旧值,至于为什么要这样,等会再看看
            PropertyValuesHolder valuesHolder = mValues[0];
            String oldName = valuesHolder.getPropertyName();
            valuesHolder.setPropertyName(propertyName);
            mValuesMap.remove(oldName);
           //当前的属性 put 到表中 mValuesMap.put(propertyName, valuesHolder);
        }
        mPropertyName = propertyName;
        mInitialized = false;
    }
    public void start() {
        // AnimationHandler 需要了解下功能
        // 这里将之前取消掉,然后调用父类的 start()
        AnimationHandler.getInstance().autoCancelBasedOn(this);
        super.start();
    }
    
    /**
    ObjectAnimator 的父类 VauleAnimator
    **/
    private void start(boolean playBackwards) {
        // 一般不会,因为在主线程执行
        if (Looper.myLooper() == null) {
            throw new AndroidRuntimeException("Animators may only be run on Looper threads");
        }
        // 是否重复执行
        mReversing = playBackwards;
        mSelfPulse = !mSuppressSelfPulseRequested;
        // AnimationHandler.addAnimationFrameCallback()
        addAnimationCallback(0);

        if (mStartDelay == 0 || mSeekFraction >= 0 || mReversing) {
           // 触发 onAnimationStart() 
            startAnimation();
            if (mSeekFraction == -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);
            } else {
                setCurrentFraction(mSeekFraction);
            }
        }
        
    /**
    AnimationHandler.class
    **/
    public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {
        // 根据是否有延时执行,来放到不同的动画任务队列中
        // 若队列为空,则直接 post
        if (mAnimationCallbacks.size() == 0) {
            // 这里实际是调用 Choreographer.scheduleFrameLocked(),发送一条异步消息
            getProvider().postFrameCallback(mFrameCallback);
        }
        // 队列不为空,就往队尾插入
        if (!mAnimationCallbacks.contains(callback)) {
            mAnimationCallbacks.add(callback);
        }
        // 延时执行,则放到另外的队列中
        if (delay > 0) {
            mDelayedCallbackStartTime.put(callback, (SystemClock.uptimeMillis() + delay));
        }
    }    
    

有点绕了,先整理下思路,在调用开始后,AnimationHandler 会创建一个 Callback,放到队列中,或者直接发送 Msg,这里的作用是什么呢?

实际上最终是向 native 注册 VSYNC 信号的监听,通过 DisplayEventReceiver.scheduleVsync() 注册的,当 VSYNC 信号到来时,会触发 DisplayEventReceiver.dispatchVsync,进而触发 FrameDisplayEventReceiver.onVsync()

    // Called from native code.
    private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame) {
        onVsync(timestampNanos, physicalDisplayId, frame);
    }
    
    // 这里将 native 层的 VSYNC ,分发到 Java 层中
    public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {
            if (timestampNanos > now) {
                timestampNanos = now;
            }

            if (mHavePendingVsync) {
                Log.w(TAG, "Already have a pending vsync event.  There should only be "
                        + "one at a time.");
            } else {
                mHavePendingVsync = true;
            }

            mTimestampNanos = timestampNanos;
            mFrame = frame;
            // 发送异步消息
            Message msg = Message.obtain(mHandler, this);
            msg.setAsynchronous(true);
            mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
        }

VSYNC 是什么 ?

VSYNC 是垂直同步的意思,在 Android 应用层意味着屏幕刷新,具体的 VSYNC 机制参考 Vsync 信号机制和 UI 刷新流程

Choreographer 在动画 start() 中,实现了什么功能

注册监听 VSYNC 信号,并且在信号到来后,在 Java 层发送通知

Choreographer 原理

這個单独开一篇文章来讲

相关链接

上一篇下一篇

猜你喜欢

热点阅读