初學 Android 架構元件之 ViewModel

吳下阿吉發表於2019-02-24

在 Android 中,ActivityFragment 這類 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 元件的生命週期相關聯,如下圖所示:

ViewModel 生命週期(來自官網)

Activity 被系統重建時,ViewModel 物件不會被銷燬,新的 Activity 物件拿到的是同一個 ViewModel 物件。可以很方便的使用 ViewModel 裡的 UI 資料將之前的 UI 狀態呈現給使用者。比如上面的例子,呈現頁面載入狀態。

progressBar.visibility = when (model.loading) {
    true -> VISIBLE
    false -> INVISIBLE
}
複製程式碼

ViewModel 所在的 UI 元件被真正銷燬時,它的 onCleared() 方法會被呼叫,可以覆蓋該方法清理資源。

AndroidViewModel

當自定義的 ViewModel 類中需要使用應用上下文 Context 時,可以選擇繼承 AndroidViewModel 類,該類定義了 Application 欄位。

參考資料

相關文章