日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

Android?DataBinding單向數據綁定深入探究_Android

作者:昉鈺 ? 更新時間: 2022-12-13 編程語言

在前面DataBinding原理----布局的加載這篇文章中,我們說明了DataBinding中布局的加載過程,這里繼續下一步,數據是如何進行綁定的,這里只介紹單向數據綁定,即數據的變化會反映到控件上;后面再介紹雙向數據綁定。

在分析源碼之前,在心里要有一個概念就是這里的數據綁定是基于觀察者模式來實現的,所以在閱讀這部分源碼的時候要著重分清楚,誰是觀察者誰是被觀察者,把這個思想放在心理,這樣就能抓住代碼的本質。

這一篇分為兩個小部分,首先是數據的綁定流程分析,然后是觀察者模式綁定關系的建立流程。

一、數據綁定流程

代碼分析,走起。還是貼一段Activity的代碼如下:

class MainActivity : AppCompatActivity() {
    private val viewModel: SimpleViewModel by viewModels()
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding: ActivityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        binding.lifecycleOwner = this
        binding.viewModel = viewModel
    }
}

數據綁定的關鍵就是binding.viewModel = viewModel這行代碼,其調用的方法實現如下:

    public void setViewModel(@Nullable com.zfang.databindingstudy.module.SimpleViewModel ViewModel) {
        this.mViewModel = ViewModel;
        synchronized(this) {
            mDirtyFlags |= 0x4L;
        }
        notifyPropertyChanged(BR.viewModel);
        super.requestRebind();
    }

notifyPropertyChanged這一行其實就是在通知觀察者,數據發生變化了。不過這不是我們這里關注的重點(不過注意這里的mDirtyFlags 已經被修改了,已經有值)。重點在后面的那一行super.requestRebind(),其實現如下:

  protected void requestRebind() {
        //處理有include標簽的情況
        if (mContainingBinding != null) {
            mContainingBinding.requestRebind();
        } else {
            //我們的場景下走的是這里
            final LifecycleOwner owner = this.mLifecycleOwner;
            if (owner != null) {
                //如果生命周期不是至少STARTED則返回
                Lifecycle.State state = owner.getLifecycle().getCurrentState();
                if (!state.isAtLeast(Lifecycle.State.STARTED)) {
                    return; // wait until lifecycle owner is started
                }
            }
            synchronized (this) {
                if (mPendingRebind) {
                    return;
                }
                //置標志位
                mPendingRebind = true;
            }
            if (USE_CHOREOGRAPHER) {
                //走這里,使用垂直同步刷新
                mChoreographer.postFrameCallback(mFrameCallback);
            } else {
                mUIThreadHandler.post(mRebindRunnable);
            }
        }
    }

首先判斷是不是屬于include標簽的情況(也就是布局中包含include標簽),如果不是則走到else分支,然后判斷生命周期是不是至少STARTED狀態,如果不是則不執行數據綁定(生命周期恢復的時候會再執行數據綁定);如果滿足生命周期要求,則繼續判斷,首先把mPendingRebind 置位(避免重復綁定數據),然后使用垂直同步刷新機制post了一個callback,callback實現(位于ViewDataBinding的構建函數中)如下:

    if (USE_CHOREOGRAPHER) {
        mChoreographer = Choreographer.getInstance();
        mFrameCallback = new Choreographer.FrameCallback() {
            @Override
            public void doFrame(long frameTimeNanos) {
                mRebindRunnable.run();
            }
        };
    } else {
        mFrameCallback = null;
        mUIThreadHandler = new Handler(Looper.myLooper());
    }

其實最終走的還是mRebindRunnable,其實現如下:

    private final Runnable mRebindRunnable = new Runnable() {
        @Override
        public void run() {
            synchronized (this) {
                mPendingRebind = false;
            }
            //處理弱引用隊列中的監聽器,避免內在泄漏
            processReferenceQueue();
            //如果是Android4.4及以后走這里
            if (VERSION.SDK_INT >= VERSION_CODES.KITKAT) {
                // Nested so that we don't get a lint warning in IntelliJ
                //如果View還沒attach上,則返回;后面attach上會再執行數據綁定
                if (!mRoot.isAttachedToWindow()) {
                    // Don't execute the pending bindings until the View
                    // is attached again.
                    mRoot.removeOnAttachStateChangeListener(ROOT_REATTACHED_LISTENER);
                    mRoot.addOnAttachStateChangeListener(ROOT_REATTACHED_LISTENER);
                    return;
                }
            }
            //執行數據綁定.
            executePendingBindings();
        }
    };

