基本內(nèi)容
- build.gradle文件
- @Bindable + @Observeble + PropertyChangeRegistry
- @BindingAdapter
- @BindingConversion
- @BindingMethods
- Two Way
- RecyclerView
- ViewStub的使用,主要是通過ViewStubProxy來實現(xiàn)的阀圾。
查看官方文檔即可學(xué)習(xí)侦另,很簡單泉沾。
DataBinding的工作
#對布局文件進行預(yù)處理
1.從根元素layout開始對布局文件進行預(yù)處理,為控件設(shè)置tag歹茶,文件位置為:app/build/intermediates/data-binding-layout-out文件夾下(我現(xiàn)在3.1里面找不到這個文件夾了,不造)
2.將data標(biāo)簽以及控件中的databinding表達式內(nèi)容抽取生成xxx-layout.xml文件,文件位置為:app/build/intermediates/data-binding-info/debug文件夾下
通過給原有布局中的view設(shè)置tag和生成文件中使用tag剪验,使得抽取出來的內(nèi)容和原內(nèi)容一一對應(yīng)肴焊。
#生成XXXBinding類與BR類
生成的XXXBinding類的文件位置:app/build/intermediates/classes/debug/package/databinding文件夾內(nèi)
生成的BR類的文件位置:app/build/intermediates/classes/debug/package文件夾內(nèi)
BR文件內(nèi)容組成:
- _all變量默認生成
- data標(biāo)簽里的每一個variable變量
- @Bindable注解的get方法名變小駝峰
- @Bindable注解的變量名
BR中的常量是一種標(biāo)識符,對應(yīng)每一個會發(fā)生變化的數(shù)據(jù)功戚。當(dāng)數(shù)據(jù)改變后娶眷,可以使用該標(biāo)識符通知DataBinding,DataBinding就會用新的數(shù)據(jù)去更新UI啸臀。
需要注意的是届宠,@Bindable注解的變量名,如果更改了get方法名乘粒,則在set方法內(nèi)調(diào)用notify更新的時候豌注,并不會起作用。
#生成XXXBinding實例并綁定
1.setContentView || inflate
2.生成XXXBinding實例對象:bindToAddedView()->bind->new XXXBinding(DataBindingComponent bindingComponent, View root);構(gòu)造方法內(nèi)部:遍歷Root灯萍,初始化自己的fields轧铁,清空之前的tags,invalidateAll引發(fā)數(shù)據(jù)綁定
3.進行數(shù)據(jù)綁定:executeBindings()
#詳細介紹數(shù)據(jù)綁定的Rebind機制
在構(gòu)造函數(shù)之后旦棉,調(diào)用了invalidateAll,該方法調(diào)用了requestRebind,方法實現(xiàn)在ViewDataBinding類中齿风。
protected void requestRebind() {
if (mContainingBinding != null) {
mContainingBinding.requestRebind();
} else {
synchronized (this) {
if (mPendingRebind) {
return;
}
mPendingRebind = true;
}
if (USE_CHOREOGRAPHER) {
mChoreographer.postFrameCallback(mFrameCallback);
} else {
mUIThreadHandler.post(mRebindRunnable);
}
}
}
- 檢測判斷是否存在include進的布局的Binding
- mPendingRebind:是否存在需要評估的綁定事件,如果沒請求過rebind操作他爸,則值默認為false聂宾。
- API > 16:往mChoreographer發(fā)一個mFrameCallback,在系統(tǒng)刷新界面(doFrame)的時候執(zhí)行rebind操作
- API < 16:往UI線程post一個mRebindRunnable任務(wù)
- 如果此前請求過執(zhí)行rebind操作诊笤,即已經(jīng)post了一個任務(wù)到隊列去系谐,而且這個任務(wù)還未獲得執(zhí)行,此時mPendingRebind的值為true讨跟,那么requestRebind將直接返回纪他,避免重復(fù)、頻繁執(zhí)行rebind操作帶來的性能損耗晾匠。
#總結(jié)
整體的編譯過程簡單理解為:Begin Compilation -> Process Layout Files -> Parse Expressions -> Java Compilation -> Resolve Dependencies -> Find Setters -> WriteView Binders
(可以查看DataBindingComponent的使用茶袒,自定義DataBindingComponent并設(shè)置自己的BindingAdapter,配合Dagger 2使用更佳)
DataBinding解決的問題
- No findViewById
- 數(shù)據(jù)分發(fā)
DataBinding的性能問題:
- APT生成凉馆,No Reflection
- findViewById需要遍歷整個ViewGroup現(xiàn)在只需要做一次(bind方法薪寓,時間復(fù)雜度為O(n))
- 使用位標(biāo)記來做更新
- 數(shù)據(jù)綁定在下一次批量更新時才會執(zhí)行操作
- 緩存表達式
使用注意
Jake大神很不喜歡DataBinding,因為他認為邏輯代碼不應(yīng)該放在布局文件澜共,很有道理向叉。所以我們在使用時應(yīng)該注意以下兩點:
- 復(fù)雜表達式抽離
- 響應(yīng)方法命名
與各方面技術(shù)的比較
-
前端
:-
vue
: vue.js 則是采用數(shù)據(jù)劫持結(jié)合發(fā)布者-訂閱者模式的方式墓陈,通過Object.defineProperty()來劫持各個屬性的setter回官,getter,在數(shù)據(jù)變動時發(fā)布消息給訂閱者剧防,觸發(fā)相應(yīng)的監(jiān)聽回調(diào)京革。但是目前尤大表示新版將會使用ES6.0中的新特性Proxy+Reflect來實現(xiàn)奇唤,尚未發(fā)行版本幸斥。 -
Angular
:Angular.js是通過臟值檢測的方式比對數(shù)據(jù)是否有變更,來決定是否更新視圖咬扇,最簡單的方式就是通過 setInterval() 定時輪詢檢測數(shù)據(jù)變動甲葬,當(dāng)然Google不會這么low,Angular只有在指定的事件觸發(fā)時進入臟值檢測.(Angular6.0具體是如何實現(xiàn)的并未去關(guān)注冗栗,這是2.0使用的方式)
-
-
iOS
:并沒有成熟的框架演顾,有小團體使用block自己做
DataBinding的實現(xiàn)方式
DataBinding很成熟供搀,是觀察者模式+set方法劫持+臟值檢測的綜合引用隅居。
為什么寫這篇文章
其實在16年底就開始使用了,但是并沒有真的寫一篇文章來記錄一下葛虐,沒有寫筆記的習(xí)慣胎源,其實挺不好的。新?lián)Q了一家公司屿脐,老大給了一張圖讓選擇一個知識點進行分享涕蚤,我選擇了這個。因為公司項目還是傳統(tǒng)的MVP框架的诵,也沒有引入ButterKnife之類的万栅,還需要寫一堆的findViewById之類的,對于我用慣了這些的人來說西疤,真的是很不友好烦粒。所以借此機會寫篇文章水一水。在此感謝我的前同事Neo與我交流前端相關(guān)的東西代赁,并且其實我們都可以認識到現(xiàn)在前端各種輪子其實和Google在推的一些還是有很多共通性的扰她,所以可以多多學(xué)習(xí)一下。
學(xué)習(xí)資料
Data Binding -- Write Apps Faster (Android Dev Summit 2015)
高級數(shù)據(jù)綁定 - 2016谷歌發(fā)布會
Google Developer -> Platform -> Libraries -> Data Binding Library
Google Developer -> Docs -> Fererence -> Databinding Library