EventBus类别
EventBus非常灵活,可以用作单例,或者应用程序可以具有多个实例以适应在不同上下文中传输事件。 EventBus将按顺序分派所有事件,因此,保持事件处理方法的轻量化很重要。 如果需要在事件处理程序中进行更重的处理,则可以使用EventBus的另一种形式,即AsyncEventBus。 AsyncEventBus在功能上是相同的,但是采用ExecutorService作为构造函数参数来允许事件的异步调度。
订阅活动
对象通过以下步骤预订事件:
- 定义一个采用所需事件类型的单个参数的公共方法,并在该方法上放置@Subscribe批注。
- 通过将对象的实例传递给EventBus.register方法来向EventBus注册。
这是一个简短的示例,为清楚起见省略了详细信息:
public class PurchaseSubscriber {@Subscribepublic void handlePurchaseEvent(PurchaseEvent event) {.....}.....EventBus eventBus = new EventBus();PurchaseSubscriber purchaseSubscriber = new PurchaseSubscriber();eventBus.register(purchaseSubscriber);
还有一个可以与@Subscribe结合使用的注释,即@AllowConcurrentEvents。 @AllowConcurrentEvents将处理程序方法标记为线程安全的,因此EventBus(很可能是AsyncEventBus)可以潜在地从同时线程调用事件处理程序。 我在单元测试中发现的一件有趣的事情是,如果处理程序方法没有@AllowConcurrentEvents批注,则即使使用AsyncEventBus,它也会按顺序调用事件的处理程序。 重要的是要注意,@ AllowConcurrentEvents不会将方法标记为事件处理程序,@ Subscribe批注仍需要存在。 最后,事件处理方法必须具有一个且只有一个参数,否则当您在EventBus中注册对象时,将抛出IllegalArgumentException。
发布事件
同样,使用EventBus发布事件也很简单。 在您要发送事件通知的代码部分中,调用EventBus.post,将为该事件对象注册的所有订阅者进行通知。
public void handleTransaction(){purchaseService.purchase(item,amount);eventBus.post(new CashPurchaseEvent(item,amount));....}
尽管这很明显,但是订阅类和发布类共享同一EventBus实例很重要,并且使用Guice或Spring帮助管理依赖项是有意义的。
有关事件处理程序的更多信息
EventBus的一项非常强大的功能是,您可以根据需要使处理程序正常运行或细化。 EventBus将为已发布事件对象的所有子类型和已实现的接口调用注册的订户。 例如,要处理所有事件,可以创建一个带有Object类型参数的事件处理程序。 要仅处理单个事件,请创建一个特定于类型的处理程序。 为了帮助说明,请考虑以下简单事件层次结构:
public abstract class PurchaseEvent {String item;public PurchaseEvent(String item){this.item = item;}
}public class CashPurchaseEvent extends PurchaseEvent {int amount;public CashPurchaseEvent(String item, int amount){super(item);this.amount = amount;}
}public class CreditPurchaseEvent extends PurchaseEvent {int amount;String cardNumber;public CreditPurchaseEvent(String item,int amount, String cardNumber){super(item);this.amount = amount;this.cardNumber = cardNumber;}
}
以下是相应的事件处理类:
//Would only be notified of Cash purchase eventspublic class CashPurchaseSubscriber {@Subscribepublic void handleCashPurchase(CashPurchaseEvent event){... }}//Would only be notified of credit purchasespublic class CreditPurchaseSubscriber {@Subscribepublic void handleCreditPurchase(CreditPurchaseEvent event) {....}
}
//Notified of any purchase eventpublic class PurchaseSubscriber {@Subscribepublic void handlePurchaseEvent(PurchaseEvent event) {.....}
}
如果需要捕获各种各样的事件类型,则替代方法是在一个类中具有多个事件处理方法。 拥有多个处理程序可能是一个更好的解决方案,因为您不必对事件对象参数进行任何“ instanceof”检查。 这是我的单元测试中的一个简单示例:
public class MultiHandlerSubscriber {List<CashPurchaseEvent> cashEvents = new ArrayList<ashPurchaseEvent>();List<CreditPurchaseEvent> creditEvents = new ArrayList<CreditPurchaseEvent>();List<SimpleEvent> simpleEvents = new ArrayList<SimpleEvent>();public MultiHandlerSubscriber(EventBus eventBus){eventBus.register(this);}@Subscribepublic void handleCashEvents(CashPurchaseEvent event){cashEvents.add(event);}@Subscribepublic void handleCreditEvents(CreditPurchaseEvent event){creditEvents.add(event);}@Subscribepublic void handleSimpleEvents(SimpleEvent event){simpleEvents.add(event);}
....
测试中
由于事件处理程序只是普通方法,因此可以通过在测试用例中实例化一个EventBus或通过传递适当的事件对象来模拟EventBus来轻松对其进行测试。 当使用EventBus时,我发现很容易:
- 忘记在EventBus中注册订阅对象
- 忽略添加@Subscribe批注。
如果似乎未调用事件处理程序,请首先检查这两个错误。
一种有用的调试技术是订阅DeadEvent类。 EventBus将在DeadEvent实例中包装任何没有处理程序的已发布事件。 DeadEvent提供了getEvent方法,该方法返回原始事件对象。
结论
Guava EventBus类为标准Java事件处理机制提供了一种有吸引力且有用的替代方法。 希望读者会发现EventBus像我一样有用。 一如既往地欢迎提出意见和建议。
资源资源
- EventBus API
- 番石榴API
- 样例代码
- 活动合作
参考:来自我们的JCG合作伙伴 Bill Bejeck的Google Guava EventBus进行事件编程,来自Random Thoughts On Coding博客。
翻译自: https://www.javacodegeeks.com/2012/11/google-guava-eventbus-for-event-programming.html