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