簡介
EventBus是一種用於Android的事件發佈-訂閱總線,由GreenRobot開發,Gihub地址是:EventBus。它簡化了應用程序內各個組件之間進行通信的複雜度,尤其是碎片之間進行通信的問題,可以避免由於使用廣播通信而帶來的諸多不便。
什麼是EventBus
EventBus Github地址
進入官網解釋:
- simplifies the communication between components
decouples event senders and receivers
performs well with Activities, Fragments, and background threads
avoids complex and error-prone dependencies and life cycle issues- makes your code simpler
- is fast
- is tiny (~50k jar)
- is proven in practice by apps with 100,000,000+ installs
- has advanced features like delivery threads, subscriber priorities, etc.
意思就是:EventBus能夠簡化各組件間的通信,讓我們的代碼書寫變得簡單,能有效的分離事件發送方和接收方(解耦),能避免複雜和容易出錯的依賴性和生命週期問題。
關於EventBus的概述
三要素
- Event 事件。它可以是任意類型。
- Subscriber 事件訂閱者。在EventBus3.0之前我們必須定義以onEvent開頭的那幾個方法,分別是onEvent、onEventMainThread、onEventBackgroundThread和onEventAsync,而在3.0之後事件處理的方法名可以隨意取,不過需要加上註解@subscribe(),並且指定線程模型,默認是POSTING。
- Publisher 事件的發佈者。我們可以在任意線程裏發佈事件,一般情況下,使用EventBus.getDefault()就可以得到一個EventBus對象,然後再調用post(Object)方法即可。
四種線程模型
EventBus3.0有四種線程模型,分別是:
- POSTING (默認) 表示事件處理函數的線程跟發佈事件的線程在同一個線程。
- MAIN 表示事件處理函數的線程在主線程(UI)線程,因此在這裏不能進行耗時操作。
- BACKGROUND 表示事件處理函數的線程在後臺線程,因此不能進行UI操作。如果發佈事件的線程是主線程(UI線程),那麼事件處理函數將會開啓一個後臺線程,如果果發佈事件的線程是在後臺線程,那麼事件處理函數就使用該線程。
- ASYNC 表示無論事件發佈的線程是哪一個,事件處理函數始終會新建一個子線程運行,同樣不能進行UI操作。
EventBus 使用
引入依賴
在使用之前先要引入如下依賴:
implementation 'org.greenrobot:eventbus:3.1.1'
定義事件
然後,我們定義一個事件的封裝對象。在程序內部就使用該對象作爲通信的信息:
public class MessageWrap {
public final String message;
public static MessageWrap getInstance(String message) {
return new MessageWrap(message);
}
private MessageWrap(String message) {
this.message = message;
}
}
發佈事件
然後,我們定義一個Activity:
@Route(path = BaseConstants.LIBRARY_EVENT_BUS_ACTIVITY1)
public class EventBusActivity1 extends CommonActivity<ActivityEventBus1Binding> {
@Override
protected void doCreateView(Bundle savedInstanceState) {
// 爲按鈕添加添加單擊事件
getBinding().btnReg.setOnClickListener(v -> EventBus.getDefault().register(this));
getBinding().btnNav2.setOnClickListener( v ->
ARouter.getInstance()
.build(BaseConstants.LIBRARY_EVENT_BUS_ACTIVITY2)
.navigation());
}
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onGetMessage(MessageWrap message) {
getBinding().tvMessage.setText(message.message);
}
}
這裏我們當按下按鈕的時候向EventBus註冊監聽,然後按下另一個按鈕的時候跳轉到拎一個Activity,並在另一個Activity發佈我們輸入的事件。在上面的Activity中,我們會添加一個監聽的方法,即onGetMessage
,這裏我們需要爲其加入註解Subscribe
並指定線程模型爲主線程MAIN
。最後,就是在Activity的onDestroy
方法中取消註冊該Activity。
下面是另一個Activity的定義,在這個Activity中,我們當按下按鈕的時候從EditText中取出內容並進行發佈,然後我們退出到之前的Activity,以測試是否正確監聽到發佈的內容。
@Route(path = BaseConstants.LIBRARY_EVENT_BUS_ACTIVITY2)
public class EventBusActivity2 extends CommonActivity<ActivityEventBus2Binding> {
@Override
protected void doCreateView(Bundle savedInstanceState) {
getBinding().btnPublish.setOnClickListener(v -> publishContent());
}
private void publishContent() {
String msg = getBinding().etMessage.getText().toString();
EventBus.getDefault().post(MessageWrap.getInstance(msg));
ToastUtils.makeToast("Published : " + msg);
}
}
根據測試的結果,我們的確成功地接收到了發送的信息。
2.4 黏性事件
所謂的黏性事件,就是指發送了該事件之後再訂閱者依然能夠接收到的事件。使用黏性事件的時候有兩個地方需要做些修改。一個是訂閱事件的地方,這裏我們在先打開的Activity中註冊監聽黏性事件:
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
public void onGetStickyEvent(MessageWrap message) {
String txt = "Sticky event: " + message.message;
getBinding().tvStickyMessage.setText(txt);
}
另一個是發佈事件的地方,這裏我們在新的開的Activity中發佈黏性事件。即調用EventBus的postSticky
方法來發布事件:
private void publishStickyontent() {
String msg = getBinding().etMessage.getText().toString();
EventBus.getDefault().postSticky(MessageWrap.getInstance(msg));
ToastUtils.makeToast("Published : " + msg);
}
按照上面的模式,我們先在第一個Activity中打開第二個Activity,然後在第二個Activity中發佈黏性事件,並回到第一個Activity註冊EventBus。根據測試結果,當按下注冊按鈕的時候,會立即觸發上面的訂閱方法從而獲取到了黏性事件。
2.5 優先級
在Subscribe
註解中總共有3個參數,上面我們用到了其中的兩個,這裏我們使用以下第三個參數,即priority
。它用來指定訂閱方法的優先級,是一個整數類型的值,默認是0,值越大表示優先級越大。在某個事件被髮布出來的時候,優先級較高的訂閱方法會首先接受到事件。
爲了對優先級進行測試,這裏我們需要對上面的代碼進行一些修改。這裏,我們使用一個布爾類型的變量來判斷是否應該取消事件的分發。我們在一個較高優先級的方法中通過該布爾值進行判斷,如果未true
就停止該事件的繼續分發,從而通過低優先級的訂閱方法無法獲取到事件來證明優先級較高的訂閱方法率先獲取到了事件。
這裏有幾個地方需要注意:
- 只有當兩個訂閱方法使用相同的
ThreadMode
參數的時候,它們的優先級纔會與priority
指定的值一致; - 只有當某個訂閱方法的
ThreadMode
參數爲POSTING
的時候,它才能停止該事件的繼續分發。
所以,根據以上的內容,我們需要對代碼做如下的調整:
// 用來判斷是否需要停止事件的繼續分發
private boolean stopDelivery = false;
@Override
protected void doCreateView(Bundle savedInstanceState) {
// ...
getBinding().btnStop.setOnClickListener(v -> stopDelivery = true);
}
@Subscribe(threadMode = ThreadMode.POSTING, priority = 0)
public void onGetMessage(MessageWrap message) {
getBinding().tvMessage.setText(message.message);
}
// 訂閱方法,需要與上面的方法的threadMode一致,並且優先級略高
@Subscribe(threadMode = ThreadMode.POSTING, sticky = true, priority = 1)
public void onGetStickyEvent(MessageWrap message) {
String txt = "Sticky event: " + message.message;
getBinding().tvStickyMessage.setText(txt);
if (stopDelivery) {
// 終止事件的繼續分發
EventBus.getDefault().cancelEventDelivery(message);
}
}
即我們在之前的代碼之上增加了一個按鈕,用來將stopDelivery
的值置爲true
。該字段隨後將會被用來判斷是否要終止事件的繼續分發,因爲我們需要在代碼中停止事件的繼續分發,所以,我們需要將上面的兩個訂閱方法的threadMode
的值都置爲ThreadMode.POSTING
。
按照,上面的測試方式,首先我們在當前的Activity註冊監聽,然後跳轉到另一個Activity,發佈事件並返回。第一次的時候,這裏的兩個訂閱方法都會被觸發。然後,我們按下停止分發的按鈕,並再次執行上面的邏輯,此時只有優先級較高的方法獲取到了事件並將該事件終止。
by .k
關注"編程v",每一天漲一點
STAY HUNGRY & STAY FOOLISH