嗯,代碼中都寫了注釋。首先處理弱引用隊列,避免內存泄漏,然后判斷如果View還沒attach則返回,后面attach上再執行數據綁定。最后調用executePendingBindings,執行數據綁定。

    public void executePendingBindings() {
        if (mContainingBinding == null) {
            //沒有include會走這里
            executeBindingsInternal();
        } else {
            mContainingBinding.executePendingBindings();
        }
    }

我們的場景下走executeBindingsInternal,代碼如下:

    private void executeBindingsInternal() {
        if (mIsExecutingPendingBindings) {
            requestRebind();
            return;
        }
        //這里調用了apt代碼生存的方法,也就是我們工程中的方法。
        if (!hasPendingBindings()) {
            return;
        }
        mIsExecutingPendingBindings = true;
        mRebindHalted = false;
        if (mRebindCallbacks != null) {
            mRebindCallbacks.notifyCallbacks(this, REBIND, null);
            // The onRebindListeners will change mPendingHalted
            if (mRebindHalted) {
                mRebindCallbacks.notifyCallbacks(this, HALTED, null);
            }
        }
        if (!mRebindHalted) {
            //這里執行綁定
            executeBindings();
            if (mRebindCallbacks != null) {
                mRebindCallbacks.notifyCallbacks(this, REBOUND, null);
            }
        }
        mIsExecutingPendingBindings = false;
    }

上面主要是一些條件判斷,避免重復執行綁定。其中方法hasPendingBindings判斷有沒有需要執行數據綁定,最后executeBindings調用執行數據綁定;hasPendingBindings的實現位于ActivityMainBindingImpl.java中,也就是DataBinding幫我們生存的類,代碼如下:

   //ActivityMainBindingImpl.java
   public boolean hasPendingBindings() {
        synchronized(this) {
            if (mDirtyFlags != 0) {
                return true;
            }
        }
        return false;
    }

上面我們說過mDirtyFlags 在setViewModel方法調用的時候參數已經不為0了,所以這里會返回true,也就是后面會執行到executeBindings方法,其實現也位于ActivityMainBindingImpl.java類中,代碼如下:

    @Override
    protected void executeBindings() {
        long dirtyFlags = 0;
        synchronized (this) {
            //復制位標識,記錄了哪些數據發生變化。
            dirtyFlags = mDirtyFlags;
            mDirtyFlags = 0;
        }
        java.lang.String viewModelFirstGetValue = null;
        androidx.lifecycle.LiveData<java.lang.String> viewModelSecond = null;
        androidx.lifecycle.LiveData<java.lang.String> viewModelFirst = null;
        java.lang.String viewModelSecondGetValue = null;
        com.zfang.databindingstudy.module.SimpleViewModel viewModel = mViewModel;
        //根據dirtyFlags 判斷哪些數據發生變化,一個變量會對應到二進制里面的一個位
        //該位不為0,說明數據有變化,于是會執行相應的獲得數據邏輯。
        if ((dirtyFlags & 0xfL) != 0) {
            if ((dirtyFlags & 0xdL) != 0) {
                if (viewModel != null) {
                    // read viewModel.second
                    viewModelSecond = viewModel.getSecond();
                }
                updateLiveDataRegistration(0, viewModelSecond);
                if (viewModelSecond != null) {
                    // read viewModel.second.getValue()
                    viewModelSecondGetValue = viewModelSecond.getValue();
                }
            }
            if ((dirtyFlags & 0xeL) != 0) {
                if (viewModel != null) {
                    // read viewModel.first
                    viewModelFirst = viewModel.getFirst();
                }
                //建立觀察者綁定關系
                updateLiveDataRegistration(1, viewModelFirst);
                if (viewModelFirst != null) {
                    // read viewModel.first.getValue()
                    viewModelFirstGetValue = viewModelFirst.getValue();
                }
            }
        }
        // batch finished
        //根據上面拿到的數據調用setText,也就是設置了相應的數據到UI上,實現
        //了數據的變化更新的UI邏輯。
        if ((dirtyFlags & 0xeL) != 0) {
            // api target 1
            androidx.databinding.adapters.TextViewBindingAdapter.setText(this.first, viewModelFirstGetValue);
        }
        if ((dirtyFlags & 0xdL) != 0) {
            // api target 1
            androidx.databinding.adapters.TextViewBindingAdapter.setText(this.second, viewModelSecondGetValue);
        }
    }
    // Listener Stub Implementations
    // callback impls
    // dirty flag
    private long mDirtyFlags = 0xffffffffffffffffL;
    /* flag mapping
        flag 0 (0x1L): viewModel.second
        flag 1 (0x2L): viewModel.first
        flag 2 (0x3L): viewModel
        flag 3 (0x4L): null
    flag mapping end*/
    //end

