使用Event Bus模式解耦Android App組件間通信
轉自云在千峰的博客
場景描述
當一個Android應用功能越來越多的時候,保證應用的各個部分之間高效的通信將變得越來越困難。
在應用中的多個地方,控件經常需要根據某個狀態來更新他們顯示的內容。這種場景常見的解決方式就是定義一個接口,需要關注該事件的控件來實現這個接口。然后事件觸發的地方來注冊/取消注冊這些對該事件感興趣的控件。
例如,陌陌依賴手機位置信息來獲取附近的用戶,所以在位置更新管理器(MmLocationManager)中定義了一個接口來監聽位置更新的事件(MmLocationListener):
interface MmLocationListener { void onLocationChanged(Location location); }
然后在應用的各個需要響應該事件的地方來實現上面的接口,然后在位置更新管理器(MmLocationManager)中注冊/取消注冊事件監聽接口的實現類:
mLocationManager.get().register(this);
當不在需要監聽的時候,取消注冊
mLocationManager.get().unregister(this);
問題
上面的解決方案是沒問題的,但是不是理想方案。每個控件實現這個接口,導致這些控件和位置管理器注冊強耦合在一起。這還意味著,當單元測試的時候,您需要模擬(mocked)位置管理器來生成位置更新事件。
隨著應用功能的增加,需要監聽的事件越來越多,而越來越多的控件需要監聽不同的事件,則導致越來越多的控件需要注冊到各種事件管理器上:
// 代碼開始變得無法控制… mLocationManager.get().register(this); userAuthenticator.get().register(this); settingsManager.get().register(this); syncManager.get().register(this); configurationMonitor.get().register(this);
注意:上面的每個事件的注冊,都要實現對應的時間更新接口。
注冊和取消注冊這些事件慢慢的會變得越來越難以管理。導致測試越來越困難,并將導致開發者的效率越來越低,同時在您的應用中越來越容易引入各種奇怪的Bug。
解決方案
為了找出該問題的優雅解決方案,從一個意想不到的的地方借鑒點經驗 ― Swing應用。 Event Bus模式 ― 也被稱為Message Bus或者發布者/訂閱者(publisher/subscriber)模式 ― 可以讓兩個組件相互通信,但是他們之間并不相互知曉。
和需要注冊各個事件的監聽器相比,一個組件現在只用在Event Bus上注冊一次即可:
bus.register(this);
上面的注冊告訴Event Bus我們現在希望接收各個事件的更新。 然后Bus檢測該類中每個帶有@Subscribe注解的函數,當相關的事件發生的時候就調用這些帶有注解的函數。
所以上面示例中的位置監聽功能,不用實現位置監聽接口和里面的函數了,只需要提供一個帶有@Subscribe注解的函數即可:
@Subscribe public void locationChanged(LocationChangedEvent event) { // TODO React to location change. }
現在Event Bus會把所有的LocationChangedEvent 事件都發送給上面的函數。
MmLocationManager 類不用注冊監聽器了,當位置改變的時候 只需要向Event Bus發布事件即可:
bus.post(new LocationChangedEvent(37.892818, -121.772608));
這樣 組件間相互解耦了,而單元測試也變得簡單了。任何事件都可以發布給Event Bus,然后Event Bus會找到對該事件感興趣的函數來調用。
注意:您也許已經發現該模式在Android上層也存在 ― Intent系統就是這樣設計的!
下面介紹兩個Android系統的Event Bus模式類庫。
Otto ― Android系統的Event Bus類庫
Otto是Square公司在他們應用中使用的Event Bus實現。從Guava中演變而來,并且專注于Android平臺。
https://github.com/square/otto
EventBus ― 另外一個由greenrobot實現的Event Bus類庫
https://github.com/greenrobot/EventBus