EventBus 這一篇還不夠

參考網站

EventBus 官網

環境配置

Gradle:

compile 'org.greenrobot:eventbus:3.0.0'

Maven:

<dependency>
    <groupId>org.greenrobot</groupId>
    <artifactId>eventbus</artifactId>
    <version>3.0.0</version>
</dependency>

原理介紹

這裏寫圖片描述

這張圖是比較形象的EventBus的工作原理圖,來自EventBus官網。我們可以形象的理解 EventBus 就是一個老司機,他有一個手冊,手冊上寫了路線有很多個餐館。這個時候有一個乘客上車,掏出手機給老師機看高德地圖上的一條路線說我要去這兒,老司機和餐館老闆有私下交易,所以他會拉着你挨個經過這些餐館。

上面有一些字是加粗的,老司機代表 EventBus,他在系統中是一個單例模式。所有的事件都要經過老司機,EventBus維護這一個HashMap,及一個手冊,手冊中記錄了很多的路線,每條路線上有很多餐館,路線就是我們的 事件(Message,這裏不夠形象,但是大概理解吧),好,路線上有很多個餐館,餐館就是 訂閱者,源碼中直接用這樣的結構存儲 Map<Object, List<Class<?>>>,Object就是事件, List 裏面存的訂閱者,當某種事件發生時,這條線上的所有訂閱者都會收到消息。至於還有一個乘客,發佈事件的人,其實就是 EventBus 本身,通過 EventBus.getDefault.post( Message ) 發送事件。

不知你理解了沒有,反正我理解了。如果不理解,那就看代碼吧!

入門例子

1. 定義 MessageEvent 對象

MessageEvent.java

public class MessageEvent {

    public final String message;

    public MessageEvent(String message) {
        this.message = message;
    }
}

2. 定義 處理 MessageEvent對象方法

MainActivity.java

@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {
    Toast.makeText(getActivity(), event.message, Toast.LENGTH_SHORT).show();
}

3. 註冊和取消訂閱

MainActivity.java

@Override
public void onStart() {
    super.onStart();
    EventBus.getDefault().register(this);
}

@Override
public void onStop() {
    EventBus.getDefault().unregister(this);
    super.onStop();
}

4. 發佈MessageEvent

EventBus.getDefault().post(new MessageEvent("Hello everyone!"));

理清思路

根據代碼,我們再來理解剛開始給的那張圖。如果使用EventBus這個老司機,那麼系統中就只會出現這麼一個老司機,它是單例模式,猜都能猜到,如果不信,你可以跟到源代碼EventBus.getDefault()中看,很基本的雙重鎖寫法:

static volatile EventBus defaultInstance;

public static EventBus getDefault() {
    if (defaultInstance == null) {
        synchronized (EventBus.class) {
            if (defaultInstance == null) {
                defaultInstance = new EventBus();
            }
        }
    }
    return defaultInstance;
}

然後通過 老司機.post(MessageEvent event) 出來的事件首先會傳給老司機,因爲系統中只有老司機知道有哪些訂閱者,並且他們訂閱的是什麼事件,他是通過註解的形式知道,@Subscribe表示該方法是要處理事件的,但是處理哪種事件呢?後面方法的參數決定 MessageEvent event。當然還不要忘了一定要給老司機 註冊他才認識你 EventBus.getDefault().register(this);,看源碼發現在在調用register的時候,EventBus纔會去找這個類中的 @Subscribe

@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {
    Toast.makeText(getActivity(), event.message, Toast.LENGTH_SHORT).show();
}

然後老司機拿到了 事件,並且看了看他的註冊表中有哪些傢伙訂閱了這個事件,當事件發生時,就把事件傳給他們,讓他們去處理。我覺得這樣寫應該所有人都能明白吧,至於底層的原理,可以看看EventBus的源碼。

ThreadMode 介紹

