Android EventBus二三事

廢話不少的前言

EventBus,也即事件總線。在wiki上有關於Event Monitor的一個說法:java

Event monitoring makes use of a logical bus to transport event occurrences from sources to subscribers, where event sources signal event occurrences to all event subscribers and event subscribers receive event occurrences.
事件監聽器經過邏輯總線來將發生的事件從發生源傳輸到訂閱者。事件發生源在事件發生的時候可以發出信號給全部訂閱者,訂閱者則可以接收到發生的事件。android

我的猜想事件總線這種說法源至於計算機內經常使用的幾個總線結構【好比CPU總線,IO總線等】。他們有一個共同的特定就是負責傳遞某種 object 到指定的地方。好比數據總線負責的是CPU到RAM,地址總線負責的是RAM到RAM傳遞數據。git

這條詞條的分類是在操做系統這個目錄下,這也能夠理解成是在操做系統較早地使用了這種模式。
在Java內置的包中也有一種EventBus,但它是基於接口的。Google在Guava中實現了一套本身的EventBus,Google實現的總線庫更具可讀性,由於它使用Annotion標記訂閱者和生產者,方法名能夠自定義,更具意義。github

Android中的EventBus

在這裏先說說Android內置的兩種跟EventBus相似的機制。IntentBroadcastReceiver
這二者均可以起到跟事件總線相似的效果。註冊廣播接收器和單純發一個intent就能夠喚起其餘組件,提醒其餘組件更新,這是很是方便的,同時也是下面提到的兩個開源方案所作不到的。
但這種機制也有很差地方,它們內部的實現都須要 IPC,雖然Android的Binder使得IPC簡單了點~~(仍是蠻複雜的吧)~~,但傳遞效率上會是個問題。若是本身徹底不須要IPC,下面說到的兩個項目會更加適合。
在Android中彷佛就只有兩個Eventbus的開源項目,Square的otto和GreenRobot的EventBus(如下稱GEventBus)。ide

otto

otto是square開源的一個基於Guava的EventBus的項目。跟Guava同樣,otto使用Annotion做爲事件的標記,@Subscribe即事件的處理者,@Produce是基本事件的生產者。舉個官方給的例子:函數

<!-- lang: java -->post

@Subscribe public void answerAvailable(AnswerAvailableEvent event) {  
    // TODO: React to the event somehow!  
}  
@Produce public AnswerAvailableEvent produceAnswer() {  
    return new AnswerAvailableEvent(this.lastAnswer);  
}  
// 訂閱者和發佈者均須要向同一個總線註冊  
bus.register(this);

使用起來就是這麼簡單。訂閱者向總線註冊,若是此時存在一個生產者,那麼這個生產者的方法會被調用以產生一個初始對象來初始化訂閱者。另外,在總線接收到對應的事件以後,訂閱者的方法就也會被調用。this

bus.post(new AnswerAvailableEvent(100));

post事件並不須要對象向總線註冊。post事件後,若是存在事件的訂閱者那麼訂閱者的方法就會被調用。若是不存在訂閱者,那麼事件會被包裹成DeadEvent重拋。(待作文章細說)。 默認狀況下,otto只支持主線程的事件。好比下面代碼所示,bus1和bus2是等價的。若是將策略設置爲ANY,那麼也能夠在非主線程執行。只是不建議,這個庫適合的場景也就只是在主線程而已,其餘環境下會出現什麼情況不作保證。google

Bus bus1 = new Bus();  
Bus bus2 = new Bus(ThreadEnforcer.MAIN);  
// Bus bus3 = new Bus(ThreadEnforcer.ANY);

小問題

要想在基類中註冊事件要花點心思。(不能經過this來向bus註冊事件,由於在子類中的方法調用super到父類方法時,this指的永遠是子類)。要想在基類註冊事件,基本上只能寫一個成員變量,在變量內部註冊方法。邏輯會略奇怪。另外,可能須要本身實現一個BusProviderurl

GEventBus

