EventBus3新特性及用法

題圖來(lái)自greenrobot官網(wǎng).png

EventBus 3 簡(jiǎn)介

EventBus是一種為了優(yōu)化Android組件之間事件傳遞的解耦工具尖淘,通過(guò)發(fā)布/訂閱事件總線來(lái)實(shí)現(xiàn)事件在不同組件之間的事件傳遞。
在EventBus 3之前著觉,greenrobot團(tuán)隊(duì)因?yàn)榭紤]性能原因所以比較抵觸使用注解框架村生。目前的EventBus3開始使用注解來(lái)申明訂閱事件的處理方法。雖然目前Android 6 和ART都有了饼丘,但是對(duì)于Java反射造成的性能影響還是沒能很好的解決趁桃。
在EventBus3中,greenrobot團(tuán)隊(duì)通過(guò)利用在編譯時(shí)檢索所有注解代碼肄鸽,然后生成一個(gè)包含所有在運(yùn)行時(shí)要花很大代價(jià)才能獲取的數(shù)據(jù)的類卫病,通過(guò)這種新的注解處理方式來(lái)提升性能,讓EventBus3比其他的eventbus會(huì)更加快典徘。在后文中會(huì)貼出和otto的性能比較蟀苛。

EventBus 3 和 EventBus 2.x 的區(qū)別

回調(diào)方法改動(dòng)

由于API的改動(dòng),會(huì)導(dǎo)致EventBus3和之前使用老版本的EventBus不兼容逮诲,因?yàn)橹鞍姹荆‥ventBus 2.x)帜平,在注冊(cè)完事件之后,會(huì)要求寫相應(yīng) onEvent()方法梅鹦,包括onEvent()裆甩、onEventAsync()onEventBackground()齐唆、onEventMainThread() 分別對(duì)應(yīng) @Subscrible 淑掌、@Subscrible(threadMode = ThreadMode.ASYNC)@Subscribe(threadMode = ThreadMode.BACKGROUND)蝶念、@Subscribe(threadMode = ThreadMode.MAIN) 抛腕。EventBus 3中在未聲明threadMob時(shí),默認(rèn)的線程模式為ThreadMode.POSTING媒殉。

異常容錯(cuò)處理

在EventBus3中担敌,如果在@Subscrible標(biāo)注的方法中,如果程序出錯(cuò)廷蓉,不會(huì)立即使程序crash全封,而是由EventBus攔截異常,并打印錯(cuò)誤日志桃犬。
用戶可以通過(guò)EventBusBuilder來(lái)配置獲取EventBus實(shí)例后的對(duì)象刹悴,來(lái)決定在處理event時(shí)是否需要拋出異常信息:

  eventBus = EventBus.builder().sendNoSubscriberEvent(false)        
              .sendSubscriberExceptionEvent(false)                       
              .throwSubscriberException(BuildConfig.DEBUG) //只有在debug模式下,會(huì)拋出錯(cuò)誤異常 
              .build();

以上代碼使用Builder設(shè)計(jì)模式攒暇,來(lái)構(gòu)建返回一個(gè)eventBus實(shí)例土匀。在調(diào)試階段,可以在程序出現(xiàn)異常時(shí)直接Crash發(fā)現(xiàn)錯(cuò)誤形用。

EventBus 3的使用

引入EventBus庫(kù)文件

在這里以gradle引用EventBus3庫(kù)為例進(jìn)行說(shuō)明

  compile 'org.greenrobot:eventbus:3.0.0'

定義一個(gè)事件集合

在使用EventBus時(shí)就轧,由于我個(gè)人比較喜歡將事件進(jìn)行統(tǒng)一管理和注釋(代碼如下),在其中定義了一個(gè)常用事件CommonEvent 實(shí)現(xiàn)了BaseEvents接口的兩個(gè)方法田度。

  public interface BaseEvents{
          setObject(Object obj);
          Object getObject();
          //事件定義
         enum CommonEvent implements BaseEvent {    
                LOGIN, //登錄    
                LOGOUT; //登出    
                private Object obj;    
                @Override    
                public void setObject(Object obj) {
                        this.obj = obj;    
                }    
                @Override    
                public Object getObject() {        
                        return obj;    
                }
          }
          // ... 其他事件定義
  }

