Android EventBus3.0 深入了解
通過上一篇文章《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