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