天天看點

Databinding源碼如何實作資料綁定更新?看這篇就夠了

DataBinding是什麼這個不用多說,大部分同學使用了很久。對于我們來說有很多好處,提高了代碼的簡潔度,讓代碼的耦合度降低等等。那麼,DataBinding到底如何就實作了資料的綁定,我們光寫一個布局,怎麼就能實作資料的更新了?本文從BaseObservable開始分析一下DataBinding的一些原理。

目錄

1.DataBinding中的BaseObservable類

2.Databinding實作源碼分析

1.inflate方法:

2.生成類綁定ViewModel

3.設定可觀察資料監聽的過程

3.總結

首先了解一下DataBinding中使用的BaseObsevable

1.DataBinding中的BaseObservable類

BaseObservable類是Databindbing提供的一個擁有觀察能力的類,通過繼承它我們能夠實作資料改變的時候動态的通知。

1.基礎的實作類使用

BaseObservable繼承自Observable,同時提供了一些基礎的資料類型的實作類:

ObservableBoolean

ObservableChar

ObservableInt

ObservableFloat
           

這些基礎類,我們在一個ViewModel中聲明這些類的對象,在資料發生改變的時候調用對應的set方法更新資料,綁定的View就能自定更新了。

2.通過繼承BaseObservable自定義一個可觀察的類。

對于基本資料類不夠用的時候,我們可以自定義一個類,在類中使用@Bindable來注解這些字段,同時提供對應的set方法,在資料變化的時候調用在set方法中調用notifyChange或者notifyPropertyChanged()方法通知資料改變。

Databinding源碼如何實作資料綁定更新?看這篇就夠了

如上申明後,我們可以在綁定的View中引入這個類的對象,把類對象在布局中幫到View,在資料變化的時候就會自動更新。

假設我們定義了一個Product類型的對象如下:

Databinding源碼如何實作資料綁定更新?看這篇就夠了

假設請求網絡後更新資料如下:

Databinding源碼如何實作資料綁定更新?看這篇就夠了

布局中使用如下:

Databinding源碼如何實作資料綁定更新?看這篇就夠了

這樣就完成了一個可觀察類型的定義和綁定。

[email protected]注解幹了什麼

通過一個注解最後就能完成可觀察資料的建立,到底幹了些什麼?

我們根據官方文檔知道,在@Bindable注解後,調用notifyPropertyChanged方法的時候要傳遞一個「BR.屬性」如:BR.productName

Databinding源碼如何實作資料綁定更新?看這篇就夠了

也就是說被@Bindable注解就會在BR檔案中生成對應的值。但是上面執行個體中我們沒有注解loginViewModl、userInfo、product這幾個字段,隻是在布局中綁定對象使用了,BR檔案也生成了一個字段,這是為什麼?

我們查詢每個布局生成的對應的一個java類,比如我們目前的布局是activity_login.xml則對應的是ActivityLoginBinding。我們看到,果然在這個類中自定生成的代碼中加上了布局中定義的variable中的對象注解。(也就是在布局中添加的variable節點都會被自動使用@Bindable注解生成BR對應的id)

Databinding源碼如何實作資料綁定更新?看這篇就夠了

檢視這個生成類的父類,它繼承自BaseObservable,這下就真相大白了。

Databinding源碼如何實作資料綁定更新?看這篇就夠了

原來databinding會幫我們把布局中申明為variable中的對象添加上@Bindable注解,這樣,這些對象也擁有了可觀察的能力,當資料變化的時候就會自動更新。其中ActivityLoginBinding有一個子類來實作其他的一些功能:

Databinding源碼如何實作資料綁定更新?看這篇就夠了

Activity代碼中綁定

Databinding源碼如何實作資料綁定更新?看這篇就夠了

生成的類中設定綁定的邏輯

Databinding源碼如何實作資料綁定更新?看這篇就夠了

2.Databinding實作源碼分析

Databinding是如何實作綁定的?我們在布局中添加了layout層,然後再layout中添加了variable節點,在節點中添加引用的ViewModel等。同時添加了其他的View布局,最後通過編譯生成一個布局名稱+Binding的類比如布局是activity_login.xml,則生成ActivityLoginBinding.java,在Activity的setContentView中通過inflate來加載布局,建立這個生成的Binding對象。

Databinding源碼如何實作資料綁定更新?看這篇就夠了

是以從此處開始分析:

在build生成的檔案中找到如ActivityLoginBinding的生成類。

1.Databinding的inflate方法

Databinding源碼如何實作資料綁定更新?看這篇就夠了

能看到,在代碼中加載了布局:

Databinding源碼如何實作資料綁定更新?看這篇就夠了
Databinding源碼如何實作資料綁定更新?看這篇就夠了

在DataBindingUtil類中繼續調用inflate,在這裡通過View的inflate解析布局生成View對象:

Databinding源碼如何實作資料綁定更新?看這篇就夠了