greenrobot的另外一個比較出名的開源項目(另一個是greenDAO)。一樣是EventBus,但這個項目不是基於Annotion的。由於Android的Runtime Annotion處理起來效率實在是不高,尤爲是在4.0以前。在4.0以後也不見得效率有多大提升
GEventBus使用的是字符串匹配,默認訂閱者會調用帶onEvent或者說是EventBus類裏面的DEFAULT_METHOD_NAME做爲前綴的方法。只有仔細跟蹤代碼以後,你纔會發現它的幾種線程模型是這麼用的:

public void onEvent(SimpleEvent event) {};  
public void onEventBackgroundThread(SimpleEvent event) {};  
public void onEventMainThread(SimpleEvent event) {};  
public void onEventAysnc(SimpleEvent event) {};

其餘操做跟otto相似。只是GEventBus不支持生產者方法。(題外話:我是看了otto的demo以後才知道GEventBus的通常用法= =)

問題

  1. 跟otto同樣,若是用繼承可能會存在問題。但具體的問題大概剛好相反,若是一個父類向總線註冊了事件,在otto裏面是隻有子類註冊了事件,但在GEventBus中會是子類和父類都註冊了。若是父類中註冊了總線,那麼子類中必須實現一個onEvent*方法,不然程序就會崩掉。解決方案,也算有吧,本身實現了一個蠻挫的暫時性解決方案 issus98
  2. 默認方法是隻能帶一個參數的,若是傳入多個參數,那麼GEventBus會徹底忽略它,並且也不會報任何錯誤。這是個小細節,注意看文檔的話大概也知道只能用一個參數,可是相較於otto會對參數不符合主動拋出一個RuntimeException,我始終以爲GEventBus有問題。

我的見解

注意如下會有不少我的的喜愛致使的吐槽=_=

我的以爲最純正的是otto,即便GreenRobot的EventBus稱它的效率比前者高出許多(有benchmark,因此算是事實)。可是一個開源項目,看的並不僅是效率問題吧。
在知乎上看到過一個問題:一個開源軟件爲什麼成功。一個好的開源項目也是同理,其中有個答案提到的幾點我很是贊同。

  1. 對新手友好。
    這點在EventBus上我徹底看不到。沒有Demo,文檔只是關於項目的一個簡單介紹,還有吹噓他們比起otto效率如何如何地高,功能多強大。
    另外還有一點,EventBus的文檔極具誤導性。對於TODO的feature,它居然擺在功能介紹的模塊裏,即便它前面加上了沒有加粗的TODO。在Trinea的開源集合裏面就把那些TODO的內容加到了EventBus的功能裏面。 當初在看到它的效率如此地高,就決定選它了。但是沒有demo,徹底不知道怎麼入手。
  2. 及時接收反饋。
    EventBus上如今還有還有2012年的issues未關閉,並且不少都沒有項目的人出來解答。不像otto,只要不是太弱智,基本上每一個issues都會有反饋。

在發現沒有Demo以後,我研究過GEventBus的代碼。邏輯是搞懂了,但也發覺了GEventBus的代碼是如此地不友好,沒有關鍵的註釋,名字又表意不清。最最沒法容忍的是,它將一堆函數標記爲@depreated,即便如今只能用它們來作。這個Annotion是來這麼用的嗎?提供了新的替代接口再標記爲廢棄如何?或者你乾脆設置成private好麼。
看otto的代碼是享受,雖然我只是稍微看了一下,沒有深刻去研究。Square彷佛還爲otto開發了IntellJ的plugin,能夠快速地瀏覽全部的訂閱者和生產者,這點也是GEventBus所不及的吧。
就我的喜愛而言,果真仍是選擇otto,用annotion寫起來仍是比較順心的。不像GEventBus,居然要用那麼長的名字才能做爲事件回調。輸入越多,越容易出錯,這點不管是在用戶體驗仍是敲代碼的時候都是同理。
我的拙見,歡迎交流>_< 22:09

博客連接:http://brainku.github.io/2014/09/20/something-about-eventbus/ 參考資料:

Android Annotion的bug 總線-百度百科 Guava EventBus EventBus otto

相關文章
相關標籤/搜索