Android EventBus3.0 深入了解

xin891120 8年前發布 | 16K 次閱讀 Android EventBus Android開發 移動開發

通過上一篇文章《EventBus 3.0相見恨晚》對EventBus3.0的原理及使用方法有了簡單了解。

下面就其原理和使用方法做更深入細致的了解。

EventBus設計模式

EventBus is an open-source library for Android using the publisher/subscriber pattern for loose coupling

這句話是greenrobot官網對EventBus的解釋。

EventBus是一個針對Android為了松耦合的基于事件發布/訂閱模式(觀察者模式)的開源庫。

框架

從上圖我們可以看到EventBus的設計結構非常簡單。

EventBus 使用方式

訂閱者方法詳細用法

從之前對EventBus的Demo我們可以看到,使用EventBus的關鍵是訂閱者方法的實現,也就是事件處理方法onMessageEvent的實現。這個方法,才是我們處理數據、實現UI更新的關鍵。

 @Subscribe(threadMode = ThreadMode.POSTING,sticky = false,priority = 1)
    public void onMessageEvent(MessageEvent event) {
        tv.setText(event.message);
    }

這里再強調一遍,這個訂閱者方法一定要添加@Subscribe這個注解,這個注解的完整參數如上,后面是一些可選的參數。下面就各個參數做一下分析:

EventBus的ThreadMode總共有四種,并且都是在訂閱者中的@Subscribe里進行制定的。下面就來看一下這四種ThreadMode。

四種threadMode模式

  • ThreadMode: POSTING
            這時候訂閱者執行的線程與事件的發布者所在的線程為同一個線程。也就是說事件由哪個線程發布的,訂閱者就在哪個線程中執行。這個也是EventBus默認的線程模式;由于沒有線程的切換,也就意味消耗的資源也是最小的。如果一個任務不需要多線程的,也是推薦使用這種ThreadMode的。

我們之前的Demo就是這樣,事件發送是在SecondActivity的主線程,那么onMessageEvent在默認情況下必然會在主線程執行。

  • ThreadMode: MAIN
            從它的名字就很容易可以看出,他是在Android的主線程中運行的。如果提交的線程也是主線程,那么他就和ThreadMode.POSTING一樣了。當然在這里由于是在主線程中運行的,所以在這里就不能執行一些耗時的任務。

還是之前的Demo,我們在SecondActivity中實現登錄操作,正常情況下這必然是個網絡請求,而這個網絡請求必然不會在主線程(UI線程)中發生,所以,當我們完成網絡請求發布事件的時候,發布事件所在的線程就不再是UI線程了,我們的onMessageEvent的ThreadMode就不能為默認值,必須指定為MAIN,確保其在主線程進行對UI的操作。

  • ThreadMode: BACKGROUND
            這種模式下,我們的訂閱者將會在后臺線程中執行。如果發布者是在主線程中進行的事件發布,那么訂閱者將會重新開啟一個子線程運行,若是發布者在不是在主線程中進行的事件發布,那么這時候訂閱者就在發布者所在的線程中執行任務。

這種情況,一般是我們需要在訂閱者方法中進行耗時的操作。

  • ThreadMode: ASYNC
            在這種模式下,訂閱者將會獨立運行在一個線程中。不管發布者是在主線程還是在子線程中進行事件的發布,訂閱者都是在重新開啟一個線程來執行任務。

這里我們可以簡單的測試一下,加深理解:

下面代碼用4種不同的Thread模式訂閱同一事件MessageEvent

@Subscribe(threadMode = ThreadMode.PostThread)
public void onMessageEventPostThread(MessageEvent messageEvent) {
    Log.e("PostThread", Thread.currentThread().getName());
}

@Subscribe(threadMode = ThreadMode.MainThread)
public void onMessageEventMainThread(MessageEvent messageEvent) {
    Log.e("MainThread", Thread.currentThread().getName());
}