最後調用到了bind方法中,把生成的View傳遞進去:

Databinding源碼如何實作資料綁定更新?看這篇就夠了

來到了sMapper:

最後在DataBinderMapperImpl中找到getDataBinder方法:

Databinding源碼如何實作資料綁定更新?看這篇就夠了

檢視構造方法:

Databinding源碼如何實作資料綁定更新?看這篇就夠了
Databinding源碼如何實作資料綁定更新?看這篇就夠了

mapBinding()方法建立了一個Object[]集合,最後從mapBindings方法中看到

Databinding源碼如何實作資料綁定更新?看這篇就夠了
Databinding源碼如何實作資料綁定更新?看這篇就夠了
Databinding源碼如何實作資料綁定更新?看這篇就夠了

最後解析完成傳回了bindings[]集合,儲存了布局中的View。

前面看到了在構造方法中調用super,并取出了集合中儲存的View傳遞給父類的構造方法,檢視父類的構造方法:

Databinding源碼如何實作資料綁定更新?看這篇就夠了

這樣就解析到了布局中設定了id的View,并且儲存到了生成的ActivityLoginBinding類中。

我們在代碼中建立了對應布局的Binding對象後,就可以通過對象擷取到布局中的View來操作。

2.生成類綁定ViewModel

我們在綁定布局的時候需要把目前的ViewModel綁定到布局生成的Binding類中。如下:

Databinding源碼如何實作資料綁定更新?看這篇就夠了

這個生成的setLoginViewModel方法就是在布局中設定了variable的一個對象。我們在生成的Binding的實作類找到了setLoginViewModel的實作類

Databinding源碼如何實作資料綁定更新?看這篇就夠了
Databinding源碼如何實作資料綁定更新?看這篇就夠了

notifyPropertyChanged方法如下:

Databinding源碼如何實作資料綁定更新?看這篇就夠了

mCallbacks是一個PropertyChangeRegistry類型的對象

Databinding源碼如何實作資料綁定更新?看這篇就夠了

最終會來到CallbackRegistry類,調用notifyCallbacks方法

Databinding源碼如何實作資料綁定更新?看這篇就夠了

可以看到,還調用了notifyRecurse(sender, arg, arg2);

最後來到了notifyCallbacks方法:

Databinding源碼如何實作資料綁定更新?看這篇就夠了

然後循環調用了mNotifier的onNotifyCllback。

這個mNotifier在其構造方法中傳遞來的:

Databinding源碼如何實作資料綁定更新?看這篇就夠了

也就是前面我檢視的PropertyChangeRegistry類中傳遞來的。

Databinding源碼如何實作資料綁定更新?看這篇就夠了

也就是最後循環調用了CallbackRegistry.NotifierCallback的onNotifyCallback方法,它接收了一個OnPropertyChangedCallback參數,調用其onPropertyChanged方法。

(最後我們會知道這個onPropertyChanged是誰的方法)

到這裡就調用結束了,我們現在要找一下PropertyChangeRegistry這個類被誰使用了,幹了什麼。回到之前的setLoginViewModel()綁定ViewModel的時候。

3.設定可觀察資料監聽的過程

Databinding源碼如何實作資料綁定更新?看這篇就夠了

這裡調用了一個super.requestRebind();方法

Databinding源碼如何實作資料綁定更新?看這篇就夠了

這個方法中做了一個判斷,最終執行到了Callback。不管如何執行,最後都來到了runable中:

Databinding源碼如何實作資料綁定更新?看這篇就夠了

很明顯能看到最後的一句executePendingBindings應該是核心代碼:

Databinding源碼如何實作資料綁定更新?看這篇就夠了
Databinding源碼如何實作資料綁定更新?看這篇就夠了

其中mRebindCallbacks是CallbackRegistry對象,前面我們已經見過了,最後會循環回調。

執行到了一句核心executeBindings(),這是一個抽象類,去對應的Activity的生成的實作類中找一下實作方法:

Databinding源碼如何實作資料綁定更新?看這篇就夠了
Databinding源碼如何實作資料綁定更新?看這篇就夠了

這裡我們看到擷取了ViewModel中儲存的對象,然後調用了一個updateLiveDataRegistration()方法,因為DataBinding中可以使用LiveData這個可以儲存資料的庫,我們在這裡剛好也使用到了。

中間還有一些對布局中設定的資料的處理,比如判斷,拼接等,最後我們看到調用了setText等方法:

Databinding源碼如何實作資料綁定更新?看這篇就夠了

這裡注意到一點,并沒有直接調用TextView的setText方法,而是使用了

androidx.databinding.adapters.TextViewBindingAdapter 

com.lll.digua.bindingadpter.BaseViewBindingAdpterKt
           
Databinding源碼如何實作資料綁定更新?看這篇就夠了
Databinding源碼如何實作資料綁定更新?看這篇就夠了