首先說明下,我們的model里面一般來說會有幾個變量,其實每一個變量在DataBinding中都會有一個二進制位來標識當前數據是否發生了變化,如果發生變化,則該位置1,然后用dirtyFlags 作一個“與”運算就可以判斷出該數據位是否發生了變化(好好體會下)。

對應到我們的場景中,flag 0 (0x1L): viewModel.second這個標識就對應到second這個變量,而flag 1 (0x2L): viewModel.first就對應到first這個變量,再用dirtyFlags作運算就知道哪個位發生變化。

等等,數據是已經綁定了,怎么沒看到觀察者模式的綁定關系?其實上面的代碼注釋已經說明了,這行代碼updateLiveDataRegistration正是建立了觀察者模式的綁定關系。

終于看到數據的綁定了,下面繼續看下updateLiveDataRegistration是如何建立觀察者模式的綁定關系。

二、建立觀察者模式綁定關系

首先貼上updateLiveDataRegistration的代碼如下:

    //ViewDataBinding.java
    protected boolean updateLiveDataRegistration(int localFieldId, LiveData<?> observable) {
        mInLiveDataRegisterObserver = true;
        try {
            return updateRegistration(localFieldId, observable, CREATE_LIVE_DATA_LISTENER);
        } finally {
            mInLiveDataRegisterObserver = false;
        }
    }

首先作下參數說明,第一個參數說明是哪個位置上的變量,第二個是個LiveData(對應到了我們在ViewModel中定義的數據變量,也就是要被觀察的變量)。這里只是簡單的轉發調用下updateRegistration,最后一個參數是一個creator(利用了工廠方法模式),其實現如下:

    private static final CreateWeakListener CREATE_LIVE_DATA_LISTENER = new CreateWeakListener() {
        @Override
        public WeakListener create(
                ViewDataBinding viewDataBinding,
                int localFieldId,
                ReferenceQueue<ViewDataBinding> referenceQueue
        ) {
            return new LiveDataListener(viewDataBinding, localFieldId, referenceQueue)
                    .getListener();
        }
    };

就是創建了一個LiveDataListener對象,并調用了它的getListener方法,該方法會返回一個WeakListener類型的變量。繼續上面的updateRegistration方法,代碼如下:

    protected boolean updateRegistration(int localFieldId, Object observable,
            CreateWeakListener listenerCreator) {
        //由于observable是一個livedata對象且不空,所以不會走這里。
        if (observable == null) {
            return unregisterFrom(localFieldId);
        }
        WeakListener listener = mLocalFieldObservers[localFieldId];
        //因為是第一次進入這里,所以會進入到registerTo中
        if (listener == null) {
            registerTo(localFieldId, observable, listenerCreator);
            return true;
        }
        if (listener.getTarget() == observable) {
            return false;//nothing to do, same object
        }
        unregisterFrom(localFieldId);
        registerTo(localFieldId, observable, listenerCreator);
        return true;
    }

根據代碼中的注釋可知,如果是第一次進入最后會進入到registerTo方法中,其實現如下:

    protected void registerTo(int localFieldId, Object observable,
            CreateWeakListener listenerCreator) {
        if (observable == null) {
            return;
        }
        WeakListener listener = mLocalFieldObservers[localFieldId];
        if (listener == null) {
            //上面說過這里返回的是個WeakListener對象。
            listener = listenerCreator.create(this, localFieldId, sReferenceQueue);
            //把對象保存起來
            mLocalFieldObservers[localFieldId] = listener;
            //設置lifeCycle
            if (mLifecycleOwner != null) {
                listener.setLifecycleOwner(mLifecycleOwner);
            }
        }
        //建立觀察者綁定關系
        listener.setTarget(observable);
    }

