我們介紹了 SOLID、clean architecture、dependency injection 之後,相信大家寫 code 的時候,都會多想二分鐘怎麼讓程式碼寫得更有架構吧。
差不多是時候回頭來探討 Android 本身的架構問題了,Android 有很多種不同的架構可適用,不論是 MVP、MVI、MVVM,切多少層或是怎麼切、每層之間怎麼互動都有些許不同,但不論哪種架構本質上都是把邏輯從 UI 層獨立出來,讓每一層可以專注於某一種權責,Android 官方並沒有明確訂定什麼才是標準的做法,但我們可以發現在各個場合,MVVM 這個名字一直不斷的被提出來,我們今天就一起來看看 MVVM 到底有什麼過人之處吧!
大家可以想像一下我們的目標是想要把邏輯從 UI 層抽離。
假設抽離出來的這一層叫做 X ,UI 會直接跟 X 互動,可能是跟 X 拿資料或其他需求,有些情境 X 也會需要跟 UI 互動,比如說 api 資料回傳了要通知 UI 更新。
這樣相互依賴的關係恰恰違反了 clean architecture 裡,外層只會依賴內層這種單一方向的原則。
把 X 跟 UI 互動的介面定義一層 interface 出來,就變成 MVP 架構囉!
而在 MVVM 的架構裡,外層只會依賴內層這種守則是必須被嚴格遵守的。
MVVM stands for Model、View、ViewModel,相信 Model 跟 View 大家都不陌生,而 ViewModel
則是 Android 提供的一套 library ,主要用途是讓我們暫存跟管理讓 UI 層使用的資料,ViewModel
不會知道 UI 層的存在,通常是透過 LiveData
或其他類似機制給 UI 層,讓 UI 層註冊得到資料更新的事件。
ViewModel
的另一個優點是因為 Activity
層常會有螢幕旋轉等事件會重建 Activity
,資料必須不斷的儲存再拿出來其實有點麻煩,而透過 ViewModel
可以讓我們安全方便的保存較龐大的資料。
LiveData
是個 lifecycle aware 的 observable 物件。
UI 層透過 LiveData
註冊資料更新的事件通知,當資料有變化的時候就會主動通知註冊的 UI 物件,這種反轉主被動的手法是不是似曾相似呢。
另一個重點是 lifecycle aware,當綁定的 Activity
已經被 destroy 的時候,LiveData
也會自動釋放註冊的物件引用,不會導致潛在的 memory leak 風險。
資料來源:https://developer.android.com/jetpack/docs/guide
MVVM 的架構可以用這張圖來說明,Activity
/Fragment
所在的 UI 層只會知道 ViewModel
的存在,而 ViewModel
則依據不同需求依賴不同的 Repository
獲取資料或是進行其他互動,Repository
則可以依據自己的商業邏輯決定從 Retrofit
跟後端要資料或是直接透過 Room
拿本地端的資料回傳給 ViewModel
,而 UI 層會直接跟 ViewModel
註冊資料更新的通知。
簡而言之,UI 就是控制畫面,而 ViewModel
就是控制資料,藉此達到 UI 與邏輯的切分。
讓我們一起來看個例子吧:
跟以往一樣我們需要宣告 dependency 在 build.gradle
裡:
dependencies {
implementation "androidx.lifecycle:lifecycle-extensions:2.1.0"
}
以下是一個簡單的 ViewModel 範例:
class NameViewModel : ViewModel() {
// Create a LiveData with a String
val currentName: MutableLiveData<String> by lazy {
MutableLiveData<String>()
}
// Rest of the ViewModel...
}
currentName
是一個 MutableLiveData
,可以透過 setValue
設值,當值發生變化時,會主動通知已註冊的物件。
而在 Activity
這一層關於 ViewModel
的使用方法如下:
class NameActivity : AppCompatActivity() {
private lateinit var model: NameViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Other code to setup the activity...
// Get the ViewModel.
model = ViewModelProviders.of(this).get(NameViewModel::class.java)
// Create the observer which updates the UI.
val nameObserver = Observer<String> { newName ->
// Update the UI, in this case, a TextView.
nameTextView.text = newName
}
// Observe the LiveData, passing in this activity as the LifecycleOwner and the observer.
model.currentName.observe(this, nameObserver)
}
}
我們透過 ViewModelProviders.of(this).get(NameViewModel::class.java)
拿到 NameViewModel
的實體,Android 會保證當螢幕旋轉等時候,我們所拿到的 ViewModel
物件會是正確的,透過 observe
function 來等待資料的更新,當資料有更新的時候我們就改變我們的畫面。
這樣就達成了 Activity
知道 ViewModel
的存在、 ViewModel
不知道 Activity
的存在,但是 ViewModel
資料的更新,可以安全的透過 LiveData
通知給 Activity。
挑戰:要如何透過 dependency injection 提供
ViewModel
呢?
挑戰:試著把
Room
的介面改成LiveData
,就可以更無縫的接軌ViewModel
囉!
參考資料:
https://developer.android.com/topic/libraries/architecture/livedata
https://developer.android.com/topic/libraries/architecture/viewmodel
Android 十全大補已經正式出書上架囉!
有興趣的讀者歡迎參考:
https://www.tenlong.com.tw/products/9789864345786