本文首發於微信公衆號「Android開發之旅」,歡迎關注 ,獲取更多技術乾貨
前一篇文章咱們講解了Lifecycle的具體使用,爲了更好的理解,本篇咱們將LiveData和ViewModel放在一塊兒講解,經過簡單的Demo來講明兩者之間的協同工做。java
那麼咱們爲何要使用LiveData和ViewModel呢?他們有什麼優點呢? android
在LiveData出現以前,通常狀態分發咱們使用EventBus或者RxJava,這些都很容易出現內存泄漏問題,並且須要咱們手動管理生命週期。而LiveData則規避了這些問題,LiveData是一個持有Activity、Fragment生命週期的數據容器。當數據源發生變化的時候,通知它的觀察者更新UI界面。同時它只會通知處於Active狀態的觀察者更新界面,若是某個觀察者的狀態處於Paused或Destroyed時那麼它將不會收到通知。因此不用擔憂內存泄漏問題。數據庫
ViewModel將視圖和邏輯進行了分離。Activity或者Fragment只負責UI顯示部分。具體的網絡請求或者數據庫操做則有ViewModel負責。這樣避免了視圖的臃腫和代碼的耦合。經過下面這張ViewModel生命週期能夠看出,當屏幕發生旋轉而致使Activity被銷燬並從新建立時,ViewModel並無被銷燬,從而幫助咱們在Activity從新建立後獲取數據更新UI。它和onSaveInstanceState方法相比更有優點,由於onSaveInstanceState方法只不適合大量數據的恢復操做,只能恢復少許而且被序列化和反序列化的數據,而ViewModel不只支持大量數據,還不須要序列化、反序列化操做。segmentfault
首先咱們在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爲同步操做。架構
接着咱們在Activity中建立ViewModel並將UI組件和LiveData進行綁定以便進行數據的更新。app
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按鈕註冊了一個點擊事件來模擬數據源數據的更新。dom
若是咱們想對從服務端獲取到的數據進行修改可使用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. }) }
Android Jetpack架構組件 — Lifecycle入坑指南
Android Jetpack架構組件 — Room入坑詳解