事件总线相信大家很多时候都会用到,那大家常用的也就是常青树 EventBus,以及 RxJava 流行起来的后起之秀 RxBus。它们的使用方式都差不多,思想也都是基于观察者模式,正好 LiveData 的核心思想也是观察者模式,因此我们完全可以使用 LiveData 来实现一个事件总线,如果项目使用 Jetpack 套件的话,可以减少库的依赖,并且如果使用 LiveData 的话,还可以将注销操作交给系统处理,在避免内存泄露方面又可以省心了。
我们需要实现不同界面之间通信的话,必然是需要使用同一个 LiveData 对象,那么首先我们就需要一个 LiveData 的管理类,将 LiveData 对象用一个键值对集合存储起来,Key 为 LiveData 中 value 的权限类名,Value 就是 LiveData 对象,然后考虑到可以会使用带有泛型的对象作为事件传递,所以在注册 LiveData 的时候最好还支持泛型。有了这两个基本需求,我们就可以简单设计一下我们的管理类 LiveEventBus:
- package com.qinshou.jetpackdemo.liveeventbus;
-
- import androidx.lifecycle.MutableLiveData;
-
- import java.util.HashMap;
- import java.util.Map;
-
- public class LiveEventBus {
- private static final Map
> sMap = new HashMap<>(); -
- public static
MutableLiveData get(Class clazz) { - String key = clazz.getName();
- MutableLiveData
eventLiveData = (MutableLiveData) sMap.get(key); - if (eventLiveData == null) {
- eventLiveData = new MutableLiveData<>();
- sMap.put(key, eventLiveData);
- }
- return eventLiveData;
- }
-
- public static
MutableLiveData get(TypeToken typeToken) { - String key = typeToken.getType().toString();
- MutableLiveData
eventLiveData = (MutableLiveData) sMap.get(key); - if (eventLiveData == null) {
- eventLiveData = new MutableLiveData<>();
- sMap.put(key, eventLiveData);
- }
- return eventLiveData;
- }
- }
TypeToken 其实就是类似 Gson 中的 TypeToken 类,用于获取带泛型的对象中的真实泛型的,如果项目中 json 解析是用的 Gson 的话,那么也可以直接使用 Gson 的 TypeToken 类。由于功能简单,所以我是自己定义了 TypeToken 类:
- package com.qinshou.jetpackdemo.liveeventbus;
-
- import java.lang.reflect.ParameterizedType;
- import java.lang.reflect.Type;
-
- public abstract class TypeToken
{ - private Type mType;
-
- public TypeToken() {
- mType = new Type() {
- @Override
- public String getTypeName() {
- Type genericSuperclass = TypeToken.this.getClass().getGenericSuperclass();
- if (!(genericSuperclass instanceof ParameterizedType)) {
- return genericSuperclass.toString();
- }
- ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
- Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
- if (actualTypeArguments.length <= 0) {
- return parameterizedType.toString();
- }
- return actualTypeArguments[0].toString();
- }
-
- @Override
- public String toString() {
- Type genericSuperclass = TypeToken.this.getClass().getGenericSuperclass();
- if (!(genericSuperclass instanceof ParameterizedType)) {
- return genericSuperclass.toString();
- }
- ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
- Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
- if (actualTypeArguments.length <= 0) {
- return parameterizedType.toString();
- }
- return actualTypeArguments[0].toString();
- }
- };
- }
-
- public Type getType() {
- return mType;
- }
- }
其实到此为止,我们已经可以使用 LiveEventBus 实现不同组件间通信了,eg:
- findViewById
- LiveEventBus.get(Int::class.java).observe(this) {
- Toast.makeText(this@LiveEventBusActivity,"接受到 Int 事件: $it",Toast.LENGTH_LONG).show()
- }
- }
- findViewById
- LiveEventBus.get(object : TypeToken
>() {}).observe(this) {
- Toast.makeText(this@LiveEventBusActivity,"接受到 List
事件: $it" ,Toast.LENGTH_LONG).show() - }
- }
- findViewById
- LiveEventBus.get(Int::class.java).value = 123
- }
- findViewById
- LiveEventBus.get(object :
- TypeToken
>() {}).value = listOf("Hello LiveEventBus")
- }
先点击上面两个按钮注册观察者后,再点击下面两个按钮发送事件,效果如下:

可以看到效果跟使用 EventBus 是一样的,而且由于 LiveData 的天然特性,我们还不用去关注注销操作,可谓是省了一心。
使用过 LiveData 的朋友应该知道在 LiveData 绑定的时候会收到最新的值(不知道的朋友可以参考我的另一篇博客 Jetpack 之 LiveData_禽兽先生不禽兽的博客-CSDN博客),其效果就是先发送事件再注册观察者的话,也能收到之前的事件,如下:

这可以说是 LiveData 的一个优势,但是在作为事件总线时,这个特点相当于 EventBus 的粘性事件,我们有时候并不需要粘性事件,所以我们需要对 LiveData 进行一下改造。
定义一个 BaseObserver:
- public abstract class BaseObserver
implements Observer { - /**
- * 是否接收粘性事件
- */
- boolean mSticky = true;
-
- public boolean isSticky() {
- return mSticky;
- }
-
- public BaseObserver
setSticky(boolean sticky) { - mSticky = sticky;
- return this;
- }
- }
注意在后面添加观察者的时候我们需要添加的 BaseObserver 的实现类而不是 androidx.lifecycle 包下的 Observer 了
然后我们复制 LiveData 源码,修改两个地方,首先给 ObserverWrapper 增加一个 boolean 属性 mSticky:
- private abstract class ObserverWrapper {
- ...
- /**
- * 是否接收粘性事件
- */
- boolean mSticky = true;
-
- ...
- }
因为知道是 mLastVersion 造成的这个粘性事件,所以在添加观察者时,根据 BaseObserver 的 mSticky 属性来决定是否同步 mLastVersion:
- public void observe(@NonNull LifecycleOwner owner, @NonNull Observer super T> observer) {
- assertMainThread("observe");
- if (owner.getLifecycle().getCurrentState() == DESTROYED) {
- // ignore
- return;
- }
- LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
- // 如果是 BaseObserver,则设置 wrapper 对应属性
- if (observer instanceof BaseObserver) {
- wrapper.mSticky = ((BaseObserver>) observer).isSticky();
- }
- @SuppressLint("RestrictedApi") 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;
- }
- // 不需要粘性事件,则同步最后版本
- if (!wrapper.mSticky) {
- wrapper.mLastVersion = mVersion;
- }
- owner.getLifecycle().addObserver(wrapper);
- }
如果有需要的话,observeForever 方法也可以对应修改。另外,LiveEventBus 中对 MutableLiveData 的引用也需要去掉,引入我们自己的 MutableLiveData,所有源码最后会放上链接。
如此改造,我们就可以决定观察者是否接受粘性事件了:
- findViewById
- LiveEventBus.get(Int::class.java).observe(this, object : BaseObserver<Int>() {
- override fun onChanged(t: Int?) {
- Toast.makeText(this@LiveEventBusActivity, "接受到 Int 事件: $it", Toast.LENGTH_LONG).show()
- }
- }.setSticky(false))
- }
- findViewById
- LiveEventBus.get(object : TypeToken
>() {}).observe(this, object :
- BaseObserver
>() {
- override fun onChanged(t: List<String>?) {
- Toast.makeText(this@LiveEventBusActivity, "接受到 List
事件: $it" , Toast.LENGTH_LONG).show() - }
- }.setSticky(false))
- }
先发送事件,再注册观察者,可以看到并没有收到事件,然后再次发送事件的时候才收到:

这个问题我也同样在刚才提到的博客中分析过,主要是因为通常我们使用 LiveData 是结合数据请求用的,收到响应后一般就渲染 UI 了,而在后台的时候因为看不到 UI,所以也不关心非活跃状态的时候观察者是否有收到通知,但如果需要在后台分发一下事件的时候,我们也要相应的对其进行改造。
在 BaseObserver 中再增加一个 boolean 属性 mObserveOnlyActive,在 ObserverWrapper 中也同样增加该属性:
- public abstract class BaseObserver
implements Observer { - /**
- * 是否接收粘性事件
- */
- boolean mSticky = true;
- /**
- * 是否仅在活跃状态下接收更新
- */
- private boolean mObserveOnlyActive = true;
-
- ...
- 省略 get/set
- }
- private abstract class ObserverWrapper {
- ...
- /**
- * 是否接收粘性事件
- */
- boolean mSticky = true;
- /**
- * 是否仅在活跃状态下接收更新
- */
- boolean mObserveOnlyActive = true;
-
- ...
- }
添加观察者时增加对 mObserveOnlyActive 属性的赋值:
- public void observe(@NonNull LifecycleOwner owner, @NonNull Observer super T> observer) {
- assertMainThread("observe");
- if (owner.getLifecycle().getCurrentState() == DESTROYED) {
- // ignore
- return;
- }
- LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
- // 如果是 BaseObserver,则设置 wrapper 对应属性
- if (observer instanceof BaseObserver) {
- wrapper.mSticky = ((BaseObserver>) observer).isSticky();
- wrapper.mObserveOnlyActive = ((BaseObserver>) observer).isObserveOnlyActive();
- }
- @SuppressLint("RestrictedApi") 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;
- }
- // 不需要粘性事件,则同步最后版本
- if (!wrapper.mSticky) {
- wrapper.mLastVersion = mVersion;
- }
- owner.getLifecycle().addObserver(wrapper);
- }
然后再修改 considerNotify 方法中对观察者不活跃就 return 的判断代码即可:
- private void considerNotify(ObserverWrapper observer) {
- // 如果 observer 不是活跃状态,但 observer 要求仅在活跃状态下通知的,直接 return
- if (!observer.mActive && observer.mObserveOnlyActive) {
- return;
- }
- // Check latest state b4 dispatch. Maybe it changed state but we didn't get the event yet.
- //
- // we still first check observer.active to keep it as the entrance for events. So even if
- // the observer moved to an active state, if we've not received that event, we better not
- // notify for a more predictable notification order.
- // 如果 observer 不应该是活跃状态,但 observer 要求仅在活跃状态下通知的,直接 return
- if (!observer.shouldBeActive() && observer.mObserveOnlyActive) {
- observer.activeStateChanged(false);
- return;
- }
- if (observer.mLastVersion >= mVersion) {
- return;
- }
- observer.mLastVersion = mVersion;
- observer.mObserver.onChanged((T) mData);
- }
现在我们将给发送事件加一个延时,然后退到后台使观察者处于非活跃状态,看看还能不能收到事件:
- findViewById
- LiveEventBus.get(Int::class.java).observe(this, object : BaseObserver
() { - override fun onChanged(t: Int?) {
- Toast.makeText(this@LiveEventBusActivity, "接受到 Int 事件: $it", Toast.LENGTH_LONG).show()
- }
- }.setSticky(false).setObserveOnlyActive(false))
- }
- findViewById
- LiveEventBus.get(object : TypeToken
>() {}).observe(this, object :
- BaseObserver
>() {
- override fun onChanged(t: List
?) { - Toast.makeText(this@LiveEventBusActivity, "接受到 List
事件: $it" , Toast.LENGTH_LONG).show() - }
- }.setSticky(false).setObserveOnlyActive(false))
- }
- findViewById
- it.postDelayed({ LiveEventBus.get(Int::class.java).value = 123 }, 3000)
- }
- findViewById
- it.postDelayed({
- LiveEventBus.get(object :
- TypeToken
>() {}).value = listOf("Hello LiveEventBus")
- }, 5000)
- }

可以看到观察者处于后台也接收到了事件,所以这个问题也得以解决。
上面只是简单实现了事件总线的效果,但也由此可见使用 LiveData 来封装事件总线的可行性,对于上述两个关键问题,相信看过 LiveData 源码后对其修改并不难,所以万事还要究其根本。
如果还需要更多如指定观察者线程、观察者优先级的功能,可以参照 EventBus 进行进一步封装。