在學(xué)習(xí)一個(gè)技術(shù)之前,我們首先要搞清為什么要用它、用它以后會(huì)有什么好處,這樣我們才能有興趣的學(xué)習(xí)下去。
一、為什么要用MVVM?
我為什么要用這個(gè)什么MVVM,我就平常寫(xiě)和它有什么不同嗎?
首先我們要說(shuō)一下,使用MVVM后,程序會(huì)有哪些變化:
1.MVVM并不會(huì)提升程序的性能,甚至如果用不好還會(huì)降低性能。
2.MVVM會(huì)增大代碼的總量。
3.閱讀MVVM的代碼你必須不停的跳來(lái)跳去,跳到你惡心想吐。
啥?你在搞笑嗎?那我為什么還要用它?且慢,我還沒(méi)說(shuō)最重要的一條。
4.MVVM將讓你的程序完全解耦。當(dāng)然,是正確使用的前提下。
為了最后這一條,我們用前3條的犧牲完全不過(guò)分,真正寫(xiě)代碼的時(shí)候,我們面對(duì)的往往不是性能的些許提升,也不是幾十或幾百K的包大小增大帶來(lái)的問(wèn)題,而是程序不停迭代所帶來(lái)的程序穩(wěn)定性要求。而這也是MVVM大顯身手的地方,MVVM能夠完全讓你的業(yè)務(wù)功能組件化,讓我們需要什么就調(diào)什么,并且組件可以在不同頁(yè)面之間復(fù)用;在寫(xiě)業(yè)務(wù)組件的時(shí)候,我們可以完全集中精力,只管寫(xiě)組件要完成的功能,而無(wú)需分神它顧。
讀到這,相信你應(yīng)該對(duì)MVVM稍微有點(diǎn)興趣了吧。
二、MVVM到底是什么?
MVVM,是Model、View、ViewModel三者的縮寫(xiě),它是一種程序的設(shè)計(jì)框架,是一種設(shè)計(jì)思路。不同的人實(shí)現(xiàn)MVVM,所用的構(gòu)成技術(shù)、實(shí)現(xiàn)以后的性能都是不一樣的。千萬(wàn)不要覺(jué)得它有多深?yuàn)W,他其實(shí)就是由一系列代碼(或技術(shù))構(gòu)成的一個(gè)程序的底座。我們?cè)诹己玫鬃祥_(kāi)發(fā)出來(lái)的程序穩(wěn)定性更高,可擴(kuò)展性更強(qiáng)。
下面我們挨個(gè)來(lái)來(lái)說(shuō)Model、View、ViewModel。
1.Model:數(shù)據(jù)提供。
Model在程序中專門用于提供數(shù)據(jù),不管是網(wǎng)絡(luò)請(qǐng)求獲得的數(shù)據(jù),還是數(shù)據(jù)庫(kù)獲得的數(shù)據(jù),統(tǒng)統(tǒng)寫(xiě)在Model里。Model層獨(dú)立性相當(dāng)強(qiáng),它只用來(lái)提供數(shù)據(jù),而不管數(shù)據(jù)是用來(lái)做什么的。
2.View:視圖元素和視圖元素初始化。
View在Android中指代的就是我們常見(jiàn)的布局文件和Activity中的元素初始化部分??傊?,所有一切我們?cè)贏ndroid上肉眼能看見(jiàn)的東西都是View。在View層里,我只對(duì)UI做初始化,比如將TextView設(shè)置字體大小,為Banner控件設(shè)置滾動(dòng)速度等等,這些大多可以直接在布局文件中完成。
3.ViewModel:操作業(yè)務(wù)數(shù)據(jù),并將數(shù)據(jù)呈現(xiàn)在View上。
ViewModel根據(jù)業(yè)務(wù)需要,從Model層調(diào)取相關(guān)數(shù)據(jù),然后更新View層相關(guān)元素。
說(shuō)起來(lái)有點(diǎn)抽象,別急,下面我們用一個(gè)簡(jiǎn)單的例子來(lái)解釋它們?cè)鯓踊ハ嗯浜?,你很快就?huì)明白。
在例子中,我們使用Google提供的DataBinding技術(shù)來(lái)完成數(shù)據(jù)綁定,以實(shí)現(xiàn)View和ViewModel層的交互。
【??時(shí)間】

