本文的EventBus,是指greenrobot的 EventBus, 主要以EventBus3.0 講解;
什么是EventBus?
EventBus事件總線, 用于簡(jiǎn)化Android程序內(nèi),各個(gè)組件,線程之間的事件傳遞; 訂閱發(fā)布模式,將事件的接收者和發(fā)布者解耦,一旦publisher發(fā)出消息,subscribe自己按需改變; 我個(gè)人喜歡把它拿來和BroadCast比較;
在什么場(chǎng)景下使用
- 復(fù)雜邏輯下的對(duì)象傳遞
- 函數(shù)的調(diào)用者與被調(diào)用者需要低耦合,或者框架設(shè)計(jì)之初,無法預(yù)料到的調(diào)用
eg. 上面的使用場(chǎng)景,在我們代碼中時(shí)長(zhǎng)出現(xiàn)的場(chǎng)景就是,監(jiān)聽器的傳遞,回調(diào)函數(shù)和各種Listener;
比如,在一個(gè)activity中,又2個(gè)fragment,而每個(gè)fragment中又各嵌套一個(gè)子fragment, 其中一個(gè)子fragment要監(jiān)聽另一個(gè)子fragment中的按鈕變化; 一般做法是將listener作為函數(shù)參數(shù)傳遞, 或者設(shè)置為靜態(tài)變量;
第二個(gè), 就和BoardCast相似
怎么使用
- 在gradle中添加依賴
dependencies {
compile 'org.greenrobot:eventbus:3.0.0'
}
- 注冊(cè)和取消注冊(cè)
在要接收消息的類中registerunregister, 和廣播的注冊(cè)類似, 一般在activity的 onCreate 和 onDestory 方法中進(jìn)行
EventBus.getDefault().register( this );
EventBus.getDefault().unregister( this );
- 申明處理消息的函數(shù);
在接收消息的函數(shù)上,加上@Subscribe, EventBus是按函數(shù)參數(shù)的類型確認(rèn)消息的接收者的, 此函數(shù)只能有且僅有一個(gè)參數(shù);
@Subscribe(threadMode = ThreadMode.MAIN, priority = 1, sticky = false)
public void onEvent( TestEvent testEvent ){
Log.e( "zy", ">>>> receiverEvent");
}
只需要在函數(shù)上加上 @Subscribe 注解即可, 此注解還可以帶上額外的參數(shù)
threadMode, 用于指定此函數(shù)運(yùn)行的線程, 是一個(gè)Enum, 有4個(gè)常量,MAINBACKGROUNDASYNCPOSTING, 默認(rèn)為POSTING
ThreadMode.MAIN在主線程中運(yùn)行
ThreadMode.POSTING跟消息發(fā)送者在同一線程運(yùn)行
ThreadMode.BACKGROUND后臺(tái)線程, 如果發(fā)送消息的線程就是后臺(tái)線程,就直接執(zhí)行; 如果不是, 則會(huì)把消息放在隊(duì)列中,依次執(zhí)行
ThreadMode.ASYNC后臺(tái)線程, 消息會(huì)在單獨(dú)的線程中執(zhí)行,用了線程池,多個(gè)消息會(huì)同時(shí)執(zhí)行priority優(yōu)先級(jí), 值越小優(yōu)先級(jí)越低,當(dāng)有多個(gè)方法處理同一個(gè)消息時(shí),處理的順序,默認(rèn)為0sticky是否接收黏性消息, 和黏性廣播相同, 默認(rèn)為false
- 發(fā)送消息
所謂的消息,就只是一個(gè)java對(duì)象, 發(fā)送消息就是把這個(gè)對(duì)象,傳遞給處理消息的函數(shù); EventBus消息和EventBus的對(duì)象實(shí)例有關(guān), 用一個(gè)EventBus對(duì)象發(fā)送的消息,必須是用同一個(gè)EventBus對(duì)象注冊(cè)的才能收到消息.
// 發(fā)送黏性消息
EventBus.getDefault().postSticky( new TestEvent() );
// 發(fā)送普通的消息
EventBus.getDefault().post( new TestEvent() );
發(fā)送的消息有2種,
sticky黏性消息, 當(dāng)消息發(fā)送出去之后,如果沒有消息接收者處理這個(gè)消息,此消息會(huì)暫時(shí)存儲(chǔ)在eventBus實(shí)例中, 當(dāng)后面注冊(cè)接受者時(shí),如果合適的處理者, 將會(huì)把消息給處理者去處理;我個(gè)人喜歡用這個(gè)來做數(shù)據(jù)的預(yù)加載;
- 提升性能, 增加編譯時(shí)注解處理
由于android機(jī)器本身性能有限,一般不建議使用運(yùn)行時(shí)注解,EventBus的注解聲明為Runtime, 但它同時(shí)支持編譯時(shí)注解和運(yùn)行時(shí)注解, 當(dāng)沒配置編譯時(shí)注解處理器時(shí), 會(huì)自動(dòng)通過反射查找運(yùn)行時(shí)的注解;- 添加注解處理器依賴
buildscript {
...
dependencies {
classpath 'com.android.tools.build:gradle:2.1.0'
// 在最外層添加gradle的插件依賴
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4'
}
...
}
// 項(xiàng)目中 增加注解處理器插件
apply plugin: 'com.neenbedankt.android-apt'
dependencies {
compile 'org.greenrobot:eventbus:3.0.0'
// 添加注解處理器
apt 'org.greenrobot:eventbus-annotation-processor:3.0.1'
}
apt {
arguments {
// 注解處理器 最終生成的java文件位置
eventBusIndex "com.zy.test.MyEventBusIndex"
}
}
2. 初始化EventBus時(shí), 使用注解處理器生成的類文件
```java
mEventBus = EventBus.builder().addIndex( new MyEventBusIndex() ).build();
```
EventBus的消息和EventBus實(shí)例有關(guān)系, 自己配置的EventBus實(shí)例,一般需要用單例保存, 確保發(fā)送和接收消息的地方,使用的是同一個(gè)實(shí)例
關(guān)于其他的一些細(xì)節(jié)
- 消息處理者的繼承
EventBus的消息處理者,是可以繼承的, 父類中的消息處理器, 在子類中仍可使用; 這是一個(gè)比較好的功能, 比如通用的消息接收處理,我們?cè)贐aseActivity中聲明一次, 子類都可以使用了; 此功能可以關(guān)閉, 在構(gòu)建Eventbus實(shí)例時(shí), 調(diào)用 `EventBus.builder().eventInheritance( false )` ; 官方的說法是關(guān)閉后可以提供20%的性能;
- 黏性消息
非常實(shí)用的功能, 我一般用來做預(yù)加載數(shù)據(jù); 每種消息類型,最多存儲(chǔ)一個(gè)黏性消息, 和黏性廣播類似; 消息處理者. 聲明為sticky = true, 依然可以接收普通消息 - 進(jìn)程間的通訊
Eventbus的發(fā)送消息和消息處理是和Eventbus實(shí)例有關(guān)的, 是無法跨進(jìn)程傳遞消息的; 如果涉及到進(jìn)程間通訊, 還是要使用android系統(tǒng)的接口
對(duì)比
- Boardcast
優(yōu)點(diǎn): 可以指定運(yùn)行線程, 消息處理可繼承, 代碼簡(jiǎn)單, 消息處理可繼承, 低延遲, 對(duì)消息數(shù)據(jù)無要求(不需要實(shí)現(xiàn)Parcelable或者Serializable接口)
缺點(diǎn): 無法跨進(jìn)程 - LocalBroadcastManager
這個(gè)除了廣播的低延遲外, Boardcast的缺點(diǎn)都有, 并且它還不能不能跨進(jìn)程, 沒有黏性廣播 - RxBus
源碼初探
EventBus的源碼不多, 這里只講一下大概, 具體細(xì)節(jié)大家自己去讀源碼
源碼版本( 66ead83 )
- EventBus.java
此類對(duì)外提供所有的接口,register,unregister,post;
提供一個(gè)默認(rèn)的單例對(duì)象, 通過getDefault()獲取;
核心的變量如下
/** eventType和消息接收者存儲(chǔ)的map, key是event的class, value是接收者的信息, Subscription中包含的接收消息的對(duì)象, 處理消息的方法 */
private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
/** 消息處理者和其所包含的能處理的event的Map, key為消息處理者的實(shí)例, value為其所能處理的event的類型 */
private final Map<Object, List<Class<?>>> typesBySubscriber;
/** sticky event 的存儲(chǔ)的Map, key為event的class, value是具體事件的對(duì)象, 每種類型的sticky event 最多存儲(chǔ)一個(gè) */
private final Map<Class<?>, Object> stickyEvents;
SubscriberMethodFinder.java
用于尋找消息處理者的方法, 里面有一個(gè)靜態(tài)變量Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE用于保存找到的消息處理這, 加快下一次查找過程HandlerPoster.java
HandlerPoster本質(zhì)是一個(gè)Handler, 使用主線程的Looper, 可以看一下初始化語句mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10); 發(fā)送到主線程的消息, 實(shí)質(zhì)都是用此發(fā)送一個(gè)Message, 然后在Handler#handleMessage中, 調(diào)用 Method#invoke(Object, Event);AsyncPoster.java BackgroundPoster.java
名稱已經(jīng)很明顯, 處理后臺(tái)消息的2個(gè)類; 本質(zhì)都是Runnable, 都從消息隊(duì)列中獲取消息, 然后在線程池中執(zhí)行PendingPostQueue.java
消息存儲(chǔ)的地方, 一個(gè)簡(jiǎn)單的鏈表結(jié)構(gòu)
-
注冊(cè)流程
register后, 會(huì)通過SubscriberMethodFinder#findSubscriberMethods方法, 查找注冊(cè)的類, 如果添加注解處理器, 會(huì)通過反射去查找; 查找后,將各個(gè)對(duì)應(yīng)關(guān)系保存在Eventbus實(shí)例的成員變量; 并且檢測(cè)是否有黏性消息, 有黏性消息,則立馬執(zhí)行post流程, 有
ThreadLocal獲取所在線程信息, 然后在Eventbus#subscriptionsByEventType獲取所有的消息處理者, 然后判斷處理的線程, 分發(fā)到各個(gè)Poster去處理