LiveData
是一个可以被观察的数据持有类,可以感知并遵循Activity、Fragment,Service
等组件的生命周期。由于这种特性可以使他做到在特定生命周期执行特定的操作。
LiveData
优点:
UI和实时数据保持一致 :可以在数据发生改变时立即响应到
避免内存泄漏 :当绑定的组件被销毁时,会自动清理数据以及移除引用,避免泄漏
根据上述优点,就可以利用LiveData
去实现一个组件间通信的方案,这套方案相对于EventBus
、RxBus
有着明显的优势,不需要显式的去调用反注册方法(以免内存泄漏 ),而且其自带生命周期感知,可以在Activity等组件处于前台状态时,进行UI更改,避免浪费资源。
LiveDataBus的组成
消息 :用于在组件中通信所传递的数据,可能是基本类型也可能是自定义Model
消息通道 :用消息通道来甄别不同的LiveData(处理对象 )
订阅者 :通过消息通道获取对应的LiveData
,调用observe()
进行订阅
发布者 :通过消息通道获取对应的LiveData
,调用postValue()、setValue()
进行消息发送
LiveDataBus的实现 按照上述结构图,可以马上就写出一个大致结构
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class LiveDataBus private constructor () { private val map: MutableMap<String, MutableLiveData<Any>?> init { map = HashMap() } fun <T> getChannel (target: String , type: Class <T >) : MutableLiveData<T> { if (!map.containsKey(target)) { map[target] = MutableLiveData() } return map[target] as MutableLiveData<T> } fun getChannel (target: String ) : MutableLiveData<Any> { return getChannel(target, Any::class .java ) } companion object { val instance: LiveDataBus by lazy { LiveDataBus() } } }
对应发送数据方法
1 2 3 4 LiveDataBus.instance.getChannel("web" , String::class .java ).value = "ssa " LiveDataBus.instance.getChannel("web" ,String::class .java ).postValue ("ssa" )
对应接收数据方法
1 2 3 4 LiveDataBus.instance.getChannel("web" , String::class .java ) .observe(this @MainActivity , Observer { s -> Log.e("web" , s) })
但是在实际的使用过程中发现了另一个问题,再打开一个新页面时,如果也存在监听者,就会收到该页面打开前所发送的消息,类似粘性事件 ,但大部分场景下是不需要这样的,所以需要针对这个问题进行改进。
LiveDataBus改进 根据{% post_link LiveData简析%}这部分源码分析可知,LiveData中的数据分发流程如下图所示:
{% fullimage /images/LiveData-dispatch.png,LiveData Dispatch,LiveData Dispatch%}
根据上述流程分析:调用到setValue()/postValue()
将用户数据进行发送,然后进入到dispatchValue()
下进行分发,设置mVersion++(mVersion表示调用方法次数)
,想下调用到considerNotify()
内部需要判断observer.mLastVersion(Observer注册次数)
与mVersion
大小,如果小于就会调用到对应Observer.onChanged()
事件进行分发。
由于初始化时,先会调用到postValue()/setValue()
此时mVersion+1,就比mLastVersion
要大,就会触发事件的分发。
改进方案 只要可以设置mLastVersion
与mVersion
保持一致,就不会进行事件的分发。
此时需要利用反射 的方式对LiveData
中的数据进行改变,首先按照需求,先要找到Observer
,然后修改其中的mLastVersion
即可。
LiveData.java 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @MainThread public void observe (@NonNull LifecycleOwner owner, @NonNull Observer<T> observer) { if (owner.getLifecycle().getCurrentState() == DESTROYED) { return ; } LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer); ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper); if (existing != null && !existing.isAttachedTo(owner)) { throw new IllegalArgumentException("Cannot add the same observer" + " with different lifecycles" ); } if (existing != null ) { return ; } owner.getLifecycle().addObserver(wrapper); }
需要从mObservers
中入手,然后找到对应Observer
即可。
除了observer()
外,还有一个observerForever()
,该方法是一直存在监听的,而且不会绑定对应的组件,所以可以在任意组件中监听到事件,如果使用时,需要注意销毁。
改进代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 package com.wxy.router.eventbus.utilsimport android.arch.lifecycle.LifecycleOwnerimport android.arch.lifecycle.LiveDataimport android.arch.lifecycle.MutableLiveDataimport android.arch.lifecycle.Observerimport android.icu.lang.UCharacter.GraphemeClusterBreak.Timport java.lang.Exceptionimport java.lang.NullPointerExceptionimport java.util.*class LiveDataBus private constructor () { private val map: MutableMap<String, BusMutableLiveData<Any>?> init { map = HashMap() } fun <T> getChannel (target: String , type: Class <T >) : BusMutableLiveData<T> { if (!map.containsKey(target)) { map[target] = BusMutableLiveData() } return map[target] as BusMutableLiveData<T> } fun getChannel (target: String ) : BusMutableLiveData<Any> { return getChannel(target, Any::class .java ) } companion object { val instance: LiveDataBus by lazy { LiveDataBus() } } class ObserverWrapper <T > () : Observer<T> { private var observer: Observer<T>? = null constructor (observer: Observer<T>) : this () { this .observer = observer } override fun onChanged (t: T ?) { observer?.let { if (isCallOnObserve()) return @let it.onChanged(t) } } private fun isCallOnObserve () : Boolean { val stackTrace = Thread.currentThread().stackTrace if (stackTrace.isNotEmpty()) { stackTrace.forEach { stackTraceElement -> if ("android.arch.lifecycle.LiveData" == stackTraceElement.className && "observeForever" == stackTraceElement.methodName ) { return true } } } return false } } class BusMutableLiveData <T > : MutableLiveData <T > () { private val observerMap: MutableMap<Observer<T>, Observer<T>> = hashMapOf() override fun observe (owner: LifecycleOwner , observer: Observer <T >) { super .observe(owner, observer) try { hook(observer) } catch (e: Exception) { e.printStackTrace() } } override fun observeForever (observer: Observer <T >) { if (!observerMap.containsKey(observer)) { observerMap[observer] = LiveDataBus.ObserverWrapper(observer) } super .observeForever(observer) } override fun removeObserver (observer: Observer <T >) { val realObserver: Observer<T>? = if (observerMap.containsKey(observer)) { observerMap.remove(observer) } else { observer } realObserver?.let { super .removeObserver(it) } } private fun hook (observer: Observer <T >) { try { val classLiveData = LiveData::class .java val fieldObservers = classLiveData.getDeclaredField("mObservers" ) fieldObservers.isAccessible = true val objectObservers = fieldObservers.get (this ) val classObservers = objectObservers.javaClass val methodGet = classObservers.getDeclaredMethod("get" , Object::class .java ) methodGet.isAccessible = true val objectWrapperEntry = methodGet.invoke(objectObservers, observer) var objectWrapper: Any? = null if (objectWrapperEntry is Map.Entry<*, *>) { objectWrapper = objectWrapperEntry.value } if (objectWrapper == null ) throw NullPointerException("wrapper can not be null" ) val classObserverWrapper = objectWrapper.javaClass.superclass val fieldLastVersion = classObserverWrapper.getDeclaredField("mLastVersion" ) fieldLastVersion.isAccessible = true val fieldVersion = classLiveData.getDeclaredField("mVersion" ) fieldVersion.isAccessible = true val objectVersion = fieldVersion.get (this ) fieldLastVersion.set (objectWrapper, objectVersion) } catch (e: Exception) { e.printStackTrace() } } } }