一、什麼是EventBus
EventBus 功能:使用觀察者模式通過解耦發佈者和訂閱者,在Android中實現事件傳遞,更優雅的實現多個模塊之間的通信。
Android 傳統的模塊間通信,在fragment之間和activity之間可通過Intent或者activity之間通過startActivity+onActivityResult 進行數據傳遞,在線程間通信主要使用了Handle,而 EventBus能更優雅的方式來實現其通信,簡化使用複雜度,同時避免產生大量內部類。
二、實現方式
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) ;
}