屬性動(dòng)畫(huà)原理-動(dòng)畫(huà)的啟動(dòng)

前言

Android 的動(dòng)畫(huà)分為幀動(dòng)畫(huà),補(bǔ)間動(dòng)畫(huà),屬性動(dòng)畫(huà),這些概念都是老生常談了,那它們底層的邏輯到底是怎樣的呢,我們這次透過(guò)表面看本質(zhì),到底內(nèi)部是怎么處理動(dòng)畫(huà)的

簡(jiǎn)單的動(dòng)畫(huà)大概有移動(dòng)/縮放/旋轉(zhuǎn)等幾種類(lèi)型,屬性動(dòng)畫(huà)中是怎樣處理這些的呢?

我們化繁為簡(jiǎn),從動(dòng)畫(huà)的啟動(dòng)開(kāi)始入手,了解 Choreographer 是怎樣轉(zhuǎn)換 VSYNC 信號(hào)的

提出問(wèn)題

  1. VSYNC 是什么 ?
  2. Choreographer 在動(dòng)畫(huà) start() 中,實(shí)現(xiàn)了什么功能

源碼分析

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

  • ofInt() 做了什么
    public static ObjectAnimator ofInt(Object target, String propertyName, int... values) {
        // 這里只是創(chuàng)建 ObjectAnimator 對(duì)象
        ObjectAnimator anim = new ObjectAnimator(target, propertyName);
        anim.setIntValues(values);
        return anim;
    }
    
    private ObjectAnimator(Object target, String propertyName) {
        //設(shè)置動(dòng)畫(huà)的目前 View
        setTarget(target);
        setPropertyName(propertyName);
    }
    
    public void setTarget(@Nullable Object target) {
        final Object oldTarget = getTarget();
        if (oldTarget != target) {
            // 取消之前的
            if (isStarted()) {
                cancel();
            }
            // 重新設(shè)置 targetView ,這里使用了弱引用
            mTarget = target == null ? null : new WeakReference<Object>(target);
            mInitialized = false;
        }
    }
    
    public void setPropertyName(@NonNull String propertyName) {
        if (mValues != null) {
           // 移除掉HashMap的舊值,至于為什么要這樣,等會(huì)再看看
            PropertyValuesHolder valuesHolder = mValues[0];
            String oldName = valuesHolder.getPropertyName();
            valuesHolder.setPropertyName(propertyName);
            mValuesMap.remove(oldName);
           //當(dāng)前的屬性 put 到表中 mValuesMap.put(propertyName, valuesHolder);
        }
        mPropertyName = propertyName;
        mInitialized = false;
    }
  • start() 做了什么
    public void start() {
        // AnimationHandler 需要了解下功能
        // 這里將之前取消掉,然后調(diào)用父類(lèi)的 start()
        AnimationHandler.getInstance().autoCancelBasedOn(this);
        super.start();
    }
    
    /**
    ObjectAnimator 的父類(lèi) VauleAnimator
    **/
    private void start(boolean playBackwards) {
        // 一般不會(huì),因?yàn)樵谥骶€(xiàn)程執(zhí)行
        if (Looper.myLooper() == null) {
            throw new AndroidRuntimeException("Animators may only be run on Looper threads");
        }
        // 是否重復(fù)執(zhí)行
        mReversing = playBackwards;
        mSelfPulse = !mSuppressSelfPulseRequested;
        // AnimationHandler.addAnimationFrameCallback()
        addAnimationCallback(0);

        if (mStartDelay == 0 || mSeekFraction >= 0 || mReversing) {
           // 觸發(fā) 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) {
        // 根據(jù)是否有延時(shí)執(zhí)行,來(lái)放到不同的動(dòng)畫(huà)任務(wù)隊(duì)列中
        // 若隊(duì)列為空,則直接 post
        if (mAnimationCallbacks.size() == 0) {
            // 這里實(shí)際是調(diào)用 Choreographer.scheduleFrameLocked(),發(fā)送一條異步消息
            getProvider().postFrameCallback(mFrameCallback);
        }
        // 隊(duì)列不為空,就往隊(duì)尾插入
        if (!mAnimationCallbacks.contains(callback)) {
            mAnimationCallbacks.add(callback);
        }
        // 延時(shí)執(zhí)行,則放到另外的隊(duì)列中
        if (delay > 0) {
            mDelayedCallbackStartTime.put(callback, (SystemClock.uptimeMillis() + delay));
        }
    }    
    

有點(diǎn)繞了,先整理下思路,在調(diào)用開(kāi)始后,AnimationHandler 會(huì)創(chuàng)建一個(gè) Callback,放到隊(duì)列中,或者直接發(fā)送 Msg,這里的作用是什么呢?

實(shí)際上最終是向 native 注冊(cè) VSYNC 信號(hào)的監(jiān)聽(tīng),通過(guò) DisplayEventReceiver.scheduleVsync() 注冊(cè)的,當(dāng) VSYNC 信號(hào)到來(lái)時(shí),會(huì)觸發(fā) DisplayEventReceiver.dispatchVsync,進(jìn)而觸發(fā) FrameDisplayEventReceiver.onVsync()

    // Called from native code.
    private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame) {
        onVsync(timestampNanos, physicalDisplayId, frame);
    }
    
    // 這里將 native 層的 VSYNC ,分發(fā)到 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;
            // 發(fā)送異步消息
            Message msg = Message.obtain(mHandler, this);
            msg.setAsynchronous(true);
            mHandler.sendMessageAtTime(msg, timestampNanos / TimeUtils.NANOS_PER_MS);
        }

VSYNC 是什么 ?

VSYNC 是垂直同步的意思,在 Android 應(yīng)用層意味著屏幕刷新,具體的 VSYNC 機(jī)制參考 Vsync 信號(hào)機(jī)制和 UI 刷新流程

Choreographer 在動(dòng)畫(huà) start() 中,實(shí)現(xiàn)了什么功能

注冊(cè)監(jiān)聽(tīng) VSYNC 信號(hào),并且在信號(hào)到來(lái)后,在 Java 層發(fā)送通知

Choreographer 原理

這個(gè)單獨(dú)開(kāi)一篇文章來(lái)講

相關(guān)鏈接

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容