這樣定義基本事件妒御,主要是為了便于事件管理添加和維護(hù)。避免新建很多不必要的類和重載過(guò)多的事件處理方法镇饺。

注冊(cè)事件及聲明事件處理方法

在Activity/Frament中乎莉,可以定義一個(gè)基類,在基類的onStart()和onStop()方法中對(duì)EventBus進(jìn)行注冊(cè)和注銷(官方推薦)奸笤。然后其他的子類集成基類惋啃,復(fù)寫和實(shí)現(xiàn)對(duì)應(yīng)的注冊(cè)方法即可。

  @Override
  public void onStart() {    
      super.onStart();  
      registEventBus();
  }

  @Override
  public void onStop() {   
      unRegistEventBus(); 
      super.onStop(); 
  }
  
  protected void registEventBus(){
      //子類如果需要注冊(cè)eventbus揭保,則重寫此方法
      //EventBus.getDefault().register(this);
  }
  
  protected void unRegistEventBus(){
      //子類如果需要注銷eventbus肥橙,則重寫此方法
      //EventBus.getDefault().unregister(this);
  }

注冊(cè)方法后,需要使用注解@Subscribe聲明處理事件的方法秸侣,以下代碼摘自官方介紹

  // This method will be called when a MessageEvent is posted
  @Subscribe
  public void onMessageEvent(MessageEvent event){    
        Toast.makeText(getActivity(), event.message, Toast.LENGTH_SHORT).show();
  }
  // This method will be called when a SomeOtherEvent is posted
  @Subscribe
  public void handleSomethingElse(SomeOtherEvent event){  
      doSomethingWith(event);
  }

發(fā)送事件

在發(fā)送事件時(shí)存筏,可以利用我們定義的一個(gè)BaseEvent進(jìn)行值的傳遞,因?yàn)槎x的是一個(gè)Object對(duì)象味榛,只需要保證類型轉(zhuǎn)換正確的前提下就可以隨意傳值了椭坚。示例如下:

  BaseEvents. CommonEvent event = BaseEvents.CommonEvent.LOGIN;
  event.setObject("Send Event"); //傳入一個(gè)String對(duì)象
  eventBus.post(event); //發(fā)布事件

如此就簡(jiǎn)單的實(shí)現(xiàn)了一個(gè)EventBus事件的發(fā)布。

StickyEvent

有時(shí)候搏色,我們會(huì)面對(duì)這樣一個(gè)問題善茎,那就是我們要把一個(gè)Event發(fā)送到一個(gè)還沒有初始化的Activity/Fragment,即尚未訂閱事件频轿。那么如果只是簡(jiǎn)單的post一個(gè)事件垂涯,那么是無(wú)法收到的烁焙,這時(shí)候,需要使用 StickyEvent耕赘,實(shí)例說(shuō)明:

BaseEvents. CommonEvent event = BaseEvents.CommonEvent.LOGIN;
event.setObject("Send StickyEvent"); 
EventBus.getDefault().postSticky(event);

在啟動(dòng)的Activity骄蝇,暫且定義為StickyActivity 繼承 BaseActivity 實(shí)現(xiàn) registEventBus()unRegistEventBus(),代碼片段如下:

  @Override
  public void onStart() {    
      super.onStart();  
      registEventBus();
  }

  @Override
  public void onStop() {   
      unRegistEventBus(); 
      super.onStop(); 
  }
  
  @Subscribe(sticky = true)    
  public void onEvent(BaseEvents. CommonEvent event) {        
      // UI updates must run on MainThread  
      if(event==BaseEvent.CommonEvent.LOGIN){
             String conent = (String)event.getObject();
             Log.d(TAG,"Content is : "+content);
      } 
      
  }      

  protected void registEventBus(){
      EventBus.getDefault().register(this);
  }
  
  protected void unRegistEventBus(){
      //EventBus.getDefault().unregister(this);
  }

以上就是發(fā)送一個(gè)stickyEvent的全過(guò)程操骡,下面講一下event的優(yōu)先級(jí)和攔截

事件處理優(yōu)先級(jí)及事件攔截