@Subscribe(threadMode = ThreadMode.BackgroundThread)
public void onMessageEventBackgroundThread(MessageEvent messageEvent) {
    Log.e("BackgroundThread", Thread.currentThread().getName());
}

@Subscribe(threadMode = ThreadMode.Async)
public void onMessageEventAsync(MessageEvent messageEvent) {
    Log.e("Async", Thread.currentThread().getName());
}
  • 在主線程發布事件:
Log.e("postEvent", Thread.currentThread().getName());
EventBus.getDefault().post(new MessageEvent());

看一下日志輸出:

主線程中發布事件

可以看出,在主線程發布事件,那么Post和Main 模式下,訂閱者方法也是在主線程執行,其他兩種模式下分別是獨立的線程。

  • 在子線程發布事件
    new Thread(new Runnable() {
                  @Override
                  public void run() {
                      Log.e("postEvent", Thread.currentThread().getName());
                      EventBus.getDefault().post(new MessageEvent());
                  }
              }).start();
    看一下日志輸出:

子線程發布事件

子線程發布事件時,只有ThreadMode 為MAIN時,訂閱者方法才會在主線程執行。其余都是在獨立的線程。

sticky的意義

這個單詞sticky的意思是粘貼性,這里的意思就是在發送事件之后再訂閱該事件也能收到該事件
默認為false。

priority 的意義

這個priority的意義是優先級,這里主要說一下結論和注意事項

  • 優先級高(priority值大)的將會首先接收到發布者所發布的事件
  • 優先級只是針對于相同的ThreadMode中
  • 默認的優先級為0

平時使用時,可以根據實際需求做調整。

訂閱者索引相關

在性能方面,EventBus 3由于使用了注解,比起使用反射來遍歷方法的2.4版本遜色不少。但開啟索引后性能遠遠超出之前的版本。下面就來看一下對于EventBus的另一種使用方式。

在Project的build.gradle中添加如下代碼:

buildscript {
    dependencies {
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
    }
}

然后在app的build.gradle中添加如下代碼:

apply plugin: 'com.neenbedankt.android-apt'

dependencies {
    compile 'org.greenrobot:eventbus:3.0.0'
    apt 'org.greenrobot:eventbus-annotation-processor:3.0.1'
}
apt {
    arguments {
        eventBusIndex "com.example.myapp.MyEventBusIndex"
    }
}

重新rebuild之后會在build目錄下面生成MyEventBusIndex文件,文件名可以自定義。下面就來看一下如何使用這個MyEventBusIndex.我們可以自定義設置自己的EventBus來為其添加MyEventBusIndex對象。代碼如下所示:

EventBus eventBus = EventBus.builder().addIndex(new MyEventBusIndex()).build();

我們也能夠將MyEventBusIndex對象安裝在默認的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的思考

不使用EventBus的前提下,在Android內部各個組件的交互通信可以說是雜亂無章,各種接口定義,注冊、回調。搞得整個代碼有著很嚴重的耦合,接口中多一個參數,參數類型變化都會導致很多地方要改。

傳統的組件交互

這種思路注定了隨著項目越來越龐大,耦合會越發的嚴重,所以勢必需要一種新的思路來解決這個問題。

而EventBus的出現,很好的解決了這個問題

EventBus

從這兩幅圖,我們可以清晰的感受到,EventBus給我們代碼整體的結構帶來多么大的益處,單從圖上就可以看到明顯減少了各個組件之間的耦合;同時從代碼角度出發,EventBus短短幾行代碼,就可以實現之前通過接口-注冊接口-回調 等一系列的工作才能完成的工作量。

當然,凡事要好必然有壞,EventBus使用方式的簡單,也會導致我們在出現問題時無從下手,這就需要長時間的積累經驗了。但是總的來說,還是利大于弊吧,所以當我們項目有大量的事件交互時,EventBus不失為一種好的選擇!


來自:http://www.jianshu.com/p/dbdb92f6f0e2

 

 本文由用戶 xin891120 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!