Android Jetpack 架構組件之ViewModel 與 LiveData
前言
ViewModel 和 LiveData 作為JetPack 中架構組件中重要的組件,搭配好使用能大大提升開發效率。
ViewModel
ViewModel 具有生命週期意識,會自動存儲和管理 UI 相關的數據,即使設備配置發生變化後數據還會存在,ViewModel的出現會讓讓Activity專注於視圖控制器的角色,業務邏輯交給ViewModel,很好地將視圖與邏輯分離開來。不止於此,ViewModel還能在Fragment之間通信。
ViewModel 在Activity 之間通信
ViewModel 能在Framgment 通信,這很好理解,因為他們有共同的載體Activity,就能創建相同的ViewModel實例:
``` private val shareViewModel by lazy { ViewModelProvider(requireActivity()).get(ShareViewModel::class.java) }
``` 每點擊一次HomeFragment我都會延遲更新數據。
``` class ShareViewModel :ViewModel() {
val data = MutableLiveData<String>()
var count = 0
fun getData(){
Handler(Looper.getMainLooper()).postDelayed({
count++
data.value = "from the activity$count"
},2000)
data.value = "from the activity$count"
}
} ```
然後每個Fragment 監聽自己的數據源就可以:
shareViewModel.data.observe(viewLifecycleOwner, Observer {
textView.text = it
})
然而好像實際開發中,跨Activity共享數據的情況更多,這個時候又該怎麼處理呢?
ViewModelProvider接收的是ViewModelStoreOwner子類對象,Activity 和 Fragment都實現了ViewModelStoreOwner接口。想要跨Activity共享數據,我們讓Application實現ViewModelStoreOwner接口,通過Application來創建ViewModel不就能實現跨Activity通信的問題,代碼如下:
``` class MyApp : Application(), ViewModelStoreOwner {
private val TAG = "MyApp"
private val appViewModelStore: ViewModelStore by lazy {
ViewModelStore()
}
override fun onCreate() {
super.onCreate()
AppScope.init(this)
}
override fun onTerminate() {
super.onTerminate()
appViewModelStore.clear()
}
override fun getViewModelStore(): ViewModelStore {
return appViewModelStore
}
} ```
其中 AppScope:
``` object AppScope { private lateinit var myApp: MyApp fun init(application: MyApp){ myApp = application }
/**
* 獲取進程共享的ViewModel
*/
fun <T : ViewModel?> getAppScopeViewModel(modelClass: Class<T>): T {
return ViewModelProvider(myApp).get(modelClass)
}
} ``` 這裏我們做個小Demo,SecActivity 啟動編輯頁面ThirdActivity,編輯成功後,數據返回 SecActivity。 它們使用共同的EditViewModel,創建方式如下:
private val editViewModel: EditViewModel by lazy {
AppScope.getAppScopeViewModel(EditViewModel::class.java)
}
監聽數據變化
editViewModel.inputData.observe(this, Observer {
it.let {
tv?.text = it
}
})
運行一把:
進入編輯頁面後,編輯5689,關閉頁面,數據確實傳遞到了SecActivity,共享數據成功,但是也帶來了新的問題,重新進入SecActivity,依然接收了原來了的數據,這是LiveData支持粘性事件導致的,接下來我們談談LiveData。
LiveData
LiveData 天生支持粘性事件,google 設計LiveData 並不是為了粘性而設計,但卻有粘性的效果。
LiveData 取消粘性事件
LiveData支持粘性事件的原因是 observer version 與 LiveData 的version沒有保持一致性,Observer 每次的初始值為-1,這樣因為
``` if (observer.mLastVersion >= mVersion) { return; } observer.mLastVersion = mVersion; observer.mObserver.onChanged((T) mData);
```
只要我們重寫Observer,讓它的Version和LiveData的version保持一致,將不會把把歷史數據回調新的註冊者,也就取消了粘性事件,重新定義WrapperObserver類:
```
/*
* Observer 包裝類
* 通過改變mLastVersion的值就能做到非粘性事件
*
/
class WrapperObserver
private val TAG = "WrapperObserver"
//標記該liveData已經發射幾次數據了,用以過濾老數據重複接收
private var mLastVersion = if (sticky) {
-1
} else {
liveData.getVersion()
}
override fun onChanged(t: T) {
if (mLastVersion >= liveData.getVersion()) {
return
}
mLastVersion = liveData.getVersion()
observer?.onChanged(t)
}
} ```
自定義LiveData:
```
class NoStickyLiveData
private val TAG = "StickyLiveData"
private var mVersion = 0
/**
* 記錄 綁定的Observer
*/
private val mHashMap = ConcurrentHashMap<String, Observer<*>>()
fun getVersion(): Int {
return mVersion
}
override fun setValue(value: T) {
mVersion++
super.setValue(value)
}
override fun postValue(value: T) {
mVersion++
super.postValue(value)
}
override fun observeForever(observer: Observer<in T>) {
val observerExit = mHashMap[eventName]
if (observerExit != null) {
removeObserver(observerExit as Observer<in T>)
}
val wrapperObserver = WrapperObserver(this, observer, sticky, true)
mHashMap[eventName] = wrapperObserver
super.observeForever(wrapperObserver)
}
override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
observerSticky(owner, observer, sticky)
}
private fun observerSticky(owner: LifecycleOwner, observer: Observer<in T>, sticky: Boolean) {
super.observe(owner, WrapperObserver(this, observer, sticky))
owner.lifecycle.addObserver(LifecycleEventObserver { source, event ->
if (event == Lifecycle.Event.ON_DESTROY) {
map.let {
if (!sticky) {
it?.remove(eventName)
}
}
}
})
}
private fun observerSticky(observer: Observer<in T>, sticky: Boolean) {
super.observeForever(WrapperObserver(this, observer, sticky))
}
}
監聽:
editViewModel.inputDataNoSticky.observe(this, Observer {
it.let {
tv?.text = it
}
})
```
效果如下圖:
LiveData定製事件總線
LiveData強大的事件分發能力,可以根據LiveData來做一個事件總線,用來全局分發事件。
並且支持粘性事件和非粘性事件兩種方式。
發送事件:
LiveDataBus.withSticky<String>("edit").setValue("********")
監聽:
LiveDataBus.withSticky<String>("edit").observe(this, Observer {
it.let {
tv?.text = it
}
})
LiveDataBus 源碼:
``` /* * 消息總線 * 跨 activity / object LiveDataBus {
private val mHashMap = ConcurrentHashMap<String, NoStickyLiveData<*>>()
/**
* 不帶粘性事件
*/
fun <T> with(eventName: String): NoStickyLiveData<T> {
var liveData = mHashMap[eventName]
if (liveData == null) {
liveData =
NoStickyLiveData(
eventName,
mHashMap as ConcurrentHashMap<String, NoStickyLiveData<T>>
)
mHashMap[eventName] = liveData
}
return liveData as NoStickyLiveData<T>
}
/**
* 帶粘性事件的
*/
fun <T> withSticky(eventName: String): NoStickyLiveData<T> {
var liveData = mHashMap[eventName]
if (liveData == null) {
liveData =
NoStickyLiveData(
eventName,
mHashMap as ConcurrentHashMap<String, NoStickyLiveData<T>>,
true
)
mHashMap[eventName] = liveData
}
return liveData as NoStickyLiveData<T>
}
} ``` LiveDataBus 粘性事件:
LiveDataBus 非粘性事件:
總結
ViewModel和LiveData是JetPack中重量級組件,使用頻率之高,熟練掌握必將大大簡化我們的開發任務。 源碼:http://github.com/ThirdPrince/AppScopViewModel