首先判斷如果listener則創建,返回的是一個WeakListener對象(其實中間還有一個LiveDataListener對象,WeakListener對象的mObservable變量會持有LiveDataListener對象),然后保存到mLocalFieldObservers數組中,看上去就是一個觀察者數組對象,最后調用WeakListener的setTarget方法,其實現如下:

    public void setTarget(T object) {
        //這里的object就是傳遞過來的liveData對象,下面會把它保存到target中
        unregister();
        mTarget = object;
        if (mTarget != null) {
            //mObservable其實就是LiveDataListener對象
            mObservable.addListener(mTarget);
        }
    }

重復下,上面的mTarget就是ViewModel中的LiveData對象,mObservable其實就是LiveDataListener對象,所以調用了LiveDataListener的addListener方法,實現如下:

    //ViewDataBinding$LiveDataListener
    public void addListener(LiveData<?> target) {
        //target是LiveData
        LifecycleOwner lifecycleOwner = getLifecycleOwner();
        if (lifecycleOwner != null) {
            //LiveData監聽了lifecycleOwner的生命周期變化。
            target.observe(lifecycleOwner, this);
        }
    }

很簡單的邏輯,就是通過target.observe(lifecycleOwner, this)建立了觀察者模式的綁定關系。之后生命周期發生變化就會調用到onChange方法,代碼如下:

        public void onChanged(@Nullable Object o) {
            ViewDataBinding binder = mListener.getBinder();
            if (binder != null) {
                binder.handleFieldChange(mListener.mLocalFieldId, mListener.getTarget(), 0);
            }
        }

這里的binder其實就是ActivityMainBindingImpl,隨后調用handleFieldChange,代碼如下:

    protected void handleFieldChange(int mLocalFieldId, Object object, int fieldId) {
        if (mInLiveDataRegisterObserver || mInStateFlowRegisterObserver) {
            // We're in LiveData or StateFlow registration, which always results in a field change
            // that we can ignore. The value will be read immediately after anyway, so
            // there is no need to be dirty.
            return;
        }
        //調用了子類實現,也就是ActivityMainBindingImpl
        boolean result = onFieldChange(mLocalFieldId, object, fieldId);
        if (result) {
            //重新綁定數據
            requestRebind();
        }
    }

首先調用了onFiledChange,它的實現位于apt生存的代碼中,如下:

    @Override
    protected boolean onFieldChange(int localFieldId, Object object, int fieldId) {
        switch (localFieldId) {
            case 0 :
                return onChangeViewModelSecond((androidx.lifecycle.LiveData<java.lang.String>) object, fieldId);
            case 1 :
                return onChangeViewModelFirst((androidx.lifecycle.LiveData<java.lang.String>) object, fieldId);
        }
        return false;
    }
    private boolean onChangeViewModelSecond(androidx.lifecycle.LiveData<java.lang.String> ViewModelSecond, int fieldId) {
        if (fieldId == BR._all) {
            synchronized(this) {
                    mDirtyFlags |= 0x1L;
            }
            return true;
        }
        return false;
    }
    private boolean onChangeViewModelFirst(androidx.lifecycle.LiveData<java.lang.String> ViewModelFirst, int fieldId) {
        if (fieldId == BR._all) {
            synchronized(this) {
                    mDirtyFlags |= 0x2L;
            }
            return true;
        }
        return false;
    }

根據哪些數據發生變化,返回true或者false,如果返回true,之后會調用requestRebind,不正是對應開了開頭中分析的函數了嗎?形成閉環。從而完成了整個觀察者模式的建立與響應流程。

這一篇中分析了數據的綁定與觀察者模式的建立流程,如果想了解DataBinding中布局的加載可以看前一篇DataBinding原理----布局的加載。下一篇將會分析雙向數據綁定流程。

原文鏈接:https://blog.csdn.net/www586089/article/details/127795400

欄目分類
最近更新