MVI 架构
近日,600多名谷歌員工簽署了一份宣言,反對該公司強制接種新冠疫苗。這對谷歌領導層構成了最新挑戰,因為該公司即將迎來讓員工重返辦公室的關鍵最后期限。拜登政府已下令,員工人數在百人以上的美國公司,需要在2022年1月4日前確保員工全面接種疫苗或定期檢測新冠肺炎。泄露的谷歌內部文件顯示,該公司要求其超過15萬名員工在12月3日之前將疫苗接種狀態上傳到其內部系統上,無論他們是否計劃復崗。谷歌還表示,所有直接或間接履行政府合同的員工都必須接種疫苗,即使他們在家工作也是如此。
/ 作者簡介 /
本篇文章來自RicardoMJiang的投稿,文章主要分享了作者對目前架構進行總結并延伸的相關內容,相信會對大家有所幫助!同時也感謝作者貢獻的精彩文章。
/ 前言 /
Android開發發展到今天已經相當成熟了,各種架構大家也都耳熟能詳,如MVC,MVP,MVVM等,其中MVVM更是被官方推薦,成為Android開發中的顯學。
不過軟件開發中沒有銀彈,MVVM架構也不是盡善盡美的,在使用過程中也會有一些不太方便之處,而MVI可以很好的解決一部分MVVM的痛點。
本文主要包括以下內容
MVC,MVP,MVVM等經典架構介紹
MVI架構到底是什么?
MVI架構實戰
需要重點指出的是,標題中說MVI架構是MVVM的進階版是指MVI在MVVM非常相似,并在其基礎上做了一定的改良,并不是說MVI架構一定比MVVM適合你的項目。各位同學可以在分析比較各個架構后,選擇合適項目場景的架構
/ 經典架構介紹 /
MVC架構介紹
MVC是個古老的Android開發架構,隨著MVP與MVVM的流行已經逐漸退出歷史舞臺,我們在這里做一個簡單的介紹,其架構圖如下所示:
圖片
MVC架構主要分為以下幾部分:
視圖層(View):對應于xml布局文件和java代碼動態view部分
控制層(Controller):主要負責業務邏輯,在android中由Activity承擔,同時因為XML視圖功能太弱,所以Activity既要負責視圖的顯示又要加入控制邏輯,承擔的功能過多。
模型層(Model):主要負責網絡請求,數據庫處理,I/O的操作,即頁面的數據來源
由于android中xml布局的功能性太弱,Activity實際上負責了View層與Controller層兩者的工作,所以在android中mvc更像是這種形式:
圖片
因此MVC架構在android平臺上的主要存在以下問題:
Activity同時負責View與Controller層的工作,違背了單一職責原則
Model層與View層存在耦合,存在互相依賴,違背了最小知識原則
MVP架構介紹
由于MVC架構在Android平臺上的一些缺陷,MVP也就應運而生了,其架構圖如下所示:
圖片
MVP架構主要分為以下幾個部分:
View層:對應于Activity與XML,只負責顯示UI,只與Presenter層交互,與Model層沒有耦合
Presenter層:主要負責處理業務邏輯,通過接口回調View層
Model層:主要負責網絡請求,數據庫處理等操作,這個沒有什么變化
我們可以看到,MVP解決了MVC的兩個問題,即Activity承擔了兩層職責與View層與Model層耦合的問題。但MVP架構同樣有自己的問題:
Presenter層通過接口與View通信,實際上持有了View的引用
但是隨著業務邏輯的增加,一個頁面可能會非常復雜,這樣就會造成View的接口會很龐大。
MVVM架構介紹
MVVM 模式將 Presenter 改名為 ViewModel,基本上與 MVP 模式完全一致。唯一的區別是,它采用雙向數據綁定(data-binding):View的變動,自動反映在 ViewModel,反之亦然。
MVVM架構圖如下所示:
圖片
可以看出MVVM與MVP的主要區別在于,你不用去主動去刷新UI了,只要Model數據變了,會自動反映到UI上。換句話說,MVVM更像是自動化的MVP。
MVVM的雙向數據綁定主要通過DataBinding實現,不過相信有很多人跟我一樣,是不喜歡用DataBinding的,這樣架構就變成了下面這樣。
圖片
View觀察ViewModel的數據變化并自我更新,這其實是單一數據源而不是雙向數據綁定,所以其實MVVM的這一大特性我其實并沒有用到
View通過調用ViewModel提供的方法來與ViewModel交互
小結
MVC架構的主要問題在于Activity承擔了View與Controller兩層的職責,同時View層與Model層存在耦合
MVP引入Presenter層解決了MVC架構的兩個問題,View只能與Presenter層交互,業務邏輯放在Presenter層
MVP的問題在于隨著業務邏輯的增加,View的接口會很龐大,MVVM架構通過雙向數據綁定可以解決這個問題
MVVM與MVP的主要區別在于,你不用去主動去刷新UI了,只要Model數據變了,會自動反映到UI上。換句話說,MVVM更像是自動化的MVP。
MVVM的雙向數據綁定主要通過DataBinding實現,但有很多人(比如我)不喜歡用DataBinding,而是View通過LiveData等觀察ViewModle的數據變化并自我更新,這其實是單一數據源而不是雙向數據綁定
/ MVI架構到底是什么 /
MVVM架構有什么不足?
要了解MVI架構,我們首先來了解下MVVM架構有什么不足,相信使用MVVM架構的同學都有如下經驗,為了保證數據流的單向流動,LiveData向外暴露時需要轉化成immutable的,這需要添加不少模板代碼并且容易遺忘,如下所示:
class TestViewModel : ViewModel() {
//為保證對外暴露的LiveData不可變,增加一個狀態就要添加兩個LiveData變量
private val _pageState: MutableLiveData = MutableLiveData()
val pageState: LiveData = _pageState
private val _state1: MutableLiveData = MutableLiveData()
val state1: LiveData = _state1
private val _state2: MutableLiveData = MutableLiveData()
val state2: LiveData = _state2
//…
}
如上所示,如果頁面邏輯比較復雜,ViewModel中將會有許多全局變量的LiveData,并且每個LiveData都必須定義兩遍,一個可變的,一個不可變的。這其實就是我通過MVVM架構寫比較復雜頁面時最難受的點。其次就是View層通過調用ViewModel層的方法來交互的,View層與ViewModel的交互比較分散,不成體系。
小結一下,在我的使用中,MVVM架構主要有以下不足:
為保證對外暴露的LiveData是不可變的,需要添加不少模板代碼并且容易遺忘
View層與ViewModel層的交互比較分散零亂,不成體系
MVI架構是什么?
MVI與MVVM很相似,其借鑒了前端框架的思想,更加強調數據的單向流動和唯一數據源,架構圖如下所示:
圖片
其主要分為以下幾部分:
Model:與MVVM中的Model不同的是,MVI的Model主要指UI狀態(State)。例如頁面加載狀態、控件位置等都是一種UI狀態
View:與其他MVX中的View一致,可能是一個Activity或者任意UI承載單元。MVI中的View通過訂閱Intent的變化實現界面刷新(注意:這里不是Activity的Intent)
Intent:此Intent不是Activity的Intent,用戶的任何操作都被包裝成Intent后發送給Model層進行數據請求
單向數據流
MVI強調數據的單向流動,主要分為以下幾步:
用戶操作以Intent的形式通知Model
Model基于Intent更新State
View接收到State變化刷新UI
數據永遠在一個環形結構中單向流動,不能反向流動:
圖片
上面簡單的介紹了下MVI架構,下面我們一起來看下具體是怎么使用MVI架構的。
/ MVI架構實戰 /
總體架構圖
圖片
我們使用ViewModel來承載MVI的Model層,總體結構也與MVVM類似,主要區別在于Model與View層交互的部分:
Model層承載UI狀態,并暴露出ViewState供View訂閱,ViewState是個data class,包含所有頁面狀態
View層通過Action更新ViewState,替代MVVM通過調用ViewModel方法交互的方式
MVI實例介紹
添加ViewState與ViewEvent
ViewState承載頁面的所有狀態,ViewEvent則是一次性事件,如Toast等,如下所示:
data class MainViewState(val fetchStatus: FetchStatus, val newsList: List)
sealed class MainViewEvent {
data class ShowSnackbar(val message: String) : MainViewEvent()
data class ShowToast(val message: String) : MainViewEvent()
}
我們這里ViewState只定義了兩個,一個是請求狀態,一個是頁面數據
ViewEvent也很簡單,一個簡單的密封類,顯示Toast與Snackbar
ViewState更新
class MainViewModel : ViewModel() {
private val _viewStates: MutableLiveData = MutableLiveData()
val viewStates = _viewStates.asLiveData()
private val _viewEvents: SingleLiveEvent = SingleLiveEvent()
val viewEvents = _viewEvents.asLiveData()
}
如上所示:
我們只需定義ViewState與ViewEvent兩個State,后續增加狀態時在data class中添加即可,不需要再寫模板代碼
ViewEvents是一次性的,通過SingleLiveEvent實現,當然你也可以用Channel當來實現
當狀態更新時,通過emit來更新狀態
View監聽ViewState
private fun initViewModel() {viewModel.viewStates.observe(this) {renderViewState(it)}viewModel.viewEvents.observe(this) {renderViewEvent(it)} }如上所示,MVI使用ViewState對State集中管理,只需要訂閱一個ViewState便可獲取頁面的所有狀態,相對MVVM減少了不少模板代碼。
View通過Action更新State
class MainActivity : AppCompatActivity() {
private fun initView() {
fabStar.setOnClickListener {
viewModel.dispatch(MainViewAction.FabClicked)
}
}
}
class MainViewModel : ViewModel() {
fun dispatch(action: MainViewAction) =
reduce(viewStates.value, action)
}
如上所示,View通過Action與ViewModel交互,通過Action通信,有利于View與ViewModel之間的進一步解耦,同時所有調用以Action的形式匯總到一處,也有利于對行為的集中分析和監控。
/ 總結 /
本文主要介紹了MVC,MVP,MVVM與MVI架構,目前MVVM是官方推薦的架構,但仍然有以下幾個痛點:
MVVM與MVP的主要區別在于雙向數據綁定,但由于很多人(比如我)并不喜歡使用DataBindg,其實并沒有使用MVVM雙向綁定的特性,而是單一數據源
當頁面復雜時,需要定義很多State,并且需要定義可變與不可變兩種,狀態會以雙倍的速度膨脹,模板代碼較多且容易遺忘
View與ViewModel通過ViewModel暴露的方法交互,比較零亂難以維護
而MVI可以比較好的解決以上痛點,它主要有以下優勢:
強調數據單向流動,很容易對狀態變化進行跟蹤和回溯
使用ViewState對State集中管理,只需要訂閱一個ViewState便可獲取頁面的所有狀態,相對 MVVM 減少了不少模板代碼
ViewModel通過ViewState與Action通信,通過瀏覽ViewState和Aciton定義就可以理清ViewModel的職責,可以直接拿來作為接口文檔使用。
當然MVI也有一些缺點,比如:
所有的操作最終都會轉換成State,所以當復雜頁面的State容易膨脹
State是不變的,因此每當State需要更新時都要創建新對象替代老對象,這會帶來一定內存開銷
軟件開發中沒有銀彈,所有架構都不是完美的,有自己的適用場景,讀者可根據自己的需求選擇使用。
但通過以上的分析與介紹,我相信使用MVI架構代替沒有使用DataBinding的MVVM是一個比較好的選擇~
總結
- 上一篇: 华为AR engine 应用开发学习教程
- 下一篇: ElasticSearch第一天