有時(shí)候在開發(fā)中九火,需要定義處理相同事件的優(yōu)先級(jí),我們先來(lái)看一下EventBus3對(duì)于Subscrible的注解定義源碼:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {   
       ThreadMode threadMode() default ThreadMode.POSTING;    
        /**    
         * If true, delivers the most recent sticky event (posted with    
         * {@link EventBus#postSticky(Object)}) to this subscriber (if event available).     
         */    
        boolean sticky() default false;    
        /** Subscriber priority to influence the order of event delivery.     
          * Within the same delivery thread ({@link ThreadMode}), higher priority subscribers will receive events before     
          * others with a lower priority. The default priority is 0. Note: the priority does *NOT* affect the order of     
          * delivery among subscribers with different {@link ThreadMode}s! 
          */    
        int priority() default 0;
}

可以看到 priority 的值默認(rèn)為0册招,優(yōu)先級(jí)隨定義的priority的值越大優(yōu)先級(jí)越高岔激。注意,在注解@Retention(RetentionPolicy.RUNTIME) 這一項(xiàng)是將注解寫入編譯的class文件中是掰,這個(gè)也是前面介紹到EventBus3解決注解反射導(dǎo)致性能問題的一個(gè)關(guān)鍵點(diǎn)虑鼎。
priority 實(shí)例簡(jiǎn)介:

@Subscribe(priority = 10)
public void handleEvent(BaseEvents.CommonEvent event){
      //....
}

有了事件處理優(yōu)先級(jí)的定義,那么我們就可以在高優(yōu)先級(jí)的事件處理中冀惭,將事件傳遞攔截下來(lái)震叙,經(jīng)過(guò)實(shí)際測(cè)試,只能在 threadMode = ThreadMode.POSTING 的注釋方法中才能攔截事件散休。官方如下介紹的:

Paste_Image.png

事件通常是被高優(yōu)先級(jí)的訂閱者取消媒楼,目前取消事件的處理方法僅限在ThreadMode 為 POSTING才能取消攔截事件。

注:POSTING的事件戚丸,表示當(dāng)前的post event和subscribe event兩者在同一個(gè)線程之中划址。

注意事項(xiàng)

  • 工作線程和UI線程之間的事件傳遞

在使用EventBus3時(shí),由于event 在任何地方都可以發(fā)布一個(gè)事件限府,那么在不同線程之間傳遞事件夺颤,比如在工作線程傳遞一個(gè)事件更新UI線程中的一個(gè)控件,則需要注意threadMode的切換胁勺。

  • 取消線程僅限于 threadMode處于POSTING模式才可以

    否則容易導(dǎo)致錯(cuò)誤:
    org.greenrobot.eventbus.EventBusException: This method may only be called from inside event handling methods on the posting thread

混淆

關(guān)于正式打包簽名世澜,混淆就不多說(shuō)了,把下面的混淆代碼署穗,粘貼到你的proguard-rules.pro下就ok了

  -keepattributes *Annotation*
  -keepclassmembers class ** {    
      @org.greenrobot.eventbus.Subscribe <methods>;
  }
  -keep enum org.greenrobot.eventbus.ThreadMode { 
        *; 
  }
  # Only required if you use AsyncExecutor
  -keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {    
      <init>(java.lang.Throwable);
  }

EventBus與otto的對(duì)比

這里的對(duì)比圖來(lái)自官方Github網(wǎng)站寥裂,對(duì)于線程之間傳遞Event的比較做了一個(gè)簡(jiǎn)化,簡(jiǎn)化為不同線程之間的事件傳遞案疲。

功能對(duì)比

EventBus otto
聲明事件處理方法 注解(從3.0開始使用預(yù)編譯解決性能問題) 注解
事件可擴(kuò)展 可以 可以
訂閱者擴(kuò)展 可以 不行
緩存大量最近事件 可以(用sticky events) 不行
事件生產(chǎn)者(比如編碼緩存的事件) 不行 可以
事件相同線程中傳遞 可以 可以
事件不同線程之間傳遞 可以 不可以

性能對(duì)比

性能對(duì)比封恰,由于個(gè)人沒有去寫具體的Demo測(cè)試,不過(guò)官方源碼有關(guān)于性能的比較測(cè)試源碼褐啡,一下截圖來(lái)自官方Github的比較結(jié)果:

Compare.png

