通過上一篇文章《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ā)布事件,那么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ā)布事件時,只有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)致很多地方要改。
這種思路注定了隨著項目越來越龐大,耦合會越發(fā)的嚴重,所以勢必需要一種新的思路來解決這個問題。
而EventBus的出現(xiàn),很好的解決了這個問題
從這兩幅圖,我們可以清晰的感受到,EventBus給我們代碼整體的結(jié)構(gòu)帶來多么大的益處,單從圖上就可以看到明顯減少了各個組件之間的耦合;同時從代碼角度出發(fā),EventBus短短幾行代碼,就可以實現(xiàn)之前通過接口-注冊接口-回調(diào) 等一系列的工作才能完成的工作量。
當然,凡事要好必然有壞,EventBus使用方式的簡單,也會導(dǎo)致我們在出現(xiàn)問題時無從下手,這就需要長時間的積累經(jīng)驗了。但是總的來說,還是利大于弊吧,所以當我們項目有大量的事件交互時,EventBus不失為一種好的選擇!