Android下MVVM-Databinding框架
引言
? ? ? ?MVVM和Databinding是當(dāng)前非常流行且實用的框架和工具,相信大家已經(jīng)對MVC、MVP、MVVM框架以及Databinding有一定的了解。本篇文章主要介紹一個基于Databinding封裝的MVVM框架,并且結(jié)合當(dāng)下非?;鸬腁ndroid Architecture Components庫可以實現(xiàn)一種更簡單的MVVM框架。
一、MVVM-Databinding框架背景
? ? ? ?開篇先來了解下MVVM和Databinding及其各自優(yōu)勢,才能說明我們?yōu)槭裁匆晦o辛勞的合二為一,并希望最終達(dá)到1+1>2的效果。
1.? MVVM框架簡介及優(yōu)勢
? ? ? ?MVVM框架類似于早期的MVC和近期的MVP,但是比起這兩個更為強(qiáng)勢。MVVM中的ViewModel層類似MVP的Presenter層,所需要做的完全就是跟邏輯相關(guān)的代碼,完全不會涉及到UI。當(dāng)數(shù)據(jù)變化,直接驅(qū)動UI的改變,中間省去了MVP中冗余的接口。同時,在ViewModel層編寫代碼中,要求開發(fā)者需要將每個方法盡可能的做的功能單一,不與外部有任何的引用或者是聯(lián)系,無形中提高了代碼的健壯性,方便了后期的單元測試。如果想進(jìn)一步了解的話,可以看下我同事寫的文章(https://mp.weixin.qq.com/s/qk9vJcPgcVPYDn2rTTCJ1A)。下圖形象說明了MVC、MVP、MVVM的進(jìn)化過程:

2.? 數(shù)據(jù)綁定框架DataBinding簡介及優(yōu)勢
? ? ? ?DataBinding是谷歌出臺的工具,可以實現(xiàn)UI和數(shù)據(jù)的綁定。我們的View和ViewModel通過DataBinding可以實現(xiàn)單向綁定或雙向綁定,做到UI和數(shù)據(jù)的相互監(jiān)聽。同時開發(fā)者的任務(wù)分配也就很明確了,負(fù)責(zé)ViewModel的小伙伴完全不用考慮UI如何實現(xiàn),很大程度上提高了代碼的開發(fā)效率和后期出問題跟蹤的準(zhǔn)確性,針對這些好處,采用MVVM進(jìn)行代碼開發(fā)還是非常有必要的。當(dāng)然,實現(xiàn)MVVM框架不是只能用DataBinding,可以實現(xiàn)UI和數(shù)據(jù)綁定的框架都可以,像開源框架RoboBinding等,甚至也可以使用Android Architecture Components庫中的Livedata實現(xiàn)手動綁定UI和數(shù)據(jù)。如果您對Databinding不太熟悉,可以看下我寫的這篇入門文章(http://www.itdecent.cn/p/9fb720f405a7)。下圖描述了Databinding數(shù)據(jù)流向:

3.? MVVM-DataBinding框架簡介
? ? ? ?谷歌提供了一個Databinding工具,為什么我們要重新定義一套框架呢?其實在我們使用Databinding的過程中可以發(fā)現(xiàn),單純的使用Databinding使用有幾個痛點(diǎn): 1.Databinding規(guī)則比較多,而且每個人對其理解不同,用起來比較雜亂,導(dǎo)致代碼閱讀性差;2.對于已經(jīng)使用MVP框架的開發(fā)者來說,希望進(jìn)化到MVVM更順滑,那要是能把Presenter改成ViewModel就能完成轉(zhuǎn)化簡直太開心了。3.實現(xiàn)Databinding數(shù)據(jù)和UI綁定的方法(如使用DatabindingUtil)較為機(jī)械且繁瑣。如今并沒有一個好的框架能規(guī)避以上問題,于是我將DataBinding工具和MVVM框架進(jìn)行了封裝,希望能解決以上問題并融合兩者的優(yōu)點(diǎn),最終達(dá)到1+1>2的效果,下面將講述是如何做到的。庫代碼可以移步我的github地址(https://github.com/ganlinux/JDMvvmDatabinding.git)。
二、MVVM-Databinding框架詳解
? ? ? ?下面我將通過三部分介紹我的MVVM-Databinding框架:1.如何使用本框架2.本框架的實現(xiàn)過程。3.本框架的優(yōu)點(diǎn)及可能的隱患。
1、如何使用本框架
? ? ? ?通常我們開發(fā)過程中會在Activity、Fragment、樓層、列表item中使用UI控件,所以我們的框架也將支持這幾種情況的數(shù)據(jù)綁定。俗話說“無規(guī)矩不成方圓”,我們通過定義少量規(guī)則來避免使用Databinding中的大量規(guī)則,進(jìn)而提高代碼可讀性。以下是我們約定的五點(diǎn)規(guī)則:
? ? ? ?①??? 我們約定把Activity、Fragment綁定的數(shù)據(jù)放在xxxViewModel中,樓層綁定的數(shù)據(jù)放在樓層類(代碼中的xxxFloor)中,列表item綁定的數(shù)據(jù)放在ViewHolder中。就是說要綁定的數(shù)據(jù)都會放在其處理邏輯的類中。
? ? ? ?②??? Activity和Fragment的布局文件要做些改變,要綁定的數(shù)據(jù)類名稱(如xxxViewModel)使用"viewModel",其類型為要綁定數(shù)據(jù)的全路徑類名。如下圖示例:

? ? ? ?③? ? 樓層的布局文件中引用的數(shù)據(jù)類使用名稱為"floor",類型也是要綁定數(shù)據(jù)的全路徑類名,如下圖示例:

? ? ? ?④? ? 列表的item布局文件中引用的數(shù)據(jù)類使用名稱為"item",其類型也是要綁定數(shù)據(jù)的全路徑類名。如下圖示例:

? ? ? ?⑤? ? 數(shù)據(jù)的控制邏輯盡量寫在處理邏輯的類中,不要寫在布局文件中,寫在布局文件中會給排查問題造成困難并且造成他人閱讀代碼困難。
? ? ? ?以上是本框架制定的簡單規(guī)則,剩下的工作基本上跟接入MVP框架相似,與MVP不同的就是我們會修改相應(yīng)的布局文件達(dá)到UI和數(shù)據(jù)綁定的目的。以下是接入框架的步驟:
? ? ? ?1)創(chuàng)建ViewModel/樓層/ViewHolder繼承自BaseViewModel/BaseFloorView/BindingViewHolder,里面可以創(chuàng)建要綁定的數(shù)據(jù),例如下圖:

? ? ? ?2)創(chuàng)建layout文件,按照之前的規(guī)則配置要綁定的數(shù)據(jù)。如指定生成的綁定類名為“DemoNormalFragmentBinding”,名稱為"viewModel",其類型為第一步的ViewModel的全路徑,綁定布局和控件例如“android:text="@{viewModel.pageTitle}"”,如圖:

? ? ? ?3)創(chuàng)建Activity、Fragment并繼承BaseBindingMvvmActivity或BaseBindingMvvmFragment,繼承時需要指定綁定的ViewModel類和Binding類,這一步就完成了數(shù)據(jù)和布局UI的綁定。樓層和列表item需要完成的是ViewHolder中數(shù)據(jù)和布局UI的綁定。例如:

