Android不使用反射,完成LiveDataBus

2021-10-21  本文已影响0人  liyihuanx

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

首先来看一下LiveData原理

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


image.png

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


image.png

所以首先进入 observe 方法看一看

image.png

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

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

image.png

这里有几点需要注意一下
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就好了
就像这样:(当然这不是完整版,这只是记录一下思路)

image.png

怎么保证接收的是同一个事件

        liveData.postValue
        viewModel.liveData.observer(this , Observer {

        })

一般我们都是这样发送接收的,这个LiveData都是同一个才能接收同一份数据。
所以我们也必须在LiveDataBus保证是同一个LiveData才行。

还是同样的思路,要区分LiveData,就给LiveData加名字就行了呗
那我就再给LiveData包装一层,让调用者传入名字去生成
生成完了就保存下来,以后就用名字去找到对应的LiveData
就好比:

image.png

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


image.png

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

image.png

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


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初始化时mLastVersionmLiveDataVersion没对齐,
导致if (mLastVersion >= stickyLiveData.mLiveDataVersion) {} 没进入
所以进入if条件就有黏性事件,所以我们要改成这样

image.png

最后附上整个代码,直接复制就可以用


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


}


上一篇下一篇

猜你喜欢

热点阅读