例子非常非常簡(jiǎn)單,一個(gè)倉(cāng)庫(kù)管理的頁(yè)面,一共三個(gè)元素,其中更新時(shí)間和倉(cāng)庫(kù)物品列表是從服務(wù)器動(dòng)態(tài)獲取的。
平常寫(xiě)的話,我們會(huì)在Activity內(nèi)完成所有的操作,先進(jìn)行網(wǎng)絡(luò)請(qǐng)求,得到數(shù)據(jù)后為TextView設(shè)置文字、為RecycleView設(shè)置Adapter。但使用了MVVM后,代碼將被拆分為三個(gè)部分,我們按View、ViewModel、Model的順序直接上代碼,隨后在一一解釋:
View層:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="stockViewModel"
type="com.ben.shop.viewmodel.StockManagementViewModel" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
android:gravity="center"
android:padding="@dimen/dp_5"
android:text="倉(cāng)庫(kù)管理系統(tǒng)"
android:textStyle="bold" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_10"
android:gravity="center"
android:text="@{stockViewModel.bindingData.component2()}"
android:textColor="@color/red"
android:textSize="@dimen/sp_15" />
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:stockGoodsViewLoader="@{stockViewModel.bindingData.component1()}" />
</LinearLayout>
</layout>
ViewModel層:
class StockManagementViewModel(mvvmViewModel: MVVMViewModel?) : BaseViewModel(mvvmViewModel) {
fun load() {
val newData = getModel(StockModel::class.java).getStockList()
val time = getModel(StockModel::class.java).getUpdateTime()
bindingData.data.clear()
bindingData.data.addAll(newData)
bindingData.updateTime = "更新時(shí)間:${time}"
bindingData.notifyChange()
}
override fun onResumed() {
super.onResumed()
load()
}
val bindingData: BindingData = BindingData(mutableListOf(), "")
data class BindingData(
@Bindable val data: MutableList<StockGood>,
@Bindable var updateTime: String
) : BaseObservable()
}
@BindingAdapter("android:stockGoodsViewLoader")
fun stockGoodsViewLoader(recyclerView: RecyclerView, data: List<StockGood>) {
recyclerView.apply {
layoutManager = LinearLayoutManager(recyclerView.context).apply {
orientation = LinearLayoutManager.VERTICAL
}
adapter = StockManagementAdapter(recyclerView.context, data)
}
}
Model層:
class StockModel(mvvmModel: MVVMModel?) : HttpModel(mvvmModel) {
fun getStockList(): List<StockGood> {
return mutableListOf<StockGood>().apply {
add(StockGood("1", "鉗子", 3))
add(StockGood("2", "扳手", 12))
}
}
fun getUpdateTime(): String {
return Utils.DateUtil.getCurrentlyTimeByFormatter(Constant.DateFormat4)
}
}
View層在我們這個(gè)例子中只有一個(gè)布局文件,布局文件是DataBinding的標(biāo)準(zhǔn)寫(xiě)法,在<data></data>標(biāo)簽中寫(xiě)好控件需要的ViewModel。
<data>
<variable
name="stockViewModel"
type="com.ben.shop.viewmodel.StockManagementViewModel" />
</data>
然后將TextView和RecyclerView與ViewModel中的元素綁定好。
android:text="@{stockViewModel.bindingData.component2()}"
android:stockGoodsViewLoader="@{stockViewModel.bindingData.component1()}"
View沒(méi)看明白沒(méi)關(guān)系,先來(lái)看ViewModel。
還記得ViewModel的作用嗎,我來(lái)重復(fù)一遍:它從Model層獲取數(shù)據(jù),然后更新視圖。
這里ViewModel實(shí)現(xiàn)了Activity的生命周期函數(shù),onResumed是ViewModel開(kāi)始執(zhí)行的地方
調(diào)用Model獲取數(shù)據(jù):
val newData = getModel(StockModel::class.java).getStockList()
val time = getModel(StockModel::class.java).getUpdateTime()
更新視圖
bindingData.data.clear()
bindingData.data.addAll(newData)
bindingData.updateTime = "更新時(shí)間:${time}"
bindingData.notifyChange()
這里注意, notifyChange方法是BaseObservable中的方法,用于通知視圖刷新
因?yàn)槲覀円呀?jīng)在View層將數(shù)據(jù)綁定到了data和updateTime兩個(gè)變量上,因此,我們只需要更新這兩個(gè)變量,就可以完成View層視圖的刷新,這也正是MVVM強(qiáng)大的地方。從這里就可以看出,View層和ViewModel層是高度解耦的,只通過(guò)兩個(gè)變量相互關(guān)聯(lián),這樣,ViewModel就有了非常高的獨(dú)立性,可以輕而易舉的被其他View調(diào)用,也可以從例子中的View層瞬間剔除,就像插上或拔出一個(gè)U盤那樣簡(jiǎn)單。
Model層比較容易理解,由于只是一個(gè)例子,我們?cè)谶@里并沒(méi)有請(qǐng)求網(wǎng)絡(luò),實(shí)際開(kāi)發(fā)過(guò)程中,你只需要使用你想使用的網(wǎng)絡(luò)工具請(qǐng)求網(wǎng)絡(luò)或調(diào)用數(shù)據(jù)庫(kù),獲取到數(shù)據(jù)后返回給ViewModel層即可。
這里值得一題的是Activity中的代碼:
class StockManagementActivity : MVVMActivity<ActibityStockManagementBinding>() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentViewByDataBinding(R.layout.actibity_stock_management)
dataBinding.stockViewModel = getViewModel(StockManagementViewModel::class.java)
}
}
可以看到,Activity中幾乎沒(méi)有邏輯代碼,僅僅有一行來(lái)實(shí)例化ViewModel。MVVM就這樣,將業(yè)務(wù)邏輯完整從Activity中拆分為獨(dú)立的三塊,不僅解放了Activity,更使整個(gè)代碼的邏輯清晰,可讀性極強(qiáng),并且功能塊之間互不干擾,這也就實(shí)現(xiàn)了我們開(kāi)頭所說(shuō)的組件化開(kāi)發(fā)。
三、總結(jié)
怎么樣,MVVM還不錯(cuò)吧?如果您曾經(jīng)了解過(guò)MVC、MVP,相信本文一定很容易理解,MVVM只不過(guò)的它們的“升級(jí)版”,實(shí)現(xiàn)過(guò)程不同,目標(biāo)卻是相同的。這些設(shè)計(jì)思想并不是十分深?yuàn)W的東西,本質(zhì)就是讓軟件質(zhì)量變得更好,如果不明白它們的原理,不僅不會(huì)讓代碼質(zhì)量提高,反而會(huì)使程序變得不知所云,得不償失。
另外說(shuō)一下,本文主講MVVM,DataBinding的用法和原理沒(méi)有過(guò)多涉及,但相信看過(guò)本文以后,您應(yīng)該對(duì)它也有了一定的了解了吧。我開(kāi)頭說(shuō)過(guò),MVVM實(shí)現(xiàn)的技術(shù)各不相同。DataBinding是Goodle出品,并且Android Studio中支持的相當(dāng)不錯(cuò),因此被我采用。在了解了MVVM的機(jī)制以后,你完全可以選擇你想使用的技術(shù),構(gòu)建你自己的MVVM框架。如果各位有興趣,我可以寫(xiě)一個(gè)關(guān)于DataBinding的文章。當(dāng)然網(wǎng)絡(luò)上關(guān)于DataBinding的也很多,大家也可以作為參考。
小生才疏學(xué)淺,文中必有遺漏和錯(cuò)誤,十分歡迎閱讀過(guò)的同學(xué)指正,大家一起討論!