? ? ? ?完成了上面這三步,就已經(jīng)完成了布局和數(shù)據(jù)的綁定,你需要做的就只是寫數(shù)據(jù)變化的邏輯了,只要數(shù)據(jù)改變對應(yīng)的UI就會改變了。
? ? ? ?是不是很神奇?比MVP更簡潔吧?
2、本框架的實現(xiàn)過程
? ? ? ?先來看下框架的代碼結(jié)構(gòu),如圖:

? ? ? ?代碼一共分四部分,第一部分為公共的ViewBindingAdapter,主要功能是實現(xiàn)公共自定義控件的綁定注入。第二部分為Activity、Fragment、Viewmodel相關(guān)基類。第三部分為樓層相關(guān)基類。第四部分為Recyclerview列表相關(guān)基類。
? ? ? 這里以樓層框架為例,進(jìn)行相關(guān)實現(xiàn)機(jī)制的講解:
? ? ? 1)創(chuàng)建布局文件,生成BR文件占位資源。BR文件是Databinding編譯期間生成的,跟R文件類似,主要作用是當(dāng)數(shù)據(jù)改變后,可以用該標(biāo)識符通知 DataBinding,用新的數(shù)據(jù)去更新UI。這里占位的目的是在SDK中生成一個通用的名稱,在其他引用SDK的項目中引用。

? ? ? ?2)實現(xiàn)綁定Binding類并設(shè)置數(shù)據(jù),就是這個步驟省去了開發(fā)者手動設(shè)置綁定的工作。其實就是調(diào)用了Databinding的通用綁定方法,這樣就不用開發(fā)者顯示的調(diào)用這個方法了。

