前言
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<T>(
var liveData: NoStickyLiveData<T>,
var observer: Observer<in T>,
sticky: Boolean, observerForever: Boolean = false
) : Observer<T> {
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<T>(
private val eventName: String = "default",
private val map: ConcurrentHashMap<String, NoStickyLiveData<T>>? = null,
private val sticky: Boolean = false
) : MutableLiveData<T>() {
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中重量级组件,使用频率之高,熟练掌握必将大大简化我们的开发任务。 源码:github.com/ThirdPrince…