EventBus 線程模式

一、什麼是EventBus

EventBus 功能:使用觀察者模式通過解耦發佈者和訂閱者,在Android中實現事件傳遞,更優雅的實現多個模塊之間的通信。

Android 傳統的模塊間通信,在fragment之間和activity之間可通過Intent或者activity之間通過startActivity+onActivityResult 進行數據傳遞,在線程間通信主要使用了Handle,而 EventBus能更優雅的方式來實現其通信,簡化使用複雜度,同時避免產生大量內部類。

Github

二、實現方式

1、定義一個消息事件 MessageEvent
public class MessageEvent {
    public final String message;
    public MessageEvent(String message) {
        this.message = message;
    }
}
2、實現訂閱者(消息接受) 通過@Subscribe 註釋定義
// This method will be called when a MessageEvent is posted (in the UI thread for Toast)
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {
    Toast.makeText(getActivity(), event.message, Toast.LENGTH_SHORT).show();
}
 
// This method will be called when a SomeOtherEvent is posted
@Subscribe
public void handleSomethingElse(SomeOtherEvent event) {
    doSomethingWith(event);
}
3、註冊EventBus (必須進行註冊操作,訂閱者才能接收到消息),需要在相應的生命週期內進行註銷,避免內存泄漏
@Override
public void onStart() {
    super.onStart();
    EventBus.getDefault().register(this);
}
 
@Override
public void onStop() {
    EventBus.getDefault().unregister(this);
    super.onStop();
}
4、發佈消息(從代碼的任何部分發布事件。所有與該事件類型匹配的當前註冊訂閱者都將收到它)
EventBus.getDefault().post(new MessageEvent("Hello everyone!"));

三、EventBus 線程模式

1、ThreadMode:POSTING
  • ​訂閱者方法將在發佈事件所在的線程中被調用。也就是發送消息在那個線程,接收消息就在那個線程,比如發送消息在主線程中,接收消息則也在主線中執行,發送消息在子線程中,接收消息則也在相同的子線程中執行
  • 這是 默認的線程模式。
  • 事件的傳遞是同步的,一旦發佈事件,所有該模式的訂閱者方法都將被調用。
  • 這種線程模式意味着最少的性能開銷,因爲它避免了線程的切換。因此,對於不要求是主線程並且耗時很短的簡單任務推薦使用該模式。
  • 使用該模式的訂閱者方法應該快速返回,以避免阻塞發佈事件的線程,這可能是主線程。
@Subscribe(threadMode = ThreadMode.POSTING)
public void onMessage(MessageEvent event) {
    log(event.message);
}
2、ThreadMode:MAIN
  • 訂閱者方法將在主線程(UI線程)中被調用。
  • 可以在該模式的訂閱者方法中直接更新UI界面。
  • 如果發佈事件的線程是主線程,那麼該模式的訂閱者方法將被直接調用。
  • 使用該模式的訂閱者方法必須快速返回,不應該做耗時的操作,以避免阻塞主線程。
// Called in Android UI's main thread
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessage(MessageEvent event) {
    textField.setText(event.message);
}
3、ThreadMode:MAIN_ORDERED
  • 訂閱者方法將在主線程(UI線程)中被調用。
  • 可以在該模式的訂閱者方法中直接更新UI界面。
  • 事件將先進入隊列然後才發送給訂閱者,所以發佈事件的調用將立即返回
  • 使用該模式的訂閱者方法必須快速返回,不應該做耗時的操作,以避免阻塞主線程。
  • 不同的線程發送消息,先加入隊列的會優先執行
// Called in Android UI's main thread
@Subscribe(threadMode = ThreadMode.MAIN_ORDERED)
public void onMessage(MessageEvent event) {
    textField.setText(event.message);
}
4、ThreadMode.BACKGROUND
  • 訂閱者方法將在後臺線程中被調用。
  • 如果發佈事件的線程不是主線程,那麼訂閱者方法將直接在該線程中被調用。
  • 如果發佈事件的線程是主線程,那麼將使用一個單獨的後臺線程,該線程將按順序發送所有的事件。
  • 使用該模式的訂閱者方法應該快速返回,以避免阻塞後臺線程。
// Called in the background thread
@Subscribe(threadMode = ThreadMode.BACKGROUND)
public void onMessage(MessageEvent event){
    saveToDisk(event.message);
}
5、ThreadMode:ASYNC
  • 訂閱者方法將在一個子線程中被調用。
  • 發佈事件的調用將立即返回。
  • 該模式適合訂閱者方法執行耗時操作,例如網絡訪問
  • 避免觸發大量的長時間運行的訂閱者方法,以限制併發線程的數量。
  • EventBus使用了一個線程池來有效地重用已經完成調用訂閱者方法的線程。
// Called in a separate thread
@Subscribe(threadMode = ThreadMode.ASYNC)
public void onMessage(MessageEvent event){
    backend.send(event.message);
}

四、粘性事件

有時候消息發佈早於觀察者的註冊,而我們仍然對這部分數據感興趣的話,可以使用EventBus的粘性事件處理,EventBus將粘性事件存儲起來 ,在觀察者註冊後,再將數據發送出去。

發佈粘性事件

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

新的Activity,在註冊期間,所有粘性訂閱者方法將立即獲得先前發佈的粘性事件

@Override
public void onStart() {
    super.onStart();
    EventBus.getDefault().register(this);
}
 
// UI updates must run on MainThread
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)
public void onEvent(MessageEvent event) {   
    textField.setText(event.message);
}
 
@Override
public void onStop() {
    EventBus.getDefault().unregister(this);    
    super.onStop();
}
手動獲取和刪除粘性事件

最後一個粘性事件在訂閱者註冊時會自動傳遞給匹配的訂閱者。但有時手動檢查粘性事件可能更方便。此外,可能需要刪除(使用)粘性事件,以便它們不再被傳遞。例:

MessageEvent stickyEvent = EventBus.getDefault().getStickyEvent(MessageEvent.class);
// Better check that an event was actually posted before
if(stickyEvent != null) {
    // "Consume" the sticky event
    EventBus.getDefault().removeStickyEvent(stickyEvent);
    // Now do something with it
}

MessageEvent stickyEvent = EventBus.getDefault().removeStickyEvent(MessageEvent.class);
// Better check that an event was actually posted before
if(stickyEvent != null) {
    // Now do something with it
}

五、優先事項和事件取消

雖然EventBus的大多數用例不需要優先級,也不需要取消事件,但在某些特殊情況下它們可能會派上用場。例如,如果應用程序位於前臺,則事件可能會觸發某些UI邏輯,但如果應用程序當前對用戶不可見,則會有不同的反應。

訂閱優先權

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

在同一傳遞線程(ThreadMode)中,較高優先級的訂戶將在優先級較低的其他訂戶之前接收事件。默認優先級爲0。

注意:*優先級不會影響****具有不同ThreadModes的訂閱者的傳遞順序*

取消活動發送
// Called in the same thread (default)
@Subscribe
public void onEvent(MessageEvent event){
    // Process the event
    ...
    // Prevent delivery to other subscribers
    EventBus.getDefault().cancelEventDelivery(event) ;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章