檢視這兩個類發現,原來DataBinding底層已經設定了很多的BindingAdapter,對應了不同的View,最後調用BindingAdapter的方法設定資料。

到這裡我們還有一個方法沒有看,就是updateLiveDataRegistration(2, loginViewModlIsShow);、updateRegistration(3, loginViewModlOldUserInfo);這裡又幹了什麼?從代碼看到,應該是一個View對應一個updateXXX方法第一個參數localFieldId,是對應的一個數字,第二個參數是要使用的資料對象比如getProduct。

Databinding源碼如何實作資料綁定更新?看這篇就夠了

内部調用了updateRegistration()這個方法,第一個參數是localFieldId,第二個參數是一個observable,也就是我們傳遞的getProduct,剛好我們的Prduct就是繼承自BaseObservable

Databinding源碼如何實作資料綁定更新?看這篇就夠了
Databinding源碼如何實作資料綁定更新?看這篇就夠了

第三個參數是一個CREATE_PROPERTY_LISTENER,其實是CreateWeakListener對象。

Databinding源碼如何實作資料綁定更新?看這篇就夠了

這個create方法傳回的類WeakPropertyListener繼承了OnPropertyChangedCallback,實作了一個ObservableReference接口。其中有幾個方法addListener、onPropertyChanged注意後邊會用到這裡的類和方法.

Databinding源碼如何實作資料綁定更新?看這篇就夠了

updateRegistration方法内部:

Databinding源碼如何實作資料綁定更新?看這篇就夠了
Databinding源碼如何實作資料綁定更新?看這篇就夠了

能看到它是WeakListener類型的數組。在ViewDataBinding被初始化,也就是DataBinding生成的類初始化的時候建立。檢視生成類的代碼我們知道,這裡的root就是每個布局生成的View,localFieldCount就是布局中藥綁定的由id的View的個數,比如我們代碼中是5個View就是5.

這裡的WeakListener代碼如下:

内部有一個setTarget方法,給observable對象綁定了listener。

Databinding源碼如何實作資料綁定更新?看這篇就夠了

回到updateRegistration方法,剛開始從mLocalFieldObservers取值listener是null。是以會執行到判斷裡邊。執行registerTo(localFieldId, observable, listenerCreator);

Databinding源碼如何實作資料綁定更新?看這篇就夠了
Databinding源碼如何實作資料綁定更新?看這篇就夠了

在方法裡邊通過listenerCreator調用create方法擷取一個listener。

也就是調用前面的CreateWeakListener的create方法建立了一個WeakPropertyListener類對象,它是一個Listener,

把它儲存到mLocalFieldObservers中。

最後一句調用了listener.setTarget(observable),就是前面說的WeakListener中的setTarget方法

Databinding源碼如何實作資料綁定更新?看這篇就夠了

這裡的mObsevable對象就是WeakListener的第三個參數,而這個參數this是一個ObservableReference類型,也就是WeakPropertyListener類本身。

Databinding源碼如何實作資料綁定更新?看這篇就夠了

是以上面的setTarget方法也就是中的mObservable.addListener也就是WeakPropertyListener的方法了。

Databinding源碼如何實作資料綁定更新?看這篇就夠了

addListener的參數target就是我們傳遞進來的繼承了BaseObservable的對象。

最後調用了target.addOnPropertyChangedCallback(this);這裡的this指的就是

OnPropertyChangedCallback這個實作,它有一個方法onPropertyChanged:

Databinding源碼如何實作資料綁定更新?看這篇就夠了

這裡的onPropertyChanged什麼時候會被調用呢?

前面我們在分析notifyPropertyChanged()(一個字段更新資料後要調用它來通知更新)。

最後說了一個PropertyChangeRegistry類,它參數有一個CallbackRegistry.NotifierCallback對象,它的onNotifyCallback方法接收OnPropertyChangedCallback類型的參數,最後調用onPropertyChanged方法。

到這裡就接上了前面的内容,整個流程走通。

3.總結

從整個分析可以看出:

1.通過inflate的調用解析布局,生成View儲存在自動生成的類中提供給我們使用。

2.在建立了Binding對象後綁定ViewModel時調用setxxxViewModel方法時調用了requestRebind()方法,這個方法内部通過Handler執行了一個runable,最後執行到生成類的executeBindings()方法中.在這個方法中對布局中綁定資料進行操作,然後執行了updateRegistration和updateLiveDataRegistration相關的方法。内部又建立了實作了ObservableReference接口的WeakPropertyListener對象,來處理回調,添加可觀察對象的監聽以供資料改變時調用。

3.每次在需要更新時調用notifyPropertyChanged()方法,然後調用PropertyChangeRegistry類對象的notifyCallbacks方法,最後調用到onPropertyChanged,通知更新。更新的操作在生成類的executeBindings方法中完成。

以上為個人的學習筆記,由于水準有限,作文倉促,如有發現問題請提出意見,謝謝~