EventBus 3 簡(jiǎn)介
EventBus是一種為了優(yōu)化Android組件之間事件傳遞的解耦工具尖淘,通過(guò)發(fā)布/訂閱事件總線來(lái)實(shí)現(xiàn)事件在不同組件之間的事件傳遞。
在EventBus 3之前著觉,greenrobot團(tuán)隊(duì)因?yàn)榭紤]性能原因所以比較抵觸使用注解框架村生。目前的EventBus3開始使用注解來(lái)申明訂閱事件的處理方法。雖然目前Android 6 和ART都有了饼丘,但是對(duì)于Java反射造成的性能影響還是沒能很好的解決趁桃。
在EventBus3中,greenrobot團(tuán)隊(duì)通過(guò)利用在編譯時(shí)檢索所有注解代碼肄鸽,然后生成一個(gè)包含所有在運(yùn)行時(shí)要花很大代價(jià)才能獲取的數(shù)據(jù)的類卫病,通過(guò)這種新的注解處理方式來(lái)提升性能,讓EventBus3比其他的eventbus會(huì)更加快典徘。在后文中會(huì)貼出和otto的性能比較蟀苛。
EventBus 3 和 EventBus 2.x 的區(qū)別
回調(diào)方法改動(dòng)
由于API的改動(dòng),會(huì)導(dǎo)致EventBus3和之前使用老版本的EventBus不兼容逮诲,因?yàn)橹鞍姹荆‥ventBus 2.x)帜平,在注冊(cè)完事件之后,會(huì)要求寫相應(yīng) onEvent()方法梅鹦,包括onEvent()
裆甩、onEventAsync()
、onEventBackground()
齐唆、onEventMainThread()
分別對(duì)應(yīng) @Subscrible
淑掌、@Subscrible(threadMode = ThreadMode.ASYNC)
、@Subscribe(threadMode = ThreadMode.BACKGROUND)
蝶念、@Subscribe(threadMode = ThreadMode.MAIN)
抛腕。EventBus 3中在未聲明threadMob時(shí),默認(rèn)的線程模式為ThreadMode.POSTING
媒殉。
異常容錯(cuò)處理
在EventBus3中担敌,如果在@Subscrible標(biāo)注的方法中,如果程序出錯(cuò)廷蓉,不會(huì)立即使程序crash全封,而是由EventBus攔截異常,并打印錯(cuò)誤日志桃犬。
用戶可以通過(guò)EventBusBuilder來(lái)配置獲取EventBus實(shí)例后的對(duì)象刹悴,來(lái)決定在處理event時(shí)是否需要拋出異常信息:
eventBus = EventBus.builder().sendNoSubscriberEvent(false)
.sendSubscriberExceptionEvent(false)
.throwSubscriberException(BuildConfig.DEBUG) //只有在debug模式下,會(huì)拋出錯(cuò)誤異常
.build();
以上代碼使用Builder設(shè)計(jì)模式攒暇,來(lái)構(gòu)建返回一個(gè)eventBus實(shí)例土匀。在調(diào)試階段,可以在程序出現(xiàn)異常時(shí)直接Crash發(fā)現(xiàn)錯(cuò)誤形用。
EventBus 3的使用
引入EventBus庫(kù)文件
在這里以gradle
引用EventBus3庫(kù)為例進(jìn)行說(shuō)明
compile 'org.greenrobot:eventbus:3.0.0'
定義一個(gè)事件集合
在使用EventBus時(shí)就轧,由于我個(gè)人比較喜歡將事件進(jìn)行統(tǒng)一管理和注釋(代碼如下),在其中定義了一個(gè)常用事件CommonEvent 實(shí)現(xiàn)了BaseEvents接口的兩個(gè)方法田度。
public interface BaseEvents{
setObject(Object obj);
Object getObject();
//事件定義
enum CommonEvent implements BaseEvent {
LOGIN, //登錄
LOGOUT; //登出
private Object obj;
@Override
public void setObject(Object obj) {
this.obj = obj;
}
@Override
public Object getObject() {
return obj;
}
}
// ... 其他事件定義
}
這樣定義基本事件妒御,主要是為了便于事件管理添加和維護(hù)。避免新建很多不必要的類和重載過(guò)多的事件處理方法镇饺。
注冊(cè)事件及聲明事件處理方法
在Activity/Frament中乎莉,可以定義一個(gè)基類,在基類的onStart()和onStop()方法中對(duì)EventBus進(jìn)行注冊(cè)和注銷(官方推薦)奸笤。然后其他的子類集成基類惋啃,復(fù)寫和實(shí)現(xiàn)對(duì)應(yīng)的注冊(cè)方法即可。
@Override
public void onStart() {
super.onStart();
registEventBus();
}
@Override
public void onStop() {
unRegistEventBus();
super.onStop();
}
protected void registEventBus(){
//子類如果需要注冊(cè)eventbus揭保,則重寫此方法
//EventBus.getDefault().register(this);
}
protected void unRegistEventBus(){
//子類如果需要注銷eventbus肥橙,則重寫此方法
//EventBus.getDefault().unregister(this);
}
注冊(cè)方法后,需要使用注解@Subscribe聲明處理事件的方法秸侣,以下代碼摘自官方介紹
// This method will be called when a MessageEvent is posted
@Subscribe
public void onMessageEvent(MessageEvent event){
Toast.makeText(getActivity(), event.message, Toast.LENGTH_SHORT).show();
}
// This method will be called when a SomeOtherEvent is posted
@Subscribe
public void handleSomethingElse(SomeOtherEvent event){
doSomethingWith(event);
}
發(fā)送事件
在發(fā)送事件時(shí)存筏,可以利用我們定義的一個(gè)BaseEvent進(jìn)行值的傳遞,因?yàn)槎x的是一個(gè)Object對(duì)象味榛,只需要保證類型轉(zhuǎn)換正確的前提下就可以隨意傳值了椭坚。示例如下:
BaseEvents. CommonEvent event = BaseEvents.CommonEvent.LOGIN;
event.setObject("Send Event"); //傳入一個(gè)String對(duì)象
eventBus.post(event); //發(fā)布事件
如此就簡(jiǎn)單的實(shí)現(xiàn)了一個(gè)EventBus事件的發(fā)布。
StickyEvent
有時(shí)候搏色,我們會(huì)面對(duì)這樣一個(gè)問題善茎,那就是我們要把一個(gè)Event發(fā)送到一個(gè)還沒有初始化的Activity/Fragment,即尚未訂閱事件频轿。那么如果只是簡(jiǎn)單的post一個(gè)事件垂涯,那么是無(wú)法收到的烁焙,這時(shí)候,需要使用 StickyEvent耕赘,實(shí)例說(shuō)明:
BaseEvents. CommonEvent event = BaseEvents.CommonEvent.LOGIN;
event.setObject("Send StickyEvent");
EventBus.getDefault().postSticky(event);
在啟動(dòng)的Activity骄蝇,暫且定義為StickyActivity 繼承 BaseActivity 實(shí)現(xiàn) registEventBus()
和unRegistEventBus()
,代碼片段如下:
@Override
public void onStart() {
super.onStart();
registEventBus();
}
@Override
public void onStop() {
unRegistEventBus();
super.onStop();
}
@Subscribe(sticky = true)
public void onEvent(BaseEvents. CommonEvent event) {
// UI updates must run on MainThread
if(event==BaseEvent.CommonEvent.LOGIN){
String conent = (String)event.getObject();
Log.d(TAG,"Content is : "+content);
}
}
protected void registEventBus(){
EventBus.getDefault().register(this);
}
protected void unRegistEventBus(){
//EventBus.getDefault().unregister(this);
}
以上就是發(fā)送一個(gè)stickyEvent的全過(guò)程操骡,下面講一下event的優(yōu)先級(jí)和攔截
事件處理優(yōu)先級(jí)及事件攔截
有時(shí)候在開發(fā)中九火,需要定義處理相同事件的優(yōu)先級(jí),我們先來(lái)看一下EventBus3對(duì)于Subscrible的注解定義源碼:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
ThreadMode threadMode() default ThreadMode.POSTING;
/**
* If true, delivers the most recent sticky event (posted with
* {@link EventBus#postSticky(Object)}) to this subscriber (if event available).
*/
boolean sticky() default false;
/** Subscriber priority to influence the order of event delivery.
* Within the same delivery thread ({@link ThreadMode}), higher priority subscribers will receive events before
* others with a lower priority. The default priority is 0. Note: the priority does *NOT* affect the order of
* delivery among subscribers with different {@link ThreadMode}s!
*/
int priority() default 0;
}
可以看到 priority 的值默認(rèn)為0册招,優(yōu)先級(jí)隨定義的priority的值越大優(yōu)先級(jí)越高岔激。注意,在注解@Retention(RetentionPolicy.RUNTIME)
這一項(xiàng)是將注解寫入編譯的class文件中是掰,這個(gè)也是前面介紹到EventBus3解決注解反射導(dǎo)致性能問題的一個(gè)關(guān)鍵點(diǎn)虑鼎。
priority 實(shí)例簡(jiǎn)介:
@Subscribe(priority = 10)
public void handleEvent(BaseEvents.CommonEvent event){
//....
}
有了事件處理優(yōu)先級(jí)的定義,那么我們就可以在高優(yōu)先級(jí)的事件處理中冀惭,將事件傳遞攔截下來(lái)震叙,經(jīng)過(guò)實(shí)際測(cè)試,只能在 threadMode = ThreadMode.POSTING
的注釋方法中才能攔截事件散休。官方如下介紹的:
事件通常是被高優(yōu)先級(jí)的訂閱者取消媒楼,目前取消事件的處理方法僅限在ThreadMode 為 POSTING才能取消攔截事件。
注:POSTING的事件戚丸,表示當(dāng)前的post event和subscribe event兩者在同一個(gè)線程之中划址。
注意事項(xiàng)
- 工作線程和UI線程之間的事件傳遞
在使用EventBus3時(shí),由于event 在任何地方都可以發(fā)布一個(gè)事件限府,那么在不同線程之間傳遞事件夺颤,比如在工作線程傳遞一個(gè)事件更新UI線程中的一個(gè)控件,則需要注意threadMode的切換胁勺。
- 取消線程僅限于 threadMode處于POSTING模式才可以
否則容易導(dǎo)致錯(cuò)誤:
org.greenrobot.eventbus.EventBusException: This method may only be called from inside event handling methods on the posting thread
混淆
關(guān)于正式打包簽名世澜,混淆就不多說(shuō)了,把下面的混淆代碼署穗,粘貼到你的proguard-rules.pro
下就ok了
-keepattributes *Annotation*
-keepclassmembers class ** {
@org.greenrobot.eventbus.Subscribe <methods>;
}
-keep enum org.greenrobot.eventbus.ThreadMode {
*;
}
# Only required if you use AsyncExecutor
-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {
<init>(java.lang.Throwable);
}
EventBus與otto的對(duì)比
這里的對(duì)比圖來(lái)自官方Github網(wǎng)站寥裂,對(duì)于線程之間傳遞Event的比較做了一個(gè)簡(jiǎn)化,簡(jiǎn)化為不同線程之間的事件傳遞案疲。
功能對(duì)比
EventBus | otto | |
---|---|---|
聲明事件處理方法 | 注解(從3.0開始使用預(yù)編譯解決性能問題) | 注解 |
事件可擴(kuò)展 | 可以 | 可以 |
訂閱者擴(kuò)展 | 可以 | 不行 |
緩存大量最近事件 | 可以(用sticky events) | 不行 |
事件生產(chǎn)者(比如編碼緩存的事件) | 不行 | 可以 |
事件相同線程中傳遞 | 可以 | 可以 |
事件不同線程之間傳遞 | 可以 | 不可以 |
性能對(duì)比
性能對(duì)比封恰,由于個(gè)人沒有去寫具體的Demo測(cè)試,不過(guò)官方源碼有關(guān)于性能的比較測(cè)試源碼褐啡,一下截圖來(lái)自官方Github的比較結(jié)果:
總體而言诺舔,EventBus在各個(gè)方面完爆otto,當(dāng)然在實(shí)際項(xiàng)目中,還是應(yīng)該考慮性能和穩(wěn)定做出取舍低飒。
END
由于本人水平有限许昨,文中難免有所疏漏,有問題還請(qǐng)斧正逸嘀,開卷有益多多交流车要。如有疑問可以直接留言討論~ O(∩_∩)O哈哈~
參考資料:
EventBus官網(wǎng)文檔