還記得上面的HelloWorld例子中我們用到了 @Subscribe(threadMode = ThreadMode.MAIN), MAIN 表示UI主線程,EventBus包括以下4種:

  • ThreadMode.POSTING默認模式,效率高,收到消息就執行。讓訂閱方法工作在與發佈消息同一個線程中。

  • ThreadMode.MAIN:讓訂閱方法始終切換到 UI主線程中執行

  • ThreadMode.BACKGROUND:啓動新線程,讓訂閱方法在新線程中執行

  • ThreadMode.ASYNC:異步模式,也是工作在新線程中,但是EventBus提供了線程池的管理,避免線程不停創建。

其實ThreadMode就是爲了讓我們更好的在線程中切換,大家都知道Android中UI主線程後後臺線程的區別,以前我們都要使用 Handler,AsyncTask 等來處理,代碼繁重,現在只需要一個註解就搞定了,是不是很爽。

這也讓我想到了 RxJava 中也提供了線程切換這樣的功能,流式編程就顯得更加的簡潔明瞭,並且還不需要這樣那樣的註冊,大總管之類的東西,所以現在大家都喜歡用RxJava。

EventBus其他功能介紹

StickyEvent

第一個是粘性事件,普通的事件我們通過post發送給EventBus,發送過後之後當前已經訂閱過的方法可以收到。但是如果有些事件需要所有訂閱了該事件的方法都能執行呢?例如一個Activity,要求它管理的所有Fragment都能執行某一個事件,但是當前我只初始化了3個Fragment,如果這時候通過post發送了事件,那麼當前的3個Fragment當然能收到。但是這個時候又初始化了2個Fragment,那麼我必須重新發送事件,這兩個Fragment才能執行到訂閱方法。

粘性事件就是爲了解決這個問題,通過 postSticky 發送粘性事件,這個事件不會只被消費一次就消失,而是一直存在系統中,知道被 removeStickyEvent 刪除掉。那麼只要訂閱了該粘性事件的所有方法,只要被register 的時候,就會被檢測到,並且執行。訂閱的方法需要添加 sticky = true 屬性。

EventBus.getDefault().postSticky(new MessageEvent("Hello everyone!"));
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
public void onEvent(MessageEvent event) {   
    textField.setText(event.message);
}

訂閱權限 Subscriber Priorities

你可以通過 在@Subscribe(priority = 1)中添加 priority 來定義訂閱的權限。權限越高的方法會在收到後越先執行。默認的 priority = 0

@Subscribe(priority = 1)
public void onEvent(MessageEvent event) {
    ...
}

取消事件分發

當你收到一個事件,並且調用 cancelEventDelivery 後,那麼這個事件不會再往下分發,要知道,有可能不止一個方法訂閱了該消息哦,取消後後面的方法就都收不到消息了。

// Called in the same thread (default)
@Subscribe
public void onEvent(MessageEvent event){
    // Process the event
    ...
    // Prevent delivery to other subscribers
    EventBus.getDefault().cancelEventDelivery(event) ;
}

還有其他的就自己看官網吧

寫在最後的話

我也是纔看EventBus,其實用起來的話感覺確實比以前的Handler那些機制要好用。但是EventBus這個大總管的方式其實我不太喜歡,如果項目不停迭代,代碼越來越多,管理Message就變得很繁瑣,出錯也不好排查。而且這個大總管是單例哦,如果App一直運行,或併發數目比較多時,可能性能上會有一些問題,當然這只是自己的猜想而已,我還沒有實踐過。

老司機開開車,其實我不是老司機,學習一點內容,分享分享,希望能拋磚引玉。自己也寫過很多的內容,總結下來也有一些經驗,對於我們這些初學者來說,請記住一定學習一個新技術,新框架的時候,從官網入手,英文就英文,多看兩遍就回了。所以我寫過的每一篇筆記都是附上官網地址的,所以你還在等什麼,看官網去吧,光看這點是不夠的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章