? ? ? ?3)由于樓層就是一個Recyclerview的列表組成的,可以繼承基類Adapter ,在onBindViewHolder調(diào)用show方法,并且調(diào)用excutePendingBindings()方法觸發(fā)刷新。

? ? ? ?以上是我樓層實現(xiàn)MVVM-Databinding框架的過程,如果不關(guān)心實現(xiàn)過程的開發(fā)者可以直接看下demo就可以上手了。下圖是樓層demo的實現(xiàn)效果:

3、本框架的優(yōu)點(diǎn)及可能的隱患
? ? ? ?這個框架大部分優(yōu)點(diǎn)其實是結(jié)合了MVVM和Databinding的優(yōu)點(diǎn),如:
? ? ? ?1.減少findViewbyId造成的IO操作性能損耗,這是Databinding的一條優(yōu)點(diǎn),因為Databinding在編譯期間就將控件從布局映射到在Binding類中,只是在綁定階段一次性實例化這些控件就直接可以使用,省去了findViewbyId的IO操作。
? ? ? ?2.減少頻繁線程切換,這條也是由Databinding的優(yōu)點(diǎn),你可以在任何你想要的線程上進(jìn)行更新,因為Databinding是在UI線程進(jìn)行響應(yīng)。
? ? ? ?3.減少邏輯代碼冗余、提高編碼效率,這條優(yōu)點(diǎn)就是本框架的優(yōu)勢所在,也是為什么要將這兩種優(yōu)秀框架集合在一塊的原因。
? ? ? ?4.削減DataBinding的混亂規(guī)則,這是單純的使用DataBinding體驗較差的地方,在我們的框架里可以摒棄其復(fù)雜的規(guī)則。
? ? ? ?5.更簡單的單元測試,這是MVVM框架的優(yōu)點(diǎn),由于其天然的低耦合性,可以使我們單元測試更簡單。
??可能的隱患:
? ? ? ?1.由于DataBinding在xml提供了豐富的操作符,但是由于Android studio天生的xml語法檢查的貧弱,xml布局中的表達(dá)式邏輯錯誤,不能準(zhǔn)確定位,導(dǎo)致debug難度增加,事實上一些BindingAdapter的錯誤在build的時候也會被提示xml錯誤。我在開發(fā)過程中遇到過幾次,常常需要重啟Android studio才能消除問題,這個問題只能寄希望于谷歌在其IDE上解決。
? ? ? ?2.由于DataBinding是在預(yù)編譯時期生成一些布局和代碼,這可能導(dǎo)致許多需要動態(tài)加載資源或代碼的工程(比如插件化工程)需要做些改變。當(dāng)然普通的APP開發(fā)使用這個框架是沒問題的。
? ? ? ?當(dāng)然如果你按照以上步驟改造了自己的APP,可能過程中還會遇到一個問題,就是ViewModel中上下文引用的問題,當(dāng)然如果能用應(yīng)用級別的context當(dāng)然最好,但是有時候我們必須用activity的context時可能就有些困難了,因為我們不推薦ViewModel持有context,這樣做可能造成內(nèi)存泄漏,這時候我們可以結(jié)合Android Architecture Components來實現(xiàn)context的獲取,在我的github的工程中有兩個分支,其中mvvm-databinding-aac分支就是結(jié)合了Android Architecture Components來實現(xiàn)的。
三、結(jié)語
? ? ? ?數(shù)據(jù)綁定的應(yīng)用軟件開發(fā)是一種趨勢,使用DataBinding的優(yōu)點(diǎn)顯而易見。在我們選擇框架的過程中需要考慮諸多問題,比如性能問題、使用便捷程度、單元測試、是否相互獨(dú)立等。當(dāng)然還有一點(diǎn)很重要的就是,共同開發(fā)的同事是否認(rèn)同并喜歡這種開發(fā)方式,畢竟這會影響到他們的開發(fā)流程,習(xí)慣和體驗。如何說服他們認(rèn)可這種框架,就需要你提供便捷和健壯的封裝框架,使他們很爽很開心很便捷地使用這種框架,并且能夠滿足項目代碼的可擴(kuò)展性,解耦和相關(guān)獨(dú)立。做到這些,就能滿足調(diào)用者,項目代碼健壯性,模塊獨(dú)立性和可擴(kuò)展性的需要了。我以上講述算是拋轉(zhuǎn)引玉吧,希望能共同完善框架,共同進(jìn)步。
github地址:https://github.com/ganlinux/JDMvvmDatabinding.git