Android不使用反射,完成LiveDataBus
LiveDataBus大家都很熟悉了,网上也有很多通过反射实现的LiveDataBus。但是通过反射实现的代码比较混乱,也比较难以理解。这里给出一版通过代码实现的。更加的简洁优雅~
首先来看一下LiveData原理
一般我们都是这样使用的,创建一个LiveData去发送数据,在你想观察的地方去注册。这样只要数据发射,你就能拿到你想要的数据了。
image.png
下面就是你再使用红框语句时的调用流程
image.png
所以首先进入 observe 方法看一看
image.png这样我们创建的LifecycleBoundObserver(observe方法中的new Observer
)
就和宿主(observe方法中传入的this
) 建立了联系。
所以宿主每次生命周期的变化都会调用到
LifecycleBoundObserver的onStateChanged
而从代码中也可以看到,在宿主生命周期是DESTROYED时,会主动移除掉当前mObserver,完成自动反注册,这里注意要把mObservers 和 mObserver分清楚
这里有几点需要注意一下
1.LiveData中的mObservers是一个Map,还有一个mVersion字段默认等于 -1
image.png
2.LifecycleBoundObserver 继承 ObserverWrapper
里面有mObserver,其实就是保存自己
还有一个mLastVersion字段,默认等于 -1
image.png
接下来继续进入activeStateChanged方法,其他方法不多解释。
这里直接进入dispatchingValue
可以看到dispatchingValue不管走哪边都会进入considerNotify
image.png image.png
接下来看considerNotify
image.png
看到这里我相信你已经知道,我们为啥能再onChanged拿到数据了
viewModel.liveDataObject.observe(this, new Observer<Bean>() {
@Override
public void onChanged(Bean data) {
接收数据
}
});
接下来看一下,postValue和setValue
可以看到setValue是有注解MainThread的,表示只能在主线程中使用
而postValue没有,把某事件抛到主线程去了
image.png
再来来看一下,postValue在切换到主线程中都干了些啥,我们发现他的Runnable中的方法,最终还是执行了setValue。
image.png
所以这样看
postValue只不过是可以在子线程执行,但是消息发送最终还是要到主线程,且执行setValue
而setValue就只能在主线程执行了
在执行了setValue或者postValue后,mVersion+1,接着直接进入到considerNotify
image.png
黏性事件怎么来的?
为了造成黏性事件,我再注册观察者之前就将数据发送出去,然后通过按钮点击再去注册一个观察者,我们能发现,即使是之前发送的数据,仍然能够接受得到,这就是黏性事件。
image.png
造成的原因就是mLastVersion 和 mVersion
image.png实现自己的LiveDataBus
LiveData基本的都了解过了,接下来自己实现一个,既可以接受黏性事件,又可以接受普通事件。
怎么控制黏性事件
其实原本的代码就是可以发送事件的,只不过不能自由的控制黏性事件,
**如果我们能用一个变量去标志就好了,比如这样标志一个receiveSticky变量**
为true就是接受黏性事件,那么调用方法发送数据
为false的话就会,跳过此方法
if (observer.mLastVersion >= mVersion) {
return;
}
observer.mLastVersion = mVersion;
if(receiveSticky){
observer.mObserver.onChanged((T) mData);
} else {
// 处理普通事件
}
在假设我们能直接在源码添加这个字段的话,那这个receiveSticky从哪里来呢?
1.发送者的角度:从 postValue 和 setValue 入手
比如改写成 postValue(data,receiveSticky)
这样有个弊端,这样只能统一发送黏性或者非黏性,这样如果多个宿主监听同一个消息,而有些需要黏性,有些不需要,这样就很难控制
2.从接收者的角度:从observer入手
我们知道我们传入的observer,在包装成LifecycleBoundObserver后,才有mLastVersion。那我们可以参考一下这种思路
比如:LifecycleBoundObserver包装一下,有了mLastVersion
那么:我们将传入的Observer也包装一层,在创建的时候传入receiveSticky就好了
就像这样:(当然这不是完整版,这只是记录一下思路)
怎么保证接收的是同一个事件
liveData.postValue
viewModel.liveData.observer(this , Observer {
})
一般我们都是这样发送接收的,这个LiveData都是同一个才能接收同一份数据。
所以我们也必须在LiveDataBus保证是同一个LiveData才行。
还是同样的思路,要区分LiveData,就给LiveData加名字就行了呗
那我就再给LiveData包装一层,让调用者传入名字去生成
生成完了就保存下来,以后就用名字去找到对应的LiveData
就好比:
那么他的用法就是这样的:接收消息的有点过于复杂了。
image.png
既然observer是LiveData里面的方法,
而每次发送消息时间都是StickyObserver(sticky, Observer())
这样我们就可以在我们的包装类中去复写一下observer,比如:
这样发送接收数据就会变成,比之前稍微好一些
image.png
如何解决普通事件的接收
在上面我们其实没对普通事件做处理
我们通过sticky能判断接不接受黏性事件
但是我们不知道在我们注册之前,有没有消息事件发送
override fun onChanged(t: T) {
if (sticky) {
observer.onChanged(t)
} else {
// 普通事件
}
}
回想一下,黏性事件是怎么产生的?
简单的认为,observer.mLastVersion(Observer的) < mVersion(LiveData的) 就会产生黏性事件
所以我们也可以模仿一下,弄两个变量去判断
image.png
在我们的LiveDate中
image.png
在我们的observe中会被改写成这样
image.png
所以显然不能完全完全按照源码照抄
那黏性事件之所以会被发送出去,
就是在StickyObserver初始化时mLastVersion
和mLiveDataVersion
没对齐,
导致if (mLastVersion >= stickyLiveData.mLiveDataVersion) {}
没进入
所以进入if条件就有黏性事件,所以我们要改成这样
最后附上整个代码,直接复制就可以用
object LiveDataBus {
// LiveDataBus.with<String>("TestLiveDataBus").postStickyData("测试!")
// LiveDataBus.with<String>("TestLiveDataBus") .observerSticky(this, false) {
//
// }
private val mStickyMap = ConcurrentHashMap<String, StickyLiveData<*>>()
fun <T> with(eventName: String): StickyLiveData<T> {
var stickyLiveData = mStickyMap[eventName]
if (stickyLiveData == null) {
stickyLiveData = StickyLiveData<T>(eventName)
mStickyMap[eventName] = stickyLiveData
}
return stickyLiveData as StickyLiveData<T>
}
/**
* 将发射出去的LiveData包装一下,再做一些数据保存
*/
class StickyLiveData<T>(private var eventName: String) : LiveData<T>() {
var mLiveDataVersion = 0
var mStickyData: T? = null
fun setStickyData(stickyData: T) {
mStickyData = stickyData
setValue(stickyData)
}
fun postStickyData(stickyData: T) {
mStickyData = stickyData
postValue(stickyData)
}
override fun setValue(value: T) {
mLiveDataVersion++
super.setValue(value)
}
override fun postValue(value: T) {
super.postValue(value)
}
fun observerSticky(owner: LifecycleOwner, sticky: Boolean, observer: Observer<in T>) {
// 移除自己保存的StickyLiveData
owner.lifecycle.addObserver(LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_DESTROY) {
mStickyMap.remove(eventName)
}
})
super.observe(owner, StickyObserver(this, sticky, observer))
}
/**
* 重写LiveData的observer,把传入的observer包装一下
*/
override fun observe(owner: LifecycleOwner, observer: Observer<in T>) {
observerSticky(owner,false, observer)
}
}
class StickyObserver<T>(
private val stickyLiveData: StickyLiveData<T>,
private val sticky: Boolean,
private val observer: Observer<in T>
) : Observer<T> {
/**
* 打个比方:
* 一条数据,名称为TestName,
* 对应一个 StickyLiveData, 也就对应一个version, 初始的值为0,且这个可以复用
* 且会创建StickyObserver,对应一个 mLastVersion, 初始的值为0
*
* 如果 StickyLiveData#version 和 StickyObserver#mLastVersion 没有对齐
* LastVersion < version --> 直接发送数据,就会产生黏性事件
*
* 源码就是这样没对齐,所以无法控制黏性事件
*
* 因为源码的流程
* 将传入的observer包装成LifecycleBoundObserver(继承ObserverWrapper)会将传入的observer做保存和保存在hashMap
* 最后在considerNotify遍历hashMap,活跃的观察者会调用observer.onChanged(t)去发送数据
*
* 所以这里把传入的observer包装成StickyObserver 进入源码后 --> 再变成LifecycleBoundObserver
* 所以最终发送数据会调用StickyObserver的onChanged 就可以做黏性事件的处理了
*
*/
private var mLastVersion = stickyLiveData.mLiveDataVersion
override fun onChanged(t: T) {
if (mLastVersion >= stickyLiveData.mLiveDataVersion) {
if (sticky && stickyLiveData.mStickyData != null) {
observer.onChanged(stickyLiveData.mStickyData)
}
return
}
observer.onChanged(t)
}
}
}