EventBus事件總線(xiàn).以觀察者模式實(shí)現(xiàn),消息推送/訂閱.用于Android組件之間相互通信
Note:文章基于EventBus3.0
項(xiàng)目地址:https://github.com/greenrobot/EventBus
Docments:http://greenrobot.org/eventbus/documentation/how-to-get-started/
先來(lái)聊聊:什么是通信?
Wiki中國(guó): 通信是發(fā)送者通過(guò)某種媒體以某種格式來(lái)傳遞信息到收信者以達(dá)致某個(gè)目的。在古代馏锡,人們通過(guò)驛站憎蛤、飛鴿傳書(shū)果善、烽火報(bào)警踪旷、符號(hào)逞盆、語(yǔ)言刊懈、眼神绿饵、觸碰等方式進(jìn)行信息傳遞。到了今天垄懂,隨著科技水平的飛速發(fā)展骑晶,通信基本完全利用有線(xiàn)或無(wú)線(xiàn)電完成,相繼出現(xiàn)了有線(xiàn)電話(huà)草慧、固定電話(huà)透罢、無(wú)線(xiàn)電話(huà)、手機(jī)冠蒋、互聯(lián)網(wǎng)甚至視頻電話(huà)等各種通信方式羽圃。通信技術(shù)拉近了人與人之間的距離,提高了通信的效率抖剿,深刻的改變了人類(lèi)的通信朽寞。交流也是一種方法讓其他人理解你。
上面是Wiki的解釋,通信本身是個(gè)很抽象,寬泛的詞, 你跟朋友打個(gè)電話(huà)叫通信, 發(fā)個(gè)Email也叫通信, 兩個(gè)App之間傳數(shù)據(jù)也叫通信,也叫為進(jìn)程間通信, Activity和Activity之間傳遞數(shù)據(jù)叫組件間通信, 說(shuō)到這里,來(lái)下個(gè)通俗點(diǎn)的定義:一個(gè)東西給另一個(gè)東西傳遞東西(數(shù)據(jù)),就叫通信.
EventBus有什么用?解決了什么問(wèn)題?
方便Android組件間相互通信.在Activity,Service,Fragment之間傳遞數(shù)據(jù),例如,你在個(gè)人主頁(yè)編輯頭像的界面修改了頭像,這時(shí)候其它頁(yè)面的頭像也需要去修改,這時(shí)候可以試試EventBus.
基于觀察者模式:這個(gè)好理解, 有消息的發(fā)送者,也有消息的接收者,當(dāng)然,也可以是一發(fā)送者對(duì)應(yīng)多個(gè)接收者, EventBus使用起來(lái)非常簡(jiǎn)單,可以用來(lái)提供廣播之類(lèi)
怎么用?
- 添加依賴(lài)
- 使用三歩曲:
- 定義事件類(lèi)
- 注冊(cè)事件
- 發(fā)送事件
添加依賴(lài)
compile 'org.greenrobot:eventbus:3.0.0'
使用三步曲:
1,定義事件類(lèi)
事件類(lèi)是通信過(guò)程中的數(shù)據(jù)的載體,用來(lái)傳遞數(shù)據(jù),這里寫(xiě)一個(gè)最簡(jiǎn)單的事件類(lèi),后面說(shuō)如何傳遞數(shù)據(jù)
public static class MessageEvent {
/* Additional fields if needed */
}
2.注冊(cè)事件
上面提到數(shù)據(jù)的發(fā)送者和接收者, 注冊(cè)事件是針對(duì)接收者的, 哪個(gè)組件需要接收消息,就在哪個(gè)組件中去注冊(cè)
- 注冊(cè): EventBus.getDefault().register(this);
- 添加函數(shù)注解@Subscribe:當(dāng)數(shù)據(jù)的發(fā)送者發(fā)出消息后,添加這個(gè)注解的函數(shù)會(huì)被調(diào)用
- 添加@Subscribe注解的函數(shù)參數(shù),寫(xiě)上接收事件類(lèi)型,表示這個(gè)組件會(huì)接收這類(lèi)事件的消息
- 注銷(xiāo):EventBus:EventBus.getDefault().unregister(this);
Note:一個(gè)事件是可以有多個(gè)接收者(訂閱者)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//注冊(cè)
EventBus.getDefault().register(this);
}
//加上Subscribe注解,這個(gè)方法會(huì)在事件發(fā)出后收到回調(diào),方法名是自定義的
@Subscribe
public void onMessageEvent(MessageEvent event) {
Log.i(TAG, "onMessageEvent: ");
}
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
3.發(fā)送事件
最后,消息的發(fā)送者發(fā)送事件, 這時(shí)候上面添加@Subscribe注解的函數(shù)會(huì)被調(diào)用
EventBus.getDefault().post(new MessageEvent());
這時(shí)候在回來(lái)看這張大圖,So easy,點(diǎn)擊查看大圖
這里順便吐槽下官方示例代碼:
如果你打算把注冊(cè)寫(xiě)在onStart中,請(qǐng)?jiān)谇懊婕由吓袛?/p>
@Override
public void onStart() {
super.onStart();
if (!EventBus.getDefault().isRegistered(this)) {
EventBus.getDefault().register(this);
}
}
上面是EventBus的基本使用流程,其實(shí)這才開(kāi)始:
- 在事件中傳遞數(shù)據(jù)
- 指定事件接收線(xiàn)程
- 發(fā)送黏性事件Sticky Events
- 接收事件的優(yōu)先級(jí)別
- 中止事件傳遞
- 訂閱者索引
傳遞數(shù)據(jù)
不過(guò),上面只是發(fā)了一個(gè)空的消息,沒(méi)有傳遞任何的數(shù)據(jù),比如:我修改了用戶(hù)的頭像,上傳到服務(wù)器,服務(wù)器返回一個(gè)Url,這時(shí)候我需要修改其它界面的頭像,這時(shí)候我要把這個(gè)url數(shù)據(jù)傳到其它界面:怎么做呢?
1.只需要去修改定義事件類(lèi)中的構(gòu)造函數(shù),增加一個(gè)參數(shù),在加上set和get方法,如下:
public class UpdateHeadPicEvent {
private String mUrl;
public UpdateHeadPicEvent(String url) {
this.mUrl = url;
}
public String getUrl() {
return mUrl;
}
}
2.在發(fā)送事件的地方,使用這個(gè)帶參的構(gòu)造去創(chuàng)建對(duì)象:
EventBus.getDefault().post(new UpdateHeadImgEvent(url));
3.在接收消息的回調(diào)中用get方法去獲取
@Subscribe
public void onUpdateHeadPicEvent(UpdateHeadPicEvent event) {
String url = event.getUrl();
Log.i(TAG, "onUpdateHeadPicEvent: " + url);
}
Note:這里只是用了一個(gè)String的Url做參考,可以傳遞的數(shù)據(jù)還有很多,List集合,Bean對(duì)象...
指定事件接收的線(xiàn)程
前面說(shuō)了加上@Subscribe注解,這個(gè)函數(shù)在消息推送之后會(huì)被回調(diào),其實(shí)這個(gè)注解還有幾個(gè)屬性,其中threadMode能為該函數(shù)指定線(xiàn)程,如果不寫(xiě),也會(huì)有個(gè)默認(rèn)值:ThreadMode.POSTING,意思是和發(fā)送事件所在線(xiàn)程一樣
@Subscribe(threadMode = ThreadMode.POSTING)
public void onUpdateHeadPicEvent(UpdateHeadPicEvent event) {
String url = event.getUrl();
Log.i(TAG, "onUpdateHeadPicEvent: " + url);
}
ThreadMode列表
- ThreadMode.POSTING:和發(fā)送事件在同一個(gè)線(xiàn)程
- ThreadMode.MAIN:主線(xiàn)程
- ThreadMode.BACKGROUND:子線(xiàn)程
- ThreadMode.ASYNC:異步線(xiàn)程
發(fā)送黏性事件Sticky Events
上面示例代碼所說(shuō)的情況是:當(dāng)發(fā)送消息推送者推送消息的時(shí)候,訂閱者會(huì)立馬收到消息,它會(huì)把消息推送給它所有的訂閱者.注意后面這句話(huà):如果你希望在消息推送完成之后,讓新注冊(cè)的訂閱者也能收到這條消息,這時(shí)候你可以試試Sticky Events,這個(gè)事件就像一個(gè)常駐廣播,只要是有新的訂閱者訂閱了這個(gè)事件,就會(huì)收到消息.當(dāng)然,有兩點(diǎn)要求:
- 首先,發(fā)送的是黏性事件,代碼將post改為postSticky
// EventBus.getDefault().post(new MessageEvent());
EventBus.getDefault().postSticky(new MessageEvent());
- 然后,訂閱者要聲明自己能夠接收到黏性事件的消息:代碼中@Subscribe注解中的sticky值為true,滿(mǎn)足了這兩點(diǎn),就能愉快的玩耍了.
@Override
protected void onStart() {
super.onStart();
if (!EventBus.getDefault().isRegistered(this)) {
EventBus.getDefault().register(this);
}
}
@Subscribe(sticky = true)
public void onMessageEvent(MessageEvent event) {
Log.i(TAG, "onMessageEvent: 我是sticky event 收到消息");
}
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
測(cè)試Log日志
接收事件的優(yōu)先級(jí)別
EventBus可以定義接收事件方的優(yōu)先級(jí)別,在@Subscribe注解中有一個(gè)priority的參數(shù),默認(rèn)值是0,可以自行配置1.2.3.4...數(shù)值越大優(yōu)先級(jí)越低,會(huì)越晚收到消息
@Subscribe(priority = 0)
public void onUpdateHeadPicEvent(UpdateHeadPicEvent event) {
String url = event.getUrl();
Log.i(TAG, "onUpdateHeadPicEvent: " + url);
}
中止事件傳遞
類(lèi)似于有序廣播,優(yōu)先級(jí)高的訂閱者,可以終止事件向下傳遞,EventBus也提供了此功能
@Subscribe(priority = 0)
public void onUpdateHeadPicEvent(UpdateHeadPicEvent event) {
EventBus.getDefault().cancelEventDelivery(event) ;
}
訂閱者索引
這個(gè)新特性是在EventBus 3.0推出,簡(jiǎn)單的說(shuō):利用annotationProcessor去生成一個(gè)關(guān)于訂閱者的索引類(lèi),保存訂閱者的相關(guān)信息.
有什么用?提高效率,注冊(cè)從運(yùn)行時(shí)的反射,轉(zhuǎn)移到了編譯時(shí)
怎么用?兩種方式:
- 在gradle版本2.2.0以前使用:Android-apt
- 在gradle版本2.2.0以上使用:annotationProcessor
這里說(shuō)annotationProcessor的方式,現(xiàn)在新建項(xiàng)目一般都在2.2.0以上:
添加好Gradle設(shè)置之后,重新build項(xiàng)目,就會(huì)為你生成這樣的類(lèi):MyEventBusIndex
![](/assets/EventBus Annotaion Processor.png)
用法:將它配置應(yīng)用于默認(rèn)的EventBus,調(diào)用下面代碼:可放在Application的onCreate中調(diào)用
EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();
未添加前和添加后我分別做了三次測(cè)試,注冊(cè)完成的時(shí)間對(duì)比,速度快了不止一倍,這還是只有兩個(gè)訂閱者的時(shí)候,如果訂閱訂閱者更多呢,
//未添加前
I/Subscriber1Activity: onMessageEvent: register start 1491892487664
I/Subscriber1Activity: onMessageEvent: register end 1491892487666
I/Subscriber1Activity: onMessageEvent: register start 1491892568177
I/Subscriber1Activity: onMessageEvent: register end 1491892568180
I/Subscriber1Activity: onMessageEvent: register start 1491892715342
I/Subscriber1Activity: onMessageEvent: register end 1491892715344
//添加后
I/Subscriber1Activity: onMessageEvent: register start 1491892648185
I/Subscriber1Activity: onMessageEvent: register end 1491892648186
I/Subscriber1Activity: onMessageEvent: register start 1491892814517
I/Subscriber1Activity: onMessageEvent: register end 1491892814518
I/Subscriber1Activity: onMessageEvent: register start 1491892868879
I/Subscriber1Activity: onMessageEvent: register end 1491892868880
在Lib的model中使用:
build.gradle也要添加上圖中同樣的參數(shù),在上圖中,這個(gè)參數(shù)是自定義的,不同的model可以為他生成不同的索引類(lèi):
arguments = [eventBusIndex: 'com.example.myapp.MyEventBusIndex']
更改默認(rèn)配置也需要多添加一行代碼:
EventBus eventBus = EventBus.builder()
.addIndex(new MyEventBusAppIndex())
.addIndex(new MyEventBusLibIndex())
.installDefaultEventBus();