總體而言诺舔,EventBus在各個(gè)方面完爆otto,當(dāng)然在實(shí)際項(xiàng)目中,還是應(yīng)該考慮性能和穩(wěn)定做出取舍低飒。

END

由于本人水平有限许昨,文中難免有所疏漏,有問題還請(qǐng)斧正逸嘀,開卷有益多多交流车要。如有疑問可以直接留言討論~ O(∩_∩)O哈哈~

參考資料:
EventBus官網(wǎng)文檔

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市崭倘,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌类垫,老刑警劉巖司光,帶你破解...
    沈念sama閱讀 212,454評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異悉患,居然都是意外死亡残家,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門售躁,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)坞淮,“玉大人,你說(shuō)我怎么就攤上這事陪捷』鼐剑” “怎么了?”我有些...
    開封第一講書人閱讀 157,921評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵市袖,是天一觀的道長(zhǎng)啡直。 經(jīng)常有香客問我,道長(zhǎng)苍碟,這世上最難降的妖魔是什么酒觅? 我笑而不...
    開封第一講書人閱讀 56,648評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮微峰,結(jié)果婚禮上舷丹,老公的妹妹穿的比我還像新娘。我一直安慰自己蜓肆,他們只是感情好颜凯,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著症杏,像睡著了一般装获。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上厉颤,一...
    開封第一講書人閱讀 49,950評(píng)論 1 291
  • 那天穴豫,我揣著相機(jī)與錄音,去河邊找鬼。 笑死精肃,一個(gè)胖子當(dāng)著我的面吹牛秤涩,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播司抱,決...
    沈念sama閱讀 39,090評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼筐眷,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了习柠?” 一聲冷哼從身側(cè)響起匀谣,我...
    開封第一講書人閱讀 37,817評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎资溃,沒想到半個(gè)月后武翎,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,275評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡溶锭,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評(píng)論 2 327
  • 正文 我和宋清朗相戀三年宝恶,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片趴捅。...
    茶點(diǎn)故事閱讀 38,724評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡垫毙,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出拱绑,到底是詐尸還是另有隱情综芥,我是刑警寧澤,帶...
    沈念sama閱讀 34,409評(píng)論 4 333
  • 正文 年R本政府宣布欺栗,位于F島的核電站毫痕,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏迟几。R本人自食惡果不足惜消请,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評(píng)論 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望类腮。 院中可真熱鬧臊泰,春花似錦、人聲如沸蚜枢。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)厂抽。三九已至需频,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間筷凤,已是汗流浹背昭殉。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工苞七, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人挪丢。 一個(gè)月前我還...
    沈念sama閱讀 46,503評(píng)論 2 361
  • 正文 我出身青樓蹂风,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親乾蓬。 傳聞我的和親對(duì)象是個(gè)殘疾皇子惠啄,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容

  • 文章基于EventBus 3.0講解。首先對(duì)于EventBus的使用上任内,大多數(shù)人還是比較熟悉的撵渡。如果你還每次煩于使...
    Hohohong閱讀 2,279評(píng)論 0 6
  • 項(xiàng)目到了一定階段會(huì)出現(xiàn)一種甜蜜的負(fù)擔(dān):業(yè)務(wù)的不斷發(fā)展與人員的流動(dòng)性越來(lái)越大,代碼維護(hù)與測(cè)試回歸流程越來(lái)越繁瑣死嗦。這個(gè)...
    fdacc6a1e764閱讀 3,174評(píng)論 0 6
  • EventBus用法及源碼解析目錄介紹1.EventBus簡(jiǎn)介1.1 EventBus的三要素1.2 EventB...
    楊充211閱讀 1,888評(píng)論 0 4
  • EventBus 是一個(gè)Android端優(yōu)化的 publish/subscribe 消息總線,簡(jiǎn)化了應(yīng)用程序各個(gè)組...
    王世軍Steven閱讀 1,846評(píng)論 4 21
  • 夏天姥闭,聒噪的季節(jié) 彌漫冰淇淋和花露水的味道 男孩子們的野氣開始收斂 女孩子們的柔軟開始加重 老師教導(dǎo)主任領(lǐng)導(dǎo)們的喋...
    愛吃素的閱讀 219評(píng)論 0 0