EventBus3.0 深入了解

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

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

EventBus設(shè)計模式##

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

這句話是greenrobot官網(wǎng)對EventBus的解釋。

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

框架

從上圖我們可以看到EventBus的設(shè)計結(jié)構(gòu)非常簡單。

EventBus 使用方式##

訂閱者方法詳細用法###

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

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

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

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

四種threadMode模式####

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

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

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

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

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

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

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

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

下面代碼用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());
}
  • 在主線程發(fā)布事件:
Log.e("postEvent", Thread.currentThread().getName());
EventBus.getDefault().post(new MessageEvent());

看一下日志輸出:

主線程中發(fā)布事件

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

  • 在子線程發(fā)布事件
new Thread(new Runnable() {
                @Override
                public void run() {
                    Log.e("postEvent", Thread.currentThread().getName());
                    EventBus.getDefault().post(new MessageEvent());
                }
            }).start();

看一下日志輸出:

子線程發(fā)布事件

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

sticky的意義####

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

priority 的意義####

這個priority的意義是優(yōu)先級,這里主要說一下結(jié)論和注意事項

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

平時使用時,可以根據(jù)實際需求做調(diào)整。

訂閱者索引相關(guān)###

在性能方面,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.我們可以自定義設(shè)置自己的EventBus來為其添加MyEventBusIndex對象。代碼如下所示:

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

我們也能夠?qū)yEventBusIndex對象安裝在默認的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,這樣會比通過反射的方式來解析注解效率更高。

關(guān)于EventBus的思考##

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

傳統(tǒng)的組件交互

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

而EventBus的出現(xiàn),很好的解決了這個問題

EventBus

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

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


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,761評論 25 709
  • 對于Android開發(fā)老司機來說肯定不會陌生,它是一個基于觀察者模式的事件發(fā)布/訂閱框架,開發(fā)者可以通過極少的代碼...
    飛揚小米閱讀 1,541評論 0 50
  • 原文鏈接:http://blog.csdn.net/u012810020/article/details/7005...
    tinyjoy閱讀 652評論 1 5
  • EventBus 是一個Android端優(yōu)化的 publish/subscribe 消息總線,簡化了應(yīng)用程序各個組...
    王世軍Steven閱讀 1,938評論 4 21
  • 今天是我來到簡書的第四十天。這四十天里,我每天都會來翻一翻簡書,偶爾看到一些有趣、動人的文章,心里就像撿到寶一樣的...
    淺夏時光閱讀 628評論 2 0

友情鏈接更多精彩內(nèi)容