1、EventBus 簡介
EventBus是一種用于Android的事件發(fā)布-訂閱總線,由GreenRobot開發(fā),Gihub地址是:EventBus。它簡化了應(yīng)用程序內(nèi)各個組件之間進(jìn)行通信的復(fù)雜度铃绒,尤其是碎片之間進(jìn)行通信的問題,可以避免由于使用廣播通信而帶來的諸多不便螺捐。
1.1 三個角色
- Event:事件颠悬,它可以是任意類型,EventBus會根據(jù)事件類型進(jìn)行全局的通知定血。
-
Subscriber:事件訂閱者赔癌,在EventBus 3.0之前我們必須定義以onEvent開頭的那幾個方法,分別是
onEvent
澜沟、onEventMainThread
届榄、onEventBackgroundThread
和onEventAsync
,而在3.0之后事件處理的方法名可以隨意取倔喂,不過需要加上注解@subscribe
铝条,并且指定線程模型,默認(rèn)是POSTING
席噩。 -
Publisher:事件的發(fā)布者班缰,可以在任意線程里發(fā)布事件。一般情況下悼枢,使用
EventBus.getDefault()
就可以得到一個EventBus對象埠忘,然后再調(diào)用post(Object)
方法即可。
1.2 四種線程模型
EventBus3.0有四種線程模型馒索,分別是:
- POSTING:默認(rèn)莹妒,表示事件處理函數(shù)的線程跟發(fā)布事件的線程在同一個線程。
- MAIN:表示事件處理函數(shù)的線程在主線程(UI)線程绰上,因此在這里不能進(jìn)行耗時操作旨怠。
- BACKGROUND:表示事件處理函數(shù)的線程在后臺線程,因此不能進(jìn)行UI操作蜈块。如果發(fā)布事件的線程是主線程(UI線程)鉴腻,那么事件處理函數(shù)將會開啟一個后臺線程,如果果發(fā)布事件的線程是在后臺線程百揭,那么事件處理函數(shù)就使用該線程爽哎。
- ASYNC:表示無論事件發(fā)布的線程是哪一個,事件處理函數(shù)始終會新建一個子線程運行器一,同樣不能進(jìn)行UI操作课锌。
2、EventBus 使用
2.1 引入依賴
在使用之前先要引入如下依賴:
implementation 'org.greenrobot:eventbus:3.1.1'
2.2 定義事件
然后祈秕,我們定義一個事件的封裝對象渺贤。在程序內(nèi)部就使用該對象作為通信的信息:
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);
}
}
這里我們當(dāng)按下按鈕的時候向EventBus注冊監(jiān)聽踢步,然后按下另一個按鈕的時候跳轉(zhuǎn)到拎一個Activity癣亚,并在另一個Activity發(fā)布我們輸入的事件。在上面的Activity中获印,我們會添加一個監(jiān)聽的方法述雾,即onGetMessage
,這里我們需要為其加入注解Subscribe
并指定線程模型為主線程MAIN
兼丰。最后玻孟,就是在Activity的onDestroy
方法中取消注冊該Activity。
下面是另一個Activity的定義鳍征,在這個Activity中黍翎,我們當(dāng)按下按鈕的時候從EditText中取出內(nèi)容并進(jìn)行發(fā)布,然后我們退出到之前的Activity艳丛,以測試是否正確監(jiān)聽到發(fā)布的內(nèi)容匣掸。
@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ù)測試的結(jié)果趟紊,我們的確成功地接收到了發(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ā)布黏性事件。即調(diào)用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ù)測試結(jié)果帖烘,當(dāng)按下注冊按鈕的時候,會立即觸發(fā)上面的訂閱方法從而獲取到了黏性事件爽航。
2.5 優(yōu)先級
在Subscribe
注解中總共有3個參數(shù)蚓让,上面我們用到了其中的兩個,這里我們使用以下第三個參數(shù)讥珍,即priority
历极。它用來指定訂閱方法的優(yōu)先級,是一個整數(shù)類型的值衷佃,默認(rèn)是0趟卸,值越大表示優(yōu)先級越大。在某個事件被發(fā)布出來的時候氏义,優(yōu)先級較高的訂閱方法會首先接受到事件锄列。
為了對優(yōu)先級進(jìn)行測試,這里我們需要對上面的代碼進(jìn)行一些修改惯悠。這里邻邮,我們使用一個布爾類型的變量來判斷是否應(yīng)該取消事件的分發(fā)。我們在一個較高優(yōu)先級的方法中通過該布爾值進(jìn)行判斷克婶,如果未true
就停止該事件的繼續(xù)分發(fā)筒严,從而通過低優(yōu)先級的訂閱方法無法獲取到事件來證明優(yōu)先級較高的訂閱方法率先獲取到了事件。
這里有幾個地方需要注意:
- 只有當(dāng)兩個訂閱方法使用相同的
ThreadMode
參數(shù)的時候情萤,它們的優(yōu)先級才會與priority
指定的值一致鸭蛙; - 只有當(dāng)某個訂閱方法的
ThreadMode
參數(shù)為POSTING
的時候,它才能停止該事件的繼續(xù)分發(fā)筋岛。
所以娶视,根據(jù)以上的內(nèi)容,我們需要對代碼做如下的調(diào)整:
// 用來判斷是否需要停止事件的繼續(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
寒锚。
按照,上面的測試方式违孝,首先我們在當(dāng)前的Activity注冊監(jiān)聽刹前,然后跳轉(zhuǎn)到另一個Activity,發(fā)布事件并返回雌桑。第一次的時候喇喉,這里的兩個訂閱方法都會被觸發(fā)。然后校坑,我們按下停止分發(fā)的按鈕拣技,并再次執(zhí)行上面的邏輯,此時只有優(yōu)先級較高的方法獲取到了事件并將該事件終止耍目。
總結(jié)
上面的內(nèi)容是EventBus的基本使用方法膏斤,相關(guān)的源碼參考:Github