1、EventBus 簡介
EventBus是一種用于Android的事件發(fā)布-訂閱總線,由GreenRobot開發(fā),Gihub地址是:EventBus。它簡化了應用程序內各個組件之間進行通信的復雜度,尤其是碎片之間進行通信的問題,可以避免由于使用廣播通信而帶來的諸多不便。
1.1 三個角色
- Event:事件,它可以是任意類型,EventBus會根據(jù)事件類型進行全局的通知。
-
Subscriber:事件訂閱者,在EventBus 3.0之前我們必須定義以onEvent開頭的那幾個方法,分別是
onEvent、onEventMainThread、onEventBackgroundThread和onEventAsync,而在3.0之后事件處理的方法名可以隨意取,不過需要加上注解@subscribe,并且指定線程模型,默認是POSTING。 -
Publisher:事件的發(fā)布者,可以在任意線程里發(fā)布事件。一般情況下,使用
EventBus.getDefault()就可以得到一個EventBus對象,然后再調用post(Object)方法即可。
1.2 四種線程模型
EventBus3.0有四種線程模型,分別是:
- POSTING:默認,表示事件處理函數(shù)的線程跟發(fā)布事件的線程在同一個線程。
- MAIN:表示事件處理函數(shù)的線程在主線程(UI)線程,因此在這里不能進行耗時操作。
- BACKGROUND:表示事件處理函數(shù)的線程在后臺線程,因此不能進行UI操作。如果發(fā)布事件的線程是主線程(UI線程),那么事件處理函數(shù)將會開啟一個后臺線程,如果果發(fā)布事件的線程是在后臺線程,那么事件處理函數(shù)就使用該線程。
- ASYNC:表示無論事件發(fā)布的線程是哪一個,事件處理函數(shù)始終會新建一個子線程運行,同樣不能進行UI操作。
2、EventBus 使用
2.1 引入依賴
在使用之前先要引入如下依賴:
implementation 'org.greenrobot:eventbus:3.1.1'
2.2 定義事件
然后,我們定義一個事件的封裝對象。在程序內部就使用該對象作為通信的信息:
public class MessageWrap {
public final String message;
public static MessageWrap getInstance(String message) {
return new MessageWrap(message);
}
private MessageWrap(String message) {
this.message = message;
}
}
2.3 發(fā)布事件
然后,我們定義一個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注冊監(jiān)聽,然后按下另一個按鈕的時候跳轉到拎一個Activity,并在另一個Activity發(fā)布我們輸入的事件。在上面的Activity中,我們會添加一個監(jiān)聽的方法,即onGetMessage,這里我們需要為其加入注解Subscribe并指定線程模型為主線程MAIN。最后,就是在Activity的onDestroy方法中取消注冊該Activity。
下面是另一個Activity的定義,在這個Activity中,我們當按下按鈕的時候從EditText中取出內容并進行發(fā)布,然后我們退出到之前的Activity,以測試是否正確監(jiān)聽到發(fā)布的內容。
@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);
}
}
根據(jù)測試的結果,我們的確成功地接收到了發(fā)送的信息。
2.4 黏性事件
所謂的黏性事件,就是指發(fā)送了該事件之后再訂閱者依然能夠接收到的事件。使用黏性事件的時候有兩個地方需要做些修改。一個是訂閱事件的地方,這里我們在先打開的Activity中注冊監(jiān)聽黏性事件:
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
public void onGetStickyEvent(MessageWrap message) {
String txt = "Sticky event: " + message.message;
getBinding().tvStickyMessage.setText(txt);
}
另一個是發(fā)布事件的地方,這里我們在新的開的Activity中發(fā)布黏性事件。即調用EventBus的postSticky方法來發(fā)布事件:
private void publishStickyontent() {
String msg = getBinding().etMessage.getText().toString();
EventBus.getDefault().postSticky(MessageWrap.getInstance(msg));
ToastUtils.makeToast("Published : " + msg);
}
按照上面的模式,我們先在第一個Activity中打開第二個Activity,然后在第二個Activity中發(fā)布黏性事件,并回到第一個Activity注冊EventBus。根據(jù)測試結果,當按下注冊按鈕的時候,會立即觸發(fā)上面的訂閱方法從而獲取到了黏性事件。
2.5 優(yōu)先級
在Subscribe注解中總共有3個參數(shù),上面我們用到了其中的兩個,這里我們使用以下第三個參數(shù),即priority。它用來指定訂閱方法的優(yōu)先級,是一個整數(shù)類型的值,默認是0,值越大表示優(yōu)先級越大。在某個事件被發(fā)布出來的時候,優(yōu)先級較高的訂閱方法會首先接受到事件。
為了對優(yōu)先級進行測試,這里我們需要對上面的代碼進行一些修改。這里,我們使用一個布爾類型的變量來判斷是否應該取消事件的分發(fā)。我們在一個較高優(yōu)先級的方法中通過該布爾值進行判斷,如果未true就停止該事件的繼續(xù)分發(fā),從而通過低優(yōu)先級的訂閱方法無法獲取到事件來證明優(yōu)先級較高的訂閱方法率先獲取到了事件。
這里有幾個地方需要注意:
- 只有當兩個訂閱方法使用相同的
ThreadMode參數(shù)的時候,它們的優(yōu)先級才會與priority指定的值一致; - 只有當某個訂閱方法的
ThreadMode參數(shù)為POSTING的時候,它才能停止該事件的繼續(xù)分發(fā)。
所以,根據(jù)以上的內容,我們需要對代碼做如下的調整:
// 用來判斷是否需要停止事件的繼續(xù)分發(fā)
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一致,并且優(yōu)先級略高
@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) {
// 終止事件的繼續(xù)分發(fā)
EventBus.getDefault().cancelEventDelivery(message);
}
}
即我們在之前的代碼之上增加了一個按鈕,用來將stopDelivery的值置為true。該字段隨后將會被用來判斷是否要終止事件的繼續(xù)分發(fā),因為我們需要在代碼中停止事件的繼續(xù)分發(fā),所以,我們需要將上面的兩個訂閱方法的threadMode的值都置為ThreadMode.POSTING。
按照,上面的測試方式,首先我們在當前的Activity注冊監(jiān)聽,然后跳轉到另一個Activity,發(fā)布事件并返回。第一次的時候,這里的兩個訂閱方法都會被觸發(fā)。然后,我們按下停止分發(fā)的按鈕,并再次執(zhí)行上面的邏輯,此時只有優(yōu)先級較高的方法獲取到了事件并將該事件終止。
總結
上面的內容是EventBus的基本使用方法,相關的源碼參考:Github