EventBus使得组件之间的发布-订阅式的通信,而无需组件与另一个(因此要注意对方的),明确登记。它的设计完全使用明确的登记,以取代传统的Java过程中的事件分发。它是不是一个通用的发布-订阅系统中,也不旨在用于进程间通信。
//类通常由注册 地方在初始化过程中 eventBus 。注册(新 EventBusChangeRecorder ()); //要晚得多 公共 无效changeCustomer () { 的ChangeEvent 事件 = getChangeEvent (); eventBus 。后(事件); }
将现有的EventListener基于系统使用的EventBus容易。
侦听事件的特定味道(比如,一个CustomerChangeEvent)...
- ......在传统的Java事件:实现与该事件定义的接口-比如CustomerChangeEventListener。
- ...与EventBus:创建一个接受的方法CustomerChangeEvent作为其唯一的参数,并将其与标记订阅注解。
注册您的侦听器方法的事件生产者...
- ......在传统的Java事件:通过你的对象每个生产者的registerCustomerChangeEventListener方法。这些方法在常用接口很少定义,所以除了知道每一个可能的制片人,你还必须知道它的类型。
- ...与EventBus:通过你的对象给EventBus.register(对象)上的方法EventBus。你需要确保你的对象共享一个EventBus实例的事件生产者。
- ......在传统的Java事件:不容易。
- ...与EventBus:事件自动调度到任何超的听众,让听众接口类型或“通配符听众”的对象。
要收听和检测共出动,没有听众的事件...
- ......在传统的Java事件:将代码添加到每个事件分派方法(可能使用AOP)。
- ...与EventBus:订阅DeadEvent。该EventBus将通知被张贴,但没有交付任何的事件。(方便的调试。)
生产者
为了跟踪监听到你的事件?
- ......在传统的Java事件:写代码来管理听众的名单给你的对象,其中包括同步,或者使用类似工具类EventListenerList。
- ...与EventBus: EventBus这是否适合你。
派遣一个事件监听器...
- ......在传统的Java事件:写一个方法来分派事件到每一个事件侦听器,包括错误隔离和(如果需要)异步。
- ...与EventBus:事件对象传递给EventBus的EventBus.post(对象)的方法。
词汇表
该EventBus系统代码中使用下列术语来讨论事件分发:
事件 | 可任意物体贴到一个总线。 |
认购 | 登记的动作侦听器与EventBus,使得它的处理方法将获得的事件。 |
倾听者 | 任何希望接收事件,暴露的对象处理方法。 |
处理方法 | 该公共方法EventBus应该使用提供发布事件。处理方法的标志是订阅注解。 |
发布事件 | 使得事件提供给任何听众通过EventBus。 |
常问问题
为什么我必须创建自己的事件总线,而不是使用一个单身?
EventBus不指定你如何使用它; 没有什么不必单独停止你的应用程序EventBus情况下,为每个组件,或者使用单独的实例来分离上下文或主题活动。这也使得它琐碎的建立和拆除EventBus在您的测试对象。
当然,如果你想有一个过程性的EventBus单身,没有什么从做这种方式阻止你。只需您的容器(如吉斯)创建EventBus作为一个单身在全局范围内(或藏在一个静态字段,如果你到诸如此类的事情)。
总之,EventBus是不是单身,因为我们宁愿不作出这样的决定你。使用它,你怎么样。
我可以从注销事件总线的监听器?
是的,使用EventBus.unregister,但我们发现,只需要很少:
- 大多数听众注册在启动时或延迟初始化,并坚持应用程序的生命。
- 范围特定EventBus实例可以处理临时事件分发(如分发事件中的请求范围的对象)
- 为了测试,EventBus实例可以容易地创建并扔掉,不再需要显式的取消注册。
为什么要使用一个注释标记处理方法,而不是要求听者实现接口?
我们认为事件总线的@Subscribe注释就像明确地传达你的意图是实现一个接口(或者更左右),同时使你自由,无论你希望将事件处理方法,并给他们的意图展现的名字。
传统的Java活动使用监听器接口通常运动只有极少数的方法 - 通常是一个。这具有许多缺点:
- 任何一个类只能实现对给定事件的单个响应。
- 监听器接口方法可能会发生冲突。
- 该方法必须命名的事件(例如经过handleChangeEvent),而不是它的目的(如recordChangeInJournal)。
- 每个事件通常都有自己的接口,没有一个共同的父接口,为家庭的事件(例如,所有UI事件)。
在干净实施这一困难已经引起了一种模式,在Swing应用程序中特别常见,用微小的匿名类来实现事件监听器接口。
比较这两种情况:
class ChangeRecorder { void setCustomer ( Customer cust ) { cust . addChangeListener ( new ChangeListener () { public void customerChanged ( ChangeEvent e ) { recordChange ( e . getChange ()); } }; } }
与
//类通常由注册container. class EventBusChangeRecorder { @Subscribe public void recordCustomerChange ( ChangeEvent e ) { recordChange ( e . getChange ()); } }
其目的是在第二种情况下居然清晰的:有较少的噪声码,和事件处理程序有一个明确的和有意义的名字。
怎么样一个通用处理器<T>接口?
有人提出了一个通用的 处理程序<T>接口 EventBus听众。这将运行与Java的使用类型擦除的,更何况在可用性问题的问题。
比方说,界面看起来像下面这样:
接口 处理器< 牛逼> { 无效的handleEvent (牛逼事件); }
由于擦除,没有任何一个类可以用不同类型的参数实现的通用接口不止一次。这是一个巨大的倒退,从传统的Java事件,在那里即使 的actionPerformed和 的keyPressed都不是很有意义的名字,至少可以实现两个方法!
不EventBus破坏静态类型,消除自动重构的支持?
有些人吓坏了约EventBus的寄存器(对象)和后期(对象)的方法“使用的对象类型。
对象在这里使用一个很好的理由:事件总线库的地方在任您事件侦听器的类型没有限制(如寄存器(对象))或事件本身(在后(对象))。
事件处理方法,在另一方面,必须明确声明其参数类型 - 所需的事件类型(或它的超类型之一)。因此,寻找引用的事件类将立即查找所有处理方法该事件,并重新命名的类型会影响视野内的IDE(以及创建活动的任何代码)的所有处理方法。
这是真的,你可以重命名你@Subscribed随意事件处理方法; 事件总线不会停止这样或做任何事情来传播重命名,因为,到事件总线,你的处理方法的名称是无关紧要的。但是这是你的重构工具是什么-即直接调用,当然会受你的重命名的方法测试代码。我们认为这是一个特点,不是一个错误:能重命名处理方法的意愿让你做自己的意思更清晰。
如果我发生什么注册一个监听器没有任何处理方法?
什么都没有。
事件总线的设计与容器和模块系统集成,与吉斯作为典型例子。在这种情况下,它的方便的容器/工厂/环境下通过每个创建的对象到EventBus的寄存器(对象)的方法。
这样,通过容器/工厂/环境创建的任何对象都可以通过简单地暴露处理方法挂接到系统的事件模型。
什么事件总线问题都可以在编译时被检测到?
可以通过Java的类型系统明确地检测出任何问题。例如,定义一个处理方法不存在的事件类型。
什么事件总线问题可以立即在注册检测?
紧随调用寄存器(对象),正在注册的侦听器检查的良好性,其处理方法。具体来说,标有任何方法@Subscribe必须只有一个参数。
任何违反此规则将导致抛出:IllegalArgumentException抛出。
(此检查可以移动使用APT,一个解决方案,我们正在研究编译时间。)
什么EventBus问题可以仅购买检测,在运行时?
如果没有注册的侦听器组件发布事件,它可能会指示错误(通常是你错过了一个指示@Subscribe注释,或没有加载监听组件)。
(请注意,这并不一定表明有问题的。有很多情况下,一个应用程序会故意忽略发布的事件,特别是如果事件是从你无法控制的代码来了。)
为了处理这些事件,注册一个处理方法为DeadEvent类。每当EventBus接收到一个事件,没有注册的处理程序,它会变成一个DeadEvent,并通过它自己的方式-让你登录,或以其他方式收回。
如何测试事件侦听器及其处理方法?
因为你的监听类处理方法都是正常的方法,你可以简单地从你的测试代码中调用它们来模拟EventBus。
为什么我不能做<神奇的事情>与EventBus?
EventBus是专门用来对付一大类用例真的,真的很好。我们宁愿打在要害大多数用例在所有用例做体面。
此外,使得EventBus扩展-和使得它非常有用和生产上延伸,而仍允许自己作出补充到芯EventBus API,它不与任何的扩展的冲突-是一个极其困难的问题。
如果你真的,真的需要魔法的事情X,即EventBus目前无法提供,你应该提交一个问题,然后设计自己的选择。