事件总线与回调函数,何时使用哪个?

28

我有许多活动会触发后台任务;这些活动将自己传递进来作为实现监听器回调的对象,以便后台任务可以在活动上引发事件。反过来,活动可以在UI上显示一些内容,以指示后台活动是成功还是失败。

或者,我可以使用EventBus,在其中获取Activity将自身注册为监听器/订阅者。我可以让后台任务在EventBus上引发一个事件,监听它的Activity可以处理它。

一个相对于另一个的优势是什么?在何时使用其中之一?(代码清洁度?性能?注意事项?)


后续 - 我最终选择了使用EventBus。代码显然更加清晰,并且没有回调函数到处挂着。IDE(IntelliJ)认为onEvent方法未使用,因此我创建了一个注释。

@Target({ElementType.METHOD})
public @interface EventBusHook {}

我将其放置在我的onEvent方法之上。然后Alt + 单击它并要求IntelliJ不将其视为未使用。

并将其放置在我的onEvent方法之上。然后Alt + 单击它,并要求IntelliJ不将其视为未使用。

@EventBusHook
public void onEvent(MyEventType myEventType){

我会使用本地绑定服务来启动任务,并创建一个活动来绑定该服务并创建一个“绑定”以发送结果。 - pskink
4个回答

54

我不同意 @nnuneoi 的回答。

事件总线只有一个优点:它允许组件之间进行通信,而这些组件可能“不知道”彼此的存在。

但是,它也有几个缺点:

  1. 组件通过对事件总线和特定事件类型的依赖变得松散耦合
  2. 上述 #1 中描述的耦合并不强
  3. 上述 #1 中描述的耦合并不明显
  4. 与简单回调相比,事件总线引入了性能开销
  5. 如果事件总线对订阅者持有强引用(例如 GreenRobot's EventBus),则未注册的订阅者将导致内存泄漏

考虑到所有这些缺点,简单回调应该是默认的实现选择。

只有在不希望直接耦合或难以实现时才应使用事件总线。例如:

  1. Service 发送事件到 Activity
  2. 在独立的 Fragments 之间交换事件
  3. 应用程序范围的事件(例如用户登录/注销)
如果通信组件已经“知道”彼此的存在,它们就不需要通过事件总线进行通信。

组件之间松耦合的论点足以说明在简单的Activity / Fragment通信中不使用EventBus。因为Fragment已经与Activity相关联了。 - Murat Karagöz
2
@MuratK.,不完全是这样。Fragment 附加到 Activity 上,但为了能够从特定的 Fragment 向特定的 Activity 进行通信,该 Fragment 将需要与该特定 Activity 类(或某个 Activity 实现的接口)耦合。这是一种更强的耦合类型,还需要对从 getActivity() 调用返回的引用进行转换。因此,在某些情况下,使用 EvenBus 更加清晰。在 Fragment 到 Fragment 通信的情况下(通过 Activity),耦合将更紧密,这使得 EventBus 更加有益。 - Vasiliy
@Vasiliy 我有一个Fragment和后台服务。服务是数据源,Fragment是订阅者(显示数据)。我使用Green EventBus。如果Activity被杀死,服务会失去其订阅者,但仍然继续工作。在这种情况下,可能会出现内存泄漏吗? - tim4dev
2
只要已注册到EventBus的Activities和Fragments在其onStop()方法中注销,就不会出现内存泄漏。即使没有订阅者,Service可能仍然会继续发布事件。顺便说一句,在像你这样的情况下,我发现粘性事件非常有用。 - Vasiliy

26

使用EventBus的好处:

  • 您的代码将更加简洁
  • 您的代码将更加模块化,这将允许您轻松地为代码创建测试用例
  • 避免由于错误对象引用而导致的内存泄漏,这会锁定对象并阻止垃圾回收器清理它
  • 可以同时有多个接收者,就像广播一样
  • 将多个接口简化为一个接口:EventBus
  • 在接口类中,您需要覆盖继承的类中的每个方法。 使用EventBus,您只需监听您真正想要的事件

但是不好的部分是,您可能会在函数声明方面感到困难,因为IDE无法帮助您进行自动完成。

我的建议是,如果您发现必须创建自定义侦听器,请考虑使用EventBus,对于您大多数(如果不是全部)的要求/情况来说,它可能是更好的选择。

总之,这毕竟是你的选择 =)


1
谢谢,我因为你提到的前三点而倾向于使用EventBus。我会尝试一下,并看看在IntelliJ中它的表现如何。 - Mendhak

1
你需要检查在语义视图上你的事件是否是全局唯一的。无论订阅者是否对事件感兴趣,都应该进行检查。如果不感兴趣,就不应该订阅。
如果你确实有发布者-订阅者关系,则事件总是独立于接收者的,这时候事件总线机制是正确的选择。
因此,如果一个订阅者因为责任原因而丢弃事件("即使我已经注册了,我也不负责处理该事件"),这表明使用事件总线是错误的选择。那么你应该考虑使用专用的监听器。

1
我会选择EventBus,因为它具有松散耦合和更清晰的代码。此外,使用Greenrobot等EventBus自动为我执行所有样板文件并允许我从Activity声明周期方法(onStart和onDestroy|onStop)中注册和注销观察者非常棒。实现回调并仍然管理这些回调的Activity生命周期管理是不必要的头痛,并涉及大量不必要的样板文件。
此外,显然所有垃圾收集器都认为弱引用很好-Event bus确保您的观察者和组件正是如此。这是观察者模式的基础。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接