注册

Android不使用反射完成LiveDataBus

LiveDataBus大家都很熟悉了,网上也有很多通过反射实现的LiveDataBus。但是通过反射实现的代码比较混乱,也比较难以理解。这里给出一版通过代码实现的。更加的简洁优雅~

首先来看一下LiveData原理

一般我们都是这样使用的,创建一个LiveData去发送数据,在你想观察的地方去注册。这样只要数据发射,你就能拿到你想要的数据了。 

下面就是你再使用红框语句时的调用流程 

先进入 observe 方法看一看

 这样我们创建的LifecycleBoundObserver(observe方法中的new Observer)就和宿主(observe方法中传入的this) 建立了联系。

所以宿主每次生命周期的变化都会调用到 LifecycleBoundObserver的onStateChanged 而从代码中也可以看到,在宿主生命周期是DESTROYED时,会主动移除掉当前mObserver,完成自动反注册,这里注意要把mObservers 和 mObserver分清楚

这里有几点需要注意一下 1.LiveData中的mObservers是一个Map,还有一个mVersion字段默认等于 -1 

2.LifecycleBoundObserver 继承 ObserverWrapper 里面有mObserver,其实就是保存自己 还有一个mLastVersion字段,默认等于 -1 

接下来继续进入activeStateChanged方法,其他方法不多解释。 这里直接进入dispatchingValue 可以看到dispatchingValue不管走哪边都会进入considerNotify 

接下来看considerNotify 

看到这里我相信你已经知道,我们为啥能再onChanged拿到数据了

    viewModel.liveDataObject.observe(this, new Observer<Bean>() {
@Override
public void onChanged(Bean data) {
接收数据
}
});

接下来看一下,postValue和setValue

可以看到setValue是有注解MainThread的,表示只能在主线程中使用
而postValue没有,把某事件抛到主线程去了 

再来来看一下,postValue在切换到主线程中都干了些啥,我们发现他的Runnable中的方法,最终还是执行了setValue。 

所以这样看 postValue只不过是可以在子线程执行,但是消息发送最终还是要到主线程,且执行setValue 而setValue就只能在主线程执行了

在执行了setValue或者postValue后,mVersion+1,接着直接进入到considerNotify 

黏性事件怎么来的?

为了造成黏性事件,我再注册观察者之前就将数据发送出去,然后通过按钮点击再去注册一个观察者,我们能发现,即使是之前发送的数据,仍然能够接受得到,这就是黏性事件。 

造成的原因就是mLastVersion 和 mVersion

实现自己的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

就好比:

那么他的用法就是这样的:接收消息的有点过于复杂了。 

既然observer是LiveData里面的方法 而每次发送消息时间都是StickyObserver(sticky, Observer()) 这样我们就可以在我们的包装类中去复写一下observer,比如: 

这样发送接收数据就会变成,比之前稍微好一些 

如何解决普通事件的接收

在上面我们其实没对普通事件做处理 我们通过sticky能判断接不接受黏性事件 但是我们不知道在我们注册之前,有没有消息事件发送

override fun onChanged(t: T) {
if (sticky) {
observer.onChanged(t)
} else {
// 普通事件
}
}

回想一下,黏性事件是怎么产生的?

简单的认为,observer.mLastVersion(Observer的) < mVersion(LiveData的) 就会产生黏性事件

所以我们也可以模仿一下,弄两个变量去判断 

在我们的LiveDate中 

在我们的observe中会被改写成这样 

所以显然不能完全完全按照源码照抄

那黏性事件之所以会被发送出去

就是在StickyObserver初始化时mLastVersionmLiveDataVersion没对齐,

导致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)
}
}


}

0 个评论

要回复文章请先登录注册