發佈/訂閱事件總線。它可讓咱們很輕鬆的實如今Android各個組件之間傳遞消息,而且代碼的可讀性更好,耦合度更低。java
在使用EventBus以前,須要添加依賴:模塊的build.gradle文件中android
dependencies { implementation 'org.greenrobot:eventbus:3.1.1' }
一、步驟一:定義事件
事件沒有任何特定的要求,一個普通的java對象網絡
public class MessageEvent { public final String message; public MessageEvent(String message) { this.message = message; } }
二、步驟二:準備訂閱者
訂閱者實現事件處理方法(也稱爲 訂閱方法),這些方法在事件發佈時調用。它們要用@Subscribe註解定義,並指定線程模型。
注意:EventBus3.0方法名能夠隨意起,不像EventBus2.0版本有命名約定。併發
// 當MessageEvent事件發佈時,該方法在UI線程調用(由於線程模型指定爲UI線程) @Subscribe(threadMode = ThreadMode.MAIN) public void onMessageEvent(MessageEvent event) { Log.d(TAG, event.message + " ### receive thread name: " + Thread.currentThread().getName()); } // 該方法在發佈MessageEvent事件的線程調用(沒有指定線程模型,會在發佈事件線程執行) @Subscribe public void handleSomethingElse(MessageEvent event) { }
訂閱者也須要註冊或者註銷到總線,只有訂閱者被註冊才能收到消息。在Android中,一般根據Activity或者Fragment的生命週期進行註冊,例如:Activity中的onCreate/onDestroyapp
// Activity 中 @Override protected void onCreate(Bundle savedInstanceState) { EventBus.getDefault().register(this); } @Override protected void onDestroy() { EventBus.getDefault().unregister(this); super.onDestroy(); }
三、發佈事件
在代碼的任何地方均可以發佈事件。全部註冊訂閱者將接收匹配事件類型async
EventBus.getDefault().post(new MessageEvent("post thread name: " + Thread.currentThread().getName());
EventBus提供了線程模型,能夠幫助咱們處理線程:訂閱方法能夠在不一樣於發佈事件的線程中處理。常見用例涉及到UI更新,在Android中,UI更新必須在UI(main)線程執行。另外一方面,網絡或者任何耗時的任務不能運行在主線程中。EventBus幫助咱們處理這個任務並與UI線程同步(無需深刻研究線程切換,或使用AsyncTask等)。ide
在EventBus中,可使用四個線程模型之必定義調用事件處理函數的線程。函數
訂閱事件函數指定了線程模型爲POSTING:該事件在哪一個線程發佈出來的,事件處理函數就會在這個線程中運行,也就是說發佈事件和事件處理函數在同一個線程。在線程模型爲POSTING的事件處理函數中儘可能避免執行耗時操做,由於它會阻塞事件的傳遞,甚至有可能會引發ANR。post
// ThreadMode 是可選的,默認值爲ThreadMode.POSTING // 與發佈事件在同一個線程調用 @Subscribe(threadMode = ThreadMode.POSTING) public void onMessageEventPosting(MessageEvent event) { Log.d(TAG, "posting: " + Thread.currentThread().getName()); }
訂閱事件函數指定了線程模型爲MAIN:不論事件是在哪一個線程中發佈出來的,該事件處理函數都會在UI線程中執行。該方法能夠用來更新UI,可是不能處理耗時操做。性能
// 在Android UI 主線程調用 @Subscribe(threadMode = ThreadMode.MAIN) public void onMessageEventMain(MessageEvent event) { Log.d(TAG, "main: " + Thread.currentThread().getName()); }
訂閱事件函數指定了線程模型爲MAIN_ORDERED:不論事件在哪一個線程中發佈出去,該事件處理函數都會在UI線程中執行。與MAIN模型區別在於:事件處理函數老是在主線程排隊執行
// 在Android UI 主線程調用 @Subscribe(threadMode = ThreadMode.MAIN_ORDERED) public void onMessageEventMainOrdered(MessageEvent event) { Log.d(TAG, "main_ordered: " + Thread.currentThread().getName()); }
訂閱事件函數指定了線程模型爲BACKGROUND:若是事件是在UI線程中發佈出來的,那麼該事件處理函數就會在單線程串行執行,儘可能不要堵塞線程;若是事件原本就是子線程中發佈出來的,那麼該事件處理函數直接在發佈事件的線程中執行。在此事件處理函數中禁止進行UI更新操做。
// 在後臺線程調用 @Subscribe(threadMode = ThreadMode.BACKGROUND) public void onMessageEventBackground(MessageEvent event) { Log.d(TAG, "background: " + Thread.currentThread().getName()); }
訂閱事件函數指定了線程模型爲ASYNC:不管事件在哪一個線程發佈,該事件處理函數都會在新建的子線程中執行。發佈事件不須要等待,適合耗時的事件處理函數,但避免同時觸發大量長時間的事件處理函數。 一樣,此事件處理函數中禁止進行UI更新操做。
// 在單獨的線程調用 @Subscribe(threadMode = ThreadMode.ASYNC) public void onMessageEventAsync(MessageEvent event) { Log.d(TAG, "async: " + Thread.currentThread().getName()); }
爲了驗證這些線程模型,寫了一個簡單的例子:
@Subscribe(threadMode = ThreadMode.POSTING) public void onMessageEventPosting(MessageEvent event) { Log.d(TAG, "posting: " + Thread.currentThread().getName()); } @Subscribe(threadMode = ThreadMode.MAIN) public void onMessageEventMain(MessageEvent event) { Log.d(TAG, "main: " + Thread.currentThread().getName()); } @Subscribe(threadMode = ThreadMode.BACKGROUND) public void onMessageEventBackground(MessageEvent event) { Log.d(TAG, "background: " + Thread.currentThread().getName()); } @Subscribe(threadMode = ThreadMode.ASYNC) public void onMessageEventAsync(MessageEvent event) { Log.d(TAG, "async: " + Thread.currentThread().getName()); }
分別使用上面四個方法訂閱同一事件,打印他們運行所在的線程。首先咱們在UI線程中發佈一條MessageEvent的消息,看下日誌打印結果是什麼。
findViewById(R.id.post_events).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Log.d(TAG, "postEvent: " + Thread.currentThread().getName()); EventBus.getDefault().post(new MessageEvent()); } });
打印結果以下:
8614-8614/com.example.eventbus.demo D/event_bus: postEvent: main 8614-8664/com.example.eventbus.demo D/event_bus: ASYNC: pool-1-thread-1 8614-8614/com.example.eventbus.demo D/event_bus: MAIN: main 8614-8614/com.example.eventbus.demo D/event_bus: POSTING: main 8614-8665/com.example.eventbus.demo D/event_bus: BACKGROUND: pool-1-thread-2
從日誌打印結果能夠看出,若是在UI線程中發佈事件,則線程模型爲POSTING的事件處理函數也執行在UI線程,與發佈事件的線程一致。線程模型爲ASYNC的事件處理函數執行在名字叫作pool-1-thread-1的新的線程中。而MAIN的事件處理函數執行在UI線程,BACKGROUND的時間處理函數執行在名字叫作pool-1-thread-2的新的線程中。
咱們再看看在子線程中發佈一條MessageEvent的消息時,會有什麼樣的結果。
findViewById(R.id.post_events).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { new Thread(new Runnable() { @Override public void run() { Log.d(TAG, "postEvent: " + Thread.currentThread().getName()); EventBus.getDefault().post(new MessageEvent()); } }).start(); } });
打印結果以下:
8754-8807/com.example.eventbus.demo D/event_bus: postEvent: Thread-2 8754-8807/com.example.eventbus.demo D/event_bus: BACKGROUND: Thread-2 8754-8807/com.example.eventbus.demo D/event_bus: POSTING: Thread-2 8754-8808/com.example.eventbus.demo D/event_bus: ASYNC: pool-1-thread-1 8754-8754/com.example.eventbus.demo D/event_bus: MAIN: main
從日誌打印結果能夠看出,若是在子線程中發佈事件,則線程模型爲POSTING的事件處理函數也執行在子線程,與發佈事件的線程一致(都是Thread-2)。BACKGROUND事件模型也與發佈事件在同一線程執行。ASYNC則在一個名叫pool-1-thread-1的新線程中執行。MAIN仍是在UI線程中執行。
EventBus提供一個EventBusBuilder配置類。例如:如何構建沒有log信息輸出的EventBus,例如沒有訂閱者沒有註冊。
EventBus eventBus = EventBus.builder() .logNoSubscriberMessages(false) .sendNoSubscriberEvent(false) .build();
另外一個例子,當事件處理函數執行拋出異常
EventBus eventBus = EventBus.builder().throwSubscriberException(true).installDefaultEventBus();
注意:默認狀況,EventBus會捕獲訂閱函數拋出的異常,併發送SubscriberExceptionEvent事件,該事件不是必須處理的。
咱們能夠在代碼的任何地方經過EventBus.getDefault()獲取共享的EventBus實例對象。EventBusBuilder也可使用installDefaultEventBus()函數配置默認的EventBus實例對象。
EventBus.builder().throwSubscriberException(BuildConfig.DEBUG).installDefaultEventBus();
除了上面講的普通事件外,EventBus還支持發送黏性事件。何爲黏性事件呢?簡單講,就是在發送事件以後再訂閱該事件也能收到該事件,跟黏性廣播相似。具體用法以下:
寫一個簡單的黏性事件例子說明:
int index = 0; findViewById(R.id.post_events).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 發佈黏性事件 EventBus.getDefault().postSticky(new MessageEvent("event: " + index++)); } }); findViewById(R.id.register).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // 註冊 EventBus.getDefault().register(MainActivity.this); } }); @Subscribe(threadMode = ThreadMode.MAIN, sticky = true) public void onMessageEventMain(MessageEvent event) { Log.d(TAG, "MAIN: " + event.message); }
代碼很簡單,有兩個點擊按鈕,一個用來發布黏性事件(沒發送一次,index+1),一個用來註冊。在沒有點擊註冊按鈕以前發送黏性事件,而後點擊註冊按鈕,會看到打印的log日誌以下:
11260-11260/com.example.eventbus.demo D/event_bus: MAIN: event: 0
這就是粘性事件,可以收到註冊以前發送的消息。可是它只能收到最新的一次消息,好比說在未註冊以前已經發送了多條黏性消息了,而後再註冊只能收到最近的一條消息。這個咱們能夠驗證一下,咱們連續點擊5次發送黏性事件按鈕(index自增到4),而後再點擊註冊按鈕,打印結果以下:
11166-11166/com.example.eventbus.demo D/event_bus: MAIN: event: 4
由打印結果能夠看出,發送了5次黏性事件,但只收到最近的一條黏性事件。
最後一個黏性事件會在註冊時自動傳遞給匹配的訂閱者,但有時須要手動檢查黏性事件更方便,另外還有可能須要刪除黏性事件,不須要在註冊時傳遞給匹配的訂閱者。
MessageEvent event = EventBus.getDefault().getStickyEvent(MessageEvent.class); if (event != null) { EventBus.getDefault().removeStickyEvent(event); }
removeStickyEvent有重載函數,當傳入的是Class時,返回保存的黏性事件。
MessageEvent event = EventBus.getDefault().removeStickyEvent(MessageEvent.class); if (event != null) { // Now do something with it }
訂閱索引在EventBus3.0是一個新的特性功能。可選的,能夠加快訂閱的註冊速度。
訂閱索引經過APT技術在編譯器生成的,在Android中,推薦使用。
訂閱索引前提條件:被@Subscriber註解標記且是public修飾。因爲APT技術自身的緣由,匿名中@Subscriber註解不能被識別。
當EventBus不可以使用索引,會自動回退使用反射技術實現,依然可以工做,僅僅影響性能。
若是你不是使用Android Gradle Plugin 版本是 2.2.0 或者更高,使用android-apt配置。
爲了啓動索引生成,須要使用annotationProcessor屬性將EventBus註解處理器添加到構建中,此外還須要設置eventBusIndex參數,以指定要生成的索引的徹底限定類。
android { defaultConfig { javaCompileOptions { annotationProcessorOptions { arguments = [ eventBusIndex : 'com.example.myapp.MyEventBusIndex' ] } } } } dependencies { implementation 'org.greenrobot:eventbus:3.1.1' annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.1.1' }
若是你在kotlin代碼中使用EventBus,須要kapt替代annotationProcessor
apply plugin: 'kotlin-kapt' // ensure kapt plugin is applied dependencies { implementation 'org.greenrobot:eventbus:3.1.1' kapt 'org.greenrobot:eventbus-annotation-processor:3.1.1' } kapt { arguments { arg('eventBusIndex', 'com.example.myapp.MyEventBusIndex') } }
若是上面的內容不能工做,您可使用Android-APT Gradle插件將EventBus註釋處理器添加到您的構建中
buildscript { dependencies { classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' } } apply plugin: 'com.neenbedankt.android-apt' dependencies { compile 'org.greenrobot:eventbus:3.1.1' apt 'org.greenrobot:eventbus-annotation-processor:3.1.1' } apt { arguments { eventBusIndex "com.example.myapp.MyEventBusIndex" } }
成功構建項目後,將生成使用eventBusIndex指定的類。
EventBus eventBus = EventBus.builder().addIndex(new MyEventBusIndex()).build();
或者若是想使用默認的EventBus實例對象
EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus(); // Now the default instance uses the given index. Use it like this: EventBus eventBus = EventBus.getDefault();
EventBus eventBus = EventBus.builder() .addIndex(new MyEventBusAppIndex()) .addIndex(new MyEventBusLibIndex()).build();
-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); }
AsyncExecutor與線程池相似,但處理失敗(異常)。失敗將引起異常,AsyncExecutor將這些異常包裝在一個事件中,該事件將自動發佈.
一般,能夠調用AsyncExecutor.create()來建立實例,並將其保存在應用程序範圍內。而後,實現RunnableEx接口並將其傳遞給AsyncExecutor的Execute方法。
與Runnable不一樣,RunnableEx可能引起異常。若是RunnableEx實現拋出異常,它將被捕獲幷包裝到ThrowableFailureEvent中,該事件將被髮布。
AsyncExecutor.create().execute( new AsyncExecutor.RunnableEx() { @Override public void run() throws LoginException { // No need to catch any Exception (here: LoginException) remote.login(); EventBus.getDefault().postSticky(new LoggedInEvent()); } } );
接收事件
@Subscribe(threadMode = ThreadMode.MAIN) public void handleLoginEvent(LoggedInEvent event) { // do something } @Subscribe(threadMode = ThreadMode.MAIN) public void handleFailureEvent(ThrowableFailureEvent event) { // do something }
若是要自定義AsyncExecutor實例,請調用靜態方法AsyncExecutor.builder()。它將返回一個生成器,用於自定義EventBus實例、線程池和失敗事件的類。
另外一個自定義選項是執行範圍,它給出故障事件上下文信息。例如,故障事件可能僅與特定活動實例或類別相關。
若是自定義故障事件類實現HasExecutionScope接口,AsyncExecutor將自動設置執行範圍。與此相似,您的用戶能夠查詢其執行範圍的失敗事件,並根據它作出反應。