Android DataBinding單向數(shù)據(jù)綁定深入探究
在前面DataBinding原理----布局的加載這篇文章中,我們說明了DataBinding中布局的加載過程,這里繼續(xù)下一步,數(shù)據(jù)是如何進行綁定的,這里只介紹單向數(shù)據(jù)綁定,即數(shù)據(jù)的變化會反映到控件上;后面再介紹雙向數(shù)據(jù)綁定。
在分析源碼之前,在心里要有一個概念就是這里的數(shù)據(jù)綁定是基于觀察者模式來實現(xiàn)的,所以在閱讀這部分源碼的時候要著重分清楚,誰是觀察者誰是被觀察者,把這個思想放在心理,這樣就能抓住代碼的本質(zhì)。
這一篇分為兩個小部分,首先是數(shù)據(jù)的綁定流程分析,然后是觀察者模式綁定關(guān)系的建立流程。
一、數(shù)據(jù)綁定流程
代碼分析,走起。還是貼一段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 } }
數(shù)據(jù)綁定的關(guān)鍵就是binding.viewModel = viewModel這行代碼,其調(diào)用的方法實現(xiàn)如下:
public void setViewModel(@Nullable com.zfang.databindingstudy.module.SimpleViewModel ViewModel) { this.mViewModel = ViewModel; synchronized(this) { mDirtyFlags |= 0x4L; } notifyPropertyChanged(BR.viewModel); super.requestRebind(); }
notifyPropertyChanged這一行其實就是在通知觀察者,數(shù)據(jù)發(fā)生變化了。不過這不是我們這里關(guān)注的重點(不過注意這里的mDirtyFlags 已經(jīng)被修改了,已經(jīng)有值)。重點在后面的那一行super.requestRebind(),其實現(xiàn)如下:
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狀態(tài),如果不是則不執(zhí)行數(shù)據(jù)綁定(生命周期恢復的時候會再執(zhí)行數(shù)據(jù)綁定);如果滿足生命周期要求,則繼續(xù)判斷,首先把mPendingRebind 置位(避免重復綁定數(shù)據(jù)),然后使用垂直同步刷新機制post了一個callback,callback實現(xiàn)(位于ViewDataBinding的構(gòu)建函數(shù)中)如下:
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,其實現(xiàn)如下:
private final Runnable mRebindRunnable = new Runnable() { @Override public void run() { synchronized (this) { mPendingRebind = false; } //處理弱引用隊列中的監(jiān)聽器,避免內(nèi)在泄漏 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上會再執(zhí)行數(shù)據(jù)綁定 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; } } //執(zhí)行數(shù)據(jù)綁定. executePendingBindings(); } };
嗯,代碼中都寫了注釋。首先處理弱引用隊列,避免內(nèi)存泄漏,然后判斷如果View還沒attach則返回,后面attach上再執(zhí)行數(shù)據(jù)綁定。最后調(diào)用executePendingBindings,執(zhí)行數(shù)據(jù)綁定。
public void executePendingBindings() { if (mContainingBinding == null) { //沒有include會走這里 executeBindingsInternal(); } else { mContainingBinding.executePendingBindings(); } }
我們的場景下走executeBindingsInternal,代碼如下:
private void executeBindingsInternal() { if (mIsExecutingPendingBindings) { requestRebind(); return; } //這里調(diào)用了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) { //這里執(zhí)行綁定 executeBindings(); if (mRebindCallbacks != null) { mRebindCallbacks.notifyCallbacks(this, REBOUND, null); } } mIsExecutingPendingBindings = false; }
上面主要是一些條件判斷,避免重復執(zhí)行綁定。其中方法hasPendingBindings判斷有沒有需要執(zhí)行數(shù)據(jù)綁定,最后executeBindings調(diào)用執(zhí)行數(shù)據(jù)綁定;hasPendingBindings的實現(xiàn)位于ActivityMainBindingImpl.java中,也就是DataBinding幫我們生存的類,代碼如下:
//ActivityMainBindingImpl.java public boolean hasPendingBindings() { synchronized(this) { if (mDirtyFlags != 0) { return true; } } return false; }
上面我們說過mDirtyFlags 在setViewModel方法調(diào)用的時候參數(shù)已經(jīng)不為0了,所以這里會返回true,也就是后面會執(zhí)行到executeBindings方法,其實現(xiàn)也位于ActivityMainBindingImpl.java類中,代碼如下:
@Override protected void executeBindings() { long dirtyFlags = 0; synchronized (this) { //復制位標識,記錄了哪些數(shù)據(jù)發(fā)生變化。 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; //根據(jù)dirtyFlags 判斷哪些數(shù)據(jù)發(fā)生變化,一個變量會對應(yīng)到二進制里面的一個位 //該位不為0,說明數(shù)據(jù)有變化,于是會執(zhí)行相應(yīng)的獲得數(shù)據(jù)邏輯。 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(); } //建立觀察者綁定關(guān)系 updateLiveDataRegistration(1, viewModelFirst); if (viewModelFirst != null) { // read viewModel.first.getValue() viewModelFirstGetValue = viewModelFirst.getValue(); } } } // batch finished //根據(jù)上面拿到的數(shù)據(jù)調(diào)用setText,也就是設(shè)置了相應(yīng)的數(shù)據(jù)到UI上,實現(xiàn) //了數(shù)據(jù)的變化更新的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中都會有一個二進制位來標識當前數(shù)據(jù)是否發(fā)生了變化,如果發(fā)生變化,則該位置1,然后用dirtyFlags 作一個“與”運算就可以判斷出該數(shù)據(jù)位是否發(fā)生了變化(好好體會下)。
對應(yīng)到我們的場景中,flag 0 (0x1L): viewModel.second這個標識就對應(yīng)到second這個變量,而flag 1 (0x2L): viewModel.first就對應(yīng)到first這個變量,再用dirtyFlags作運算就知道哪個位發(fā)生變化。
等等,數(shù)據(jù)是已經(jīng)綁定了,怎么沒看到觀察者模式的綁定關(guān)系?其實上面的代碼注釋已經(jīng)說明了,這行代碼updateLiveDataRegistration正是建立了觀察者模式的綁定關(guān)系。
終于看到數(shù)據(jù)的綁定了,下面繼續(xù)看下updateLiveDataRegistration是如何建立觀察者模式的綁定關(guān)系。
二、建立觀察者模式綁定關(guān)系
首先貼上updateLiveDataRegistration的代碼如下:
//ViewDataBinding.java protected boolean updateLiveDataRegistration(int localFieldId, LiveData<?> observable) { mInLiveDataRegisterObserver = true; try { return updateRegistration(localFieldId, observable, CREATE_LIVE_DATA_LISTENER); } finally { mInLiveDataRegisterObserver = false; } }
首先作下參數(shù)說明,第一個參數(shù)說明是哪個位置上的變量,第二個是個LiveData(對應(yīng)到了我們在ViewModel中定義的數(shù)據(jù)變量,也就是要被觀察的變量)。這里只是簡單的轉(zhuǎn)發(fā)調(diào)用下updateRegistration,最后一個參數(shù)是一個creator(利用了工廠方法模式),其實現(xiàn)如下:
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(); } };
就是創(chuàng)建了一個LiveDataListener對象,并調(diào)用了它的getListener方法,該方法會返回一個WeakListener類型的變量。繼續(xù)上面的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; }
根據(jù)代碼中的注釋可知,如果是第一次進入最后會進入到registerTo方法中,其實現(xiàn)如下:
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; //設(shè)置lifeCycle if (mLifecycleOwner != null) { listener.setLifecycleOwner(mLifecycleOwner); } } //建立觀察者綁定關(guān)系 listener.setTarget(observable); }
首先判斷如果listener則創(chuàng)建,返回的是一個WeakListener對象(其實中間還有一個LiveDataListener對象,WeakListener對象的mObservable變量會持有LiveDataListener對象),然后保存到mLocalFieldObservers數(shù)組中,看上去就是一個觀察者數(shù)組對象,最后調(diào)用WeakListener的setTarget方法,其實現(xiàn)如下:
public void setTarget(T object) { //這里的object就是傳遞過來的liveData對象,下面會把它保存到target中 unregister(); mTarget = object; if (mTarget != null) { //mObservable其實就是LiveDataListener對象 mObservable.addListener(mTarget); } }
重復下,上面的mTarget就是ViewModel中的LiveData對象,mObservable其實就是LiveDataListener對象,所以調(diào)用了LiveDataListener的addListener方法,實現(xiàn)如下:
//ViewDataBinding$LiveDataListener public void addListener(LiveData<?> target) { //target是LiveData LifecycleOwner lifecycleOwner = getLifecycleOwner(); if (lifecycleOwner != null) { //LiveData監(jiān)聽了lifecycleOwner的生命周期變化。 target.observe(lifecycleOwner, this); } }
很簡單的邏輯,就是通過target.observe(lifecycleOwner, this)建立了觀察者模式的綁定關(guān)系。之后生命周期發(fā)生變化就會調(diào)用到onChange方法,代碼如下:
public void onChanged(@Nullable Object o) { ViewDataBinding binder = mListener.getBinder(); if (binder != null) { binder.handleFieldChange(mListener.mLocalFieldId, mListener.getTarget(), 0); } }
這里的binder其實就是ActivityMainBindingImpl,隨后調(diào)用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; } //調(diào)用了子類實現(xiàn),也就是ActivityMainBindingImpl boolean result = onFieldChange(mLocalFieldId, object, fieldId); if (result) { //重新綁定數(shù)據(jù) requestRebind(); } }
首先調(diào)用了onFiledChange,它的實現(xiàn)位于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; }
根據(jù)哪些數(shù)據(jù)發(fā)生變化,返回true或者false,如果返回true,之后會調(diào)用requestRebind,不正是對應(yīng)開了開頭中分析的函數(shù)了嗎?形成閉環(huán)。從而完成了整個觀察者模式的建立與響應(yīng)流程。
這一篇中分析了數(shù)據(jù)的綁定與觀察者模式的建立流程,如果想了解DataBinding中布局的加載可以看前一篇DataBinding原理----布局的加載。下一篇將會分析雙向數(shù)據(jù)綁定流程。
到此這篇關(guān)于Android DataBinding單向數(shù)據(jù)綁定深入探究的文章就介紹到這了,更多相關(guān)Android DataBinding 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 淺析Android企業(yè)級開發(fā)數(shù)據(jù)綁定技術(shù)
- Android Studio綁定下拉框數(shù)據(jù)詳解
- 詳解Android的MVVM框架 - 數(shù)據(jù)綁定
- Android Data Binding數(shù)據(jù)綁定詳解
- Android RecyclerView 數(shù)據(jù)綁定實例代碼
- Android ListView數(shù)據(jù)綁定顯示的三種解決方法
- Android中 自定義數(shù)據(jù)綁定適配器BaseAdapter的方法
- Android數(shù)據(jù)雙向綁定原理實現(xiàn)和應(yīng)用場景
相關(guān)文章
Android開發(fā)手冊Button按鈕實現(xiàn)點擊音效
這篇文章主要為大家介紹了Android開發(fā)手冊Button按鈕實現(xiàn)點擊音效示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-06-06