Jetpack:ViewModel來拯救臃腫的Activity or Fragment

以注重生命週期的方式管理和界面相關的數據,Jetpack爲咱們帶來了ViewModel,從本文你能夠學習到使用ViewModel的正確姿式。java

1、ViewModel

ViewModel被用來以注重生命週期的方式來保存數據和管理界面相關的數據。同時能夠在相關配置發生變化是保存數據,例如屏幕旋轉。android

因爲Android framework管理着像Activity、Fragment這樣具備生命週期的UI控制器,因此在用戶的某些操做或者系統分發的某些事件,會致使framework在沒有通過咱們控制下,銷燬和重建UI控制器。例如,屏幕發生旋轉時,framwork會調用Activity的onSaveIntanceState()保存數據,在新Activity的onCreate()方法中恢復這些數據,不過這僅僅是恢復可序列化和反序列化的小數據上,像list或者bitmap這種大數據就不合適了。此外,也會致使大量已經存在的數據被銷燬,而後從新生成,形成資源的浪費。git

LiveData也說到,不要在Activity和Fragment作大量的邏輯操做,會致使代碼臃腫,難以維護,建議把相關邏輯抽到單獨的類進行維護,讓UI控制器負責它們的本質工做。github

爲此,使用ViewModel能夠輕鬆解決以上問題。數據庫

1. 實現ViewModel

Architecture Components提供ViewModel工具類,用來爲UI提供數據。ViewModel對象在配置發生變化時會自動保存數據,而且會在新建的Activity或Fragment實例使用。例如,在APP中須要顯示持有多個user對象的list,應該將請求和保存users數據的動做在ViewModel對象實現,而不是Activity或Fragment。api

class MyViewModel : ViewModel() {
    private val users: MutableLiveData<List<User>> by lazy {
        MutableLiveData().also {
            loadUsers()
        }
    }

    fun getUsers(): LiveData<List<User>> {
        return users
    }

    private fun loadUsers() {
        // Do an asynchronous operation to fetch users.
    }
}
複製代碼

在Activity中訪問數據:bash

class MyActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        // Create a ViewModel the first time the system calls an activity’s onCreate() method.
        // Re-created activities receive the same MyViewModel instance created by the first activity.
        val model = ViewModelProviders.of(this).get(MyViewModel::class.java)
        model.getUsers().observe(this, Observer<List<User>>{ users ->
            // update UI
        })
    }
}
複製代碼

當Activity被重建時,它會接收到來自第一個Activity的ViewModel對象,因此數據不會發生變化。當Activity銷燬後,framework會自動調用ViewModel對象的onCleared()方法來進行釋放資源。記得,ViewModel必定不要持有View、Lifecycle或者任何有Activity context對象的引用。由於ViewModel的生命週期比它們的長,會致使內存泄漏。若是ViewModel須要持有應用的conetxt,例如用來獲取系統服務,能夠繼承AndroidViewModel類,在它的構造方法中對接收應用的context。架構

若是Android Studio找不到ViewModelProviders,添加下面依賴哦。async

api "android.arch.lifecycle:extensions:1.1.1"
複製代碼

2. ViewModel的生命週期

ViewModelProvider獲取ViewModel對象時,ViewModel的生命週期做用域會傳遞給ViewModelProvider,ViewModel會一直保持在內存中,直到其生命週期做用域失效。在下面兩種狀況,ViewModel對象生命週期會失效:一種是Activity對象finished,一種是Fragment對象detached。ide

下圖(圖來自官網)顯示了Activity的生命週期和ViewModel的做用域,第一列顯示了Activity對象的狀態,第二列顯示Activity生命週期方法,第三列ViewModel的做用域。

從圖能夠看出,在系統第一次調用Activity對象的 onCreate()方法,咱們一般要在第一次時間建立ViewModel對象。在Activity對象的生命週期內, onCreate()方法可能因爲系統配置改變而被系統調用屢次,而ViewModel對象只有一次,ViewModel對象會一直存在直到Activity被終結和銷燬掉。

3. 在Fragment之間共享數據

在實際開發中,常常會在兩個或多個fragement對象共享數據,一般作法是實定義接口,由Activity綁定在fragment中。此外,Activity還要處理fragment的建立和可見的狀況。

fragment之間能夠在Activity的做用域內共享同個ViewModel對象來處理這個麻煩的數據交互問題。例如:

class SharedViewModel : ViewModel() {
    val selected = MutableLiveData<Item>()

    fun select(item: Item) {
        selected.value = item
    }
}

class MasterFragment : Fragment() {

    private lateinit var itemSelector: Selector

    private lateinit var model: SharedViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        model = activity?.run {
            ViewModelProviders.of(this).get(SharedViewModel::class.java)
        } ?: throw Exception("Invalid Activity")
        itemSelector.setOnClickListener { item ->
            // Update the UI
        }
    }
}

class DetailFragment : Fragment() {

    private lateinit var model: SharedViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        model = activity?.run {
            ViewModelProviders.of(this).get(SharedViewModel::class.java)
        } ?: throw Exception("Invalid Activity")
        model.selected.observe(this, Observer<Item> { item ->
            // Update the UI
        })
    }
}
複製代碼

因爲兩個Fragment對象綁定在同一個Activity對象,它們經過ViewModelProvider獲取在Activity範圍內的同一個ViewModel對象。

這樣作的好處是:

  • Activity對象不會感知兩個Fragment對象的交互,因此也不用作任何事。
  • Fragment對象不須要知道另一個Fragment對象,除了和ViewModel的約定。當一個對象消失,另一個對象還能正常使用。
  • Fragment對象的生命週期不受其餘的對象影響,一個Fragment對象替換另外一個,UI還能正常運行。

4. ViewModel替代Loader

加載器類像CursorLoader,常常用來保持UI中的數據與數據庫的同步。能夠經過ViewModel,和其餘的類,替換Loader類。使用ViewModel分離UI控制器的數據加載動做,意味着能夠減小更多的類強引用。 使用Loader一個常見的方法,就是在應用程序中使用一個CursorLoader觀察的數據庫內容,當數據庫的值發生變化時,Loader自動從新加載數據和更新用戶界面:

使用ViewModel,並和Room或LiveData一塊兒代替Loader。ViewModel對象在配置發生變化是保持數據,而Room通知LiveData數據庫數據改變,LiveData則將修改後的數據更新到UI上。

其餘更多信息可閱讀官網

總結

Jetpack講到這裏,基本都明白Jetpack是幹嗎了,Jetpack總結咱們日常開發的各類效率和架構,爲咱們提供更標準的組件。Room操做數據庫,LiveData通知數據更改,DataBinding更新View,Lifecycle管理週期,及其後面要講的其餘組件。都在給咱們應用層開發定義一套統一開發架構標準,以即可以開發更好APP。經過這麼一套架構組件,能夠快速開發可維護性高,擴展性好的APP。

Welcom to visit my github

本文是Jetpack系列文章第四篇

第一篇: Jetpack:你還在findViewById麼

第二篇:Jetpack:你如何管理Activity和Fragment的生命週期

第三篇:Jetpack:在數據變化時如何優雅更新Views數據

相關文章
相關標籤/搜索