Android Jetpack架構組件 — LiveData與ViewModel入坑詳解

本文首發於微信公衆號「Android開發之旅」,歡迎關注 ,獲取更多技術乾貨java

前言

前一篇文章咱們講解了Lifecycle的具體使用,爲了更好的理解,本篇咱們將LiveData和ViewModel放在一塊兒講解,經過簡單的Demo來講明兩者之間的協同工做。android

那麼咱們爲何要使用LiveData和ViewModel呢?他們有什麼優點呢?數據庫

在LiveData出現以前,通常狀態分發咱們使用EventBus或者RxJava,這些都很容易出現內存泄漏問題,並且須要咱們手動管理生命週期。而LiveData則規避了這些問題,LiveData是一個持有Activity、Fragment生命週期的數據容器。當數據源發生變化的時候,通知它的觀察者更新UI界面。同時它只會通知處於Active狀態的觀察者更新界面,若是某個觀察者的狀態處於Paused或Destroyed時那麼它將不會收到通知。因此不用擔憂內存泄漏問題。bash

ViewModel將視圖和邏輯進行了分離。Activity或者Fragment只負責UI顯示部分。具體的網絡請求或者數據庫操做則有ViewModel負責。這樣避免了視圖的臃腫和代碼的耦合。經過下面這張ViewModel生命週期能夠看出,當屏幕發生旋轉而致使Activity被銷燬並從新建立時,ViewModel並無被銷燬,從而幫助咱們在Activity從新建立後獲取數據更新UI。它和onSaveInstanceState方法相比更有優點,由於onSaveInstanceState方法只不適合大量數據的恢復操做,只能恢復少許而且被序列化和反序列化的數據,而ViewModel不只支持大量數據,還不須要序列化、反序列化操做。微信

基本用法

首先咱們在build.gradle中添加依賴:網絡

def lifecycle_version = "2.1.0"
// ViewModel and LiveData
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"/
複製代碼

而後咱們建立ViewModel類,建立類繼承ViewModel,並在其中建立LiveData:架構

class SecondViewModel : ViewModel() {

    var userData: MutableLiveData<UserInfo> = MutableLiveData()

    /**
     *  模擬獲取數據
     */
    fun getUserInfo() {

        val user = UserInfo("李四",(1000..5000).random())
        userData.postValue(user)

    }
}
複製代碼

由於LiveData是抽象類,MutableLiveData是它的一個實現類。其中定義了postValue和setValue用來通知觀察者更新數據。postValue爲異步操做。setValue爲同步操做。app

接着咱們在Activity中建立ViewModel並將UI組件和LiveData進行綁定以便進行數據的更新。dom

class SecondActivity : AppCompatActivity() {
 
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContentView(R.layout.activiry_second)

        val secondViewModel = ViewModelProviders.of(this).get(SecondViewModel::class.java)

        secondViewModel.userData.observe(this, Observer {
            mTvShow.text = "姓名:${it.name} \n薪水:${it.salary}"
        })

        /**
         * 模擬數據源更新
         */
        mBtnData.setOnClickListener {
            secondViewModel.getUserInfo()
        }
    }
}
複製代碼

這裏經過ViewModelProviders來獲取ViewModelProvider在經過get方法來獲取ViewModel實例。咱們經過button按鈕註冊了一個點擊事件來模擬數據源數據的更新。異步

數據轉換

若是咱們想對從服務端獲取到的數據進行修改能夠使用Transformations操做符。分爲Map和switchMap兩種。switchMap須要返回一個LiveData對象。

Transformations.map

Transformations.map(secondViewModel.userData, object : Function<UserInfo, UserInfo> {
    override fun apply(userInfo: UserInfo): UserInfo {
        userInfo.name = "張三"
        return userInfo
    }

}).observe(this, Observer {
    mTvShow.text = "姓名:${it.name} \n薪水:${it.salary}"
})
複製代碼

這就將name從新賦值爲「張三」。

Transformations.switchMap

好比咱們有些數據須要依賴其餘數據進行查詢,就能夠使用switchMap。

Transformations.switchMap(secondViewModel.userData, object : Function<UserInfo, LiveData<UserInfo>> {
        override fun apply(userInfo: UserInfo): LiveData<UserInfo> {
            return secondViewModel.getUserName(userInfo)
        }
    }).observe(this, Observer {
    mTvShow.text = "姓名:${it.name} \n薪水:${it.salary}"
})
複製代碼

在ViewModel中定義getUserName方法

fun getUserName(userInfo: UserInfo): LiveData<UserInfo> {
    userInfo.name = userInfo.name + "switchMap"
    switchMapData.value = userInfo
    return switchMapData
}
複製代碼

MediatorLiveData

當咱們頁面須要多個不一樣的數據源的時候,若是咱們都是單獨的使用LiveData,會致使Activity中定義不少observe,出現不少多餘的代碼。MediatorLiveData就爲解決這個問題的。它能夠將多個LiveData合併在一塊兒,只須要定義一次observe就能夠。

var data1: MutableLiveData<UserInfo> = MutableLiveData()
var data2: MutableLiveData<UserInfo> = MutableLiveData()
var mediatorLiveData: MediatorLiveData<UserInfo> = MediatorLiveData()

val user1 = UserInfo("李四1", (1000..5000).random())
val user2 = UserInfo("李四2", (1000..5000).random())

data1.postValue(user1)
data2.postValue(user2)

mediatorLiveData.addSource(data1, object : Observer<UserInfo> {
    override fun onChanged(info: UserInfo) {
        mediatorLiveData.value = info
    }

})

mediatorLiveData.addSource(data2, object : Observer<UserInfo> {
    override fun onChanged(info: UserInfo) {
        mediatorLiveData.value = info
    }

})   
    
複製代碼

這個咱們定義了兩個MutableLiveData表示正常的數據獲取。MediatorLiveData經過addSource方法將data1和data2合併一塊兒組成新的LiveData。onChanged回調錶示的是當data1和data2數據源數據發送變化的時候進行回調。通知界面UI進行數據刷新。

咱們在Activity中使用:

secondViewModel.mediatorLiveData.observe(this, Observer {
    if (it.name.contains("1")) {
        mTvShow.text = "姓名:${it.name} \n薪水:${it.salary}"
    } else {
        mTvShowOther.text = "姓名:${it.name} \n薪水:${it.salary}"
    }

})
複製代碼

這裏就簡單的經過name來判斷不一樣的數據源類型。這樣就將data1和data2首次請求的數據在界面展現了。上文也講了onChanged是監聽data1和data2數據變化的。這裏咱們在XML佈局文件中新加一個button按鈕用來模擬數據源變化。

而後在ViewModel中第一模擬刷新方法:

/**
* 模擬data1和data2數據源改變
*/
fun update() {

val updateUser1 = UserInfo("李四1 update", (1000..5000).random())
val updateUser2 = UserInfo("李四2 update", (1000..5000).random())

data1.postValue(updateUser1)
data2.postValue(updateUser2)

}
複製代碼

postValue執行完成後onChanged方法將接受到回調並通知UI更新數據。

擴展LiveData

若是觀察者的生命週期處於STARTED或RESUMED狀態,則LiveData認爲觀察者處於活動狀態。若是咱們知道何時是active和inactive,那麼咱們能夠本身實現LiveDta。因此LiveData提供了兩個方法,分別爲onActive()與onInactive()。並給出了官方Demo:

class StockLiveData(symbol: String) : LiveData<BigDecimal>() {
    private val stockManager = StockManager(symbol)

    private val listener = { price: BigDecimal ->
        value = price
    }

    override fun onActive() {
        stockManager.requestPriceUpdates(listener)
    }

    override fun onInactive() {
        stockManager.removeUpdates(listener)
    }
}
複製代碼

onActive方法被調用說明如今有活動着的觀察者,因此添加監聽以進行數據的更新。onInactive方法被調用說明沒有活動的觀察者因此要移除監聽。

咱們使用StockLiveData:

class MyFragment : Fragment() {

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        StockLiveData.get(symbol).observe(this, Observer<BigDecimal> { price: BigDecimal? ->
            // Update the UI.
        })

    }
複製代碼

掃描下方二維碼關注公衆號,獲取更多技術乾貨。

推薦閱讀

還不知道Android Jetpack是什麼?你就out了

Android Jetpack架構組件 — Lifecycle入坑指南

Android Jetpack架構組件 — Room入坑詳解

相關文章
相關標籤/搜索