在 Android 中,Activity
和 Fragment
這類 UI 元件會被系統銷燬或重建,未特殊處理的 UI 資料將會丟失。以往處理這類問題時,會使用 onSaveInstanceState()
儲存 UI 資料,在 onCreate()
方法裡恢復 UI 資料,但是資料的大小和型別有限制。
看看下面的2個問題:
- 對於因手機 Configuration Changes 而被系統重建的介面,為了呈現之前的 UI 狀態,採用上述的方式來實現起來會顯得過於繁瑣、不優雅。
- 在實踐 Separation of Concerns 準則時,UI 元件主要是負責顯示 UI 資料、響應系統與檢視事件,若再負責資料的載入和管理,會變得臃腫、不易複用。
本文的主角 ViewModel
可以很好地解決這些問題。注意:它不是用來代替 onSaveInstanceState()
。
ViewModel
ViewModel
抽象類被設計成專門儲存和管理 UI 資料,它的定義很簡單,類中只有一個空實現的 onCleared()
。
自定義 ViewModel
在 app 模組的 build.gradle
檔案裡新增依賴。
dependencies {
implementation "androidx.lifecycle:lifecycle-extensions:2.0.0-beta01"
}
複製程式碼
自定義一個 ViewModel
子類 MainViewModel
,它維護一個 UI 狀態:loading
。
class MainViewModel : ViewModel() {
/**
* The flag indicates that the content is loading or not.
*/
val loading = false
}
複製程式碼
在 UI 元件的 onCreate()
生命週期裡,先使用 ViewModelProviders.of()
方法獲取當前 UI 作用域裡的 ViewModelProvider
物件。再通過 ViewModelProvider
類提供的 get()
方法獲取 ViewModel
物件。
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val model = ViewModelProviders.of(this).get(MainViewModel::class.java)
}
}
複製程式碼
生命週期
ViewModel
的生命週期與 UI 元件的生命週期相關聯,如下圖所示:
當 Activity
被系統重建時,ViewModel
物件不會被銷燬,新的 Activity
物件拿到的是同一個 ViewModel
物件。可以很方便的使用 ViewModel
裡的 UI 資料將之前的 UI 狀態呈現給使用者。比如上面的例子,呈現頁面載入狀態。
progressBar.visibility = when (model.loading) {
true -> VISIBLE
false -> INVISIBLE
}
複製程式碼
當 ViewModel
所在的 UI 元件被真正銷燬時,它的 onCleared()
方法會被呼叫,可以覆蓋該方法清理資源。
AndroidViewModel
當自定義的 ViewModel
類中需要使用應用上下文 Context
時,可以選擇繼承 AndroidViewModel
類,該類定義了 Application
欄位。