EventBus的缺点及改进升级

 

EventBus很屌,被广泛用于事件分发、工程解耦,现在已经出到了第3版EventBuds3。网上对它的介绍太多了,我这里就不详细展开了。有不熟悉的可以阅读以下这篇文章:

《老司机教你“飙”EventBus3》 https://blog.csdn.net/natural_story/article/details/51444299

EventBus确实很棒,用那么小小的三五十KB轻量代码实现了那么强大的功能,可以把很多复杂混乱的APP整理得干净漂亮很多。用我的话说,EventBus的SLOGAN是:脏活我来干,优雅你来装。

这话一点不错。大型APP里,既要解耦,又要代码书写灵活,永远是一对矛盾体。辛辛苦苦拆得干干净净的两个组件A和B,突然需求一变,A要B干点事,那就要引入接口依赖了。在我看来就是“打洞”,光生生的一面墙,打上一两个三四个黑黢黢的不规则的洞,是很不雅观的,而且是比较费劲的,又脏又累。EventBus就是干这种苦活的。于是用了EventBus,程序员又可以装逼了(你看,我的代码多么解耦,多么优雅)。

然而本文并不是靠吹嘘EventBus来装逼的,相反,我要数落一下它的缺点,并推介一下EventBus4(本人命名的,未得到官方背书)。

缺点如下:

  1. 事件只能通过事件的类名来区分;

这恐怕是EventBus最坑爹的设计了,EventBus吹捧者们难道没注意到它这一点吗?这至少带来了3大问题:

  1. 操作麻烦,每一个事件,都要定义一个类;
  2. 增加方法数;
  3. 导致事件发送者和接收者都依赖耦合事件类;
  1. 事件发生者只能单向广播,无法获得接收者对事件的处理结果;

EventBus能确保事件被送达给感兴趣的监听者。但这一过程是单向的。就像直流电一样,电流永远是从正极流向负极,无法“交流”(在现代社会中,交流电远比直流电用途广嘛)。谁规定的Event应该单向传递呢。很多时候,Event发送者把Event发送出去了,是很希望得到反馈的:小王,事情处理得怎么样了,有什么结论?结果小王一言不发,低头做事。没有交流的协作是低效的写作。

  1. 所有事件监听者(Subscriber)都必须显性注册;

或许EventBus的开发者觉得每一个事件接收者,向EventBus的中央注册机构注册一下自己,并不是一件费力的事情,也就一行代码而已(注册与反注册,其实有2行代码)。但对很多挑剔的程序员来说,这是件很不爽的事情。 我已经在注解里声明了自己,凭什么还要冗余地再注册一次,你很不得了么?明明大量的场景下,事件监听者是在程序一启动就需要监听事件的(不要提sticky,sticky是很weird的逻辑)。

很多第三方模块/组件都喜欢搞个什么XXX.init(), XXX.load(),作为自己模块/组件的初始化/启动函数。而且还轻描淡写地说一句:“请在你的Application.onCreate()里初始化我哦”,我靠,很讨厌好吗。Application.onCreate是什么,那是程序员最敏感的部位之一啊。

BTW,在Application.onCreate()里初始化也比那些在Android AAR里悄悄定义Provider来实现初始化的模块/组件更有节操一点。

EventBus4很完美地解决了这些问题。它在这几个方面对EventBus做了重要扩展:

  1. 事件过滤器(filter);

先看看示例代码:

@Subscribe(filter = "Event1")

public void onEvent1Emitted(Object obj)

filter其实是个很好理解的概念。事件有很多,监听者需要“筛选”、“过滤”一下,只监听我关心的事件。它是定义在Subscribe注解里面的。有了filter,就再也不需要为监听者定义事件class了。省时省力;事件发送者与监听者的依赖被大大降低(严格说来还对字符串”Event1”有依赖);而且,这样的代码不是更简洁、优雅么?

  1. 让事件发送者和监听者可以“交流”(process);

还是先看示例代码:

Object obj = EventBus.getDefault().process(new Status.STATUS_PLAYING);

在大量的场景中,我抛出一个事件(或者叫做消息Message),其实希望得到别人对该事件的回应。也就是我通过抛出事件来完成一次我对别人的调用。这样的需求,EventBus完全应该满足啊,而且鉴于它的架构特点,也是责无旁贷的。

我实现了这个process()方法,专门解决这个需求。当然,该事件的监听者应当有且仅有一个。如果定义多了,EventBus也只会把事件传给第一个监听者,其他的被忽略掉了。至于那些想要一次调用就得到多个被调用者的返回结果的需求,我觉得多半是伪需求,暂不考虑。

 

  1. 独占性接收者(exclusive Subscriber);

正是因为有大量的场景,是事件发送者希望获得事件接收者的处理结果的,所以才诞生了”独占性接收者”。

  1. 独吞事件:宣告自己是该事件唯一的接收者,不允许别的接收者染指;
  2. 排他性检查:若有两个及以上定义exclusive的接收者,则编译告警;

读者可能已经猜到了,这其实就是一种“接口暴露”,用于一个组件/模块对外提供能力服务。调用者可以简单地通过一个事件调用它。

示例代码:

@Subscribe(filter = "getUserInfo", exclusive = true)
public int getUserInfo(Object obj)

  1. 全局事件监听者;

一个监听者若比较贪婪,想监听程序从启动到退出期间一切事件,怎么办?原始落后的EventBus3居然想到的是用sticky(粘性事件,前文说了,这很weird)来解决,太笨重了。需要单独抛sticky事件不说,还需要缓存住所有sticky事件。如果某个事件发生的特别频繁,一分钟产生成千上万个,那岂不是要缓存成千上万个事件?那会导致多少潜在内存泄漏,又有多少sticky事件的监听者在注册的那一刻因大量事件处理而卡死?

这个时候需要的是全局的事件监听者。

示例如下:

@Subscribe(filter = "Event1")
public static int onEvent1Emitted(Object obj)

 

它的特点如下:

  1. 必须是静态公共方法(才能被“全局”使用);
  2. 无需开发者显性注册(编译期自动注册);
  3. 可取消注册(结束“全局监听”的能力);

 

既然继承于EventBus,EventBus4也就是一个开源项目,利用业余时间完成,遵循Apache-2.0开源协议。

地址是:https://gitee.com/iharryguo/EventBus

你可能感兴趣的:(EventBus的缺点及改进升级)