本文首發於微信公衆號「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
}
複製代碼
當咱們頁面須要多個不一樣的數據源的時候,若是咱們都是單獨的使用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更新數據。
若是觀察者的生命週期處於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.
})
}
複製代碼