安卓基礎(chǔ)開發(fā)庫,讓開發(fā)簡單點(diǎn)。
DevRing & Demo地址:https://github.com/LJYcoder/DevRing
學(xué)習(xí)/參考地址:
http://blog.csdn.net/itachi85/article/details/52205464
http://blog.csdn.net/Tencent_Bugly/article/details/51354693
http://blog.csdn.net/qq_28746251/article/details/51476389
前言
EventBus是一個(gè)基于發(fā)布/訂閱的事件總線(數(shù)據(jù)通信框架),它簡化了組件之間、線程之間的數(shù)據(jù)通信操作,并且耦合度低、開銷小。
3.0版本后,使用注解來聲明訂閱者函數(shù)及其相關(guān)屬性,使得操作流程更加便捷,還提供index幫助提升其性能。
(如果你不喜歡用EventBus,而想用RxJava自己封裝一個(gè)RxBus來實(shí)現(xiàn)通信,
可以參考http://m.itdecent.cn/p/3a3462535b4d)
介紹
下面從 配置、基本使用、粘性事件、使用index優(yōu)化、簡單封裝、混淆 這幾個(gè)部分來介紹EventBus。
1. 配置
在Module下的build.gradle中添加
//EventBus
compile 'org.greenrobot:eventbus:3.0.0'
annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.0.1'//用于eventbus開啟Index加速
2. 基本使用
使用步驟分為定義事件、訂閱事件、發(fā)送事件、處理事件、取消訂閱五步
2.1 定義事件
先定義一個(gè)你打算發(fā)送的事件類,里面添加你要發(fā)送的數(shù)據(jù)變量。
變量的類型除了基本數(shù)據(jù)類型,也可以是自定義的實(shí)體類。
public class MovieEvent {
private int count;
public MovieEvent(int count) {
this.count = count;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
}
2.2 訂閱事件
在你要接收事件的地方訂閱事件:
public class MovieActivity{
....
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//確保之前未訂閱過,再調(diào)用訂閱語句,以免報(bào)錯(cuò)
if(!EventBus.getDefault().isRegistered(subscriber)){
EventBus.getDefault().register(subscriber);
}
}
....
}
2.3 發(fā)送事件
在你要發(fā)送事件的地方,調(diào)用
EventBus.getDefault().post(new MovieEvent(1));
這里需要注意,發(fā)送的事件是屬于引用傳遞,也就是說,發(fā)送事件后,你在事件處理函數(shù)中對接收到的事件進(jìn)行了修改,那么發(fā)送源頭的事件也會(huì)跟著改變。所以如果不想影響到發(fā)送源頭的數(shù)據(jù),建議new對象后再發(fā)送。
另外,EventBus提供了一個(gè)方法用于發(fā)送粘性事件,粘性事件相關(guān)內(nèi)容會(huì)在下面另外介紹。
EventBus.getDefault().postSticky(new MovieEvent(1));
2.4 處理事件
在接收事件的地方添加處理事件的方法,請與訂閱事件方法處于同一個(gè)類下,以保證能成功訂閱事件
public class MovieActivity{
....
//聲明處理事件的方法
@Subscribe
public void handlerEvent(MovieEvent event) {
//處理事件
int count = event.getCount();
...
}
....
}
你可以自定義處理事件方法的名稱,但必須加上@Subscribe注解來聲明該方法為事件接收處理方法。
方法的參數(shù)用于指定你要接收事件類型。比如參數(shù)為MovieEvent event,則表示接收類型為MovieEvent的事件,其他類型的事件將不會(huì)接收到。
另外@Subscribe里面可以對 處理事件時(shí)所在的線程、事件接收的優(yōu)先級、是否為粘性事件 進(jìn)行設(shè)置
- 處理事件時(shí)所在的線程
@Subscribe(threadMode = 線程類型)
線程類型有以下四種選擇:
- ThreadMode.POSTING:默認(rèn)的類型。表示處理事件時(shí)所在的線程將會(huì)與事件發(fā)送所在的線程一致,也就是兩者的執(zhí)行都處于同一個(gè)線程。
- ThreadMode.MAIN:表示處理事件時(shí)所在的線程將切換為UI主線程。如果發(fā)送事件所在的線程本來就是UI主線程,則不會(huì)切換。
- ThreadMode.BACKGROUND:表示處理事件時(shí)所在的線程將切換為后臺(tái)線程。如果發(fā)送事件所在的線程本來就是后臺(tái)線程,則不會(huì)切換。
- ThreadMode.ASYNC:表示處理事件時(shí)所在的線程將會(huì)切換為一個(gè)新建的獨(dú)立子線程。
- 優(yōu)先級
@Subscribe(priority = 100)
priority用于指定接收事件的優(yōu)先級,默認(rèn)值為0。
優(yōu)先級高的事件處理函數(shù)將先收到發(fā)送的事件,你可以在優(yōu)先級高的事件處理函數(shù)中攔截事件,不讓它繼續(xù)往下傳遞,攔截方法如下
EventBus.getDefault().cancelEventDelivery(event);
- 粘性事件
@Subscribe(sticky = true)
sticky用來聲明是否接收訂閱前就已發(fā)出的粘性事件,默認(rèn)值為false,具體介紹請看后面的“粘性事件”
2.5 取消訂閱
在退出或者不需要接收事件時(shí),取消訂閱
public class MovieActivity{
....
@Override
protected void onDestroy() {
super.onDestroy();
//取消訂閱
if (EventBus.getDefault().isRegistered(subscriber)) {
EventBus.getDefault().unregister(subscriber);
}
}
....
}
3. 粘性事件
一般我們的使用流程為:訂閱事件---》發(fā)送事件---》接收處理事件。
那如果現(xiàn)在希望發(fā)送事件---》訂閱事件---》接收處理事件,這可以實(shí)現(xiàn)嗎?答案是可以的。
EventBus提供的粘性事件便可實(shí)現(xiàn)這一場景。
3.1 使用步驟
使用步驟和普通事件的基本一樣,但有兩點(diǎn)需注意:
- 注冊事件接收的操作(EventBus.getDefault().register(this);)需在控件初始化后再執(zhí)行,否則會(huì)接收不到,這里建議放在onStart的生命周期中執(zhí)行。
- 事件的發(fā)送調(diào)用的是postSticky(event),事件處理函數(shù)需聲明@Subscriber(sticky = true)。
//事件發(fā)送方
//發(fā)送粘性事件
EventBus.getDefault().postSticky(new MovieEvent(1));
//事件接收處理方
//發(fā)送完粘性事件后再進(jìn)行訂閱事件
EventBus.getDefault().register(this);//注冊事件接收
//接收處理訂閱前發(fā)出的粘性事件
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
public void handleEvent(MovieEvent event) {
//處理事件
int count = event.getCount();
}
3.2 使用場景
這里舉一個(gè)使用場景:Activity間跳轉(zhuǎn)傳值。參考自http://www.cnblogs.com/ldq2016/p/5387444.html
我們平時(shí)都是使用Intent攜帶數(shù)據(jù)來實(shí)現(xiàn),如果要傳遞的是自定義的實(shí)體類,還需要進(jìn)行序列化操作。下面大致演示如何使用EventBus的粘性事件來實(shí)現(xiàn)這一場景。
ActivityA跳轉(zhuǎn)到ActivityB,并將Movie對象傳遞過去。
//ActivityA中的代碼
Movie movie = new Movie();
//發(fā)送粘性事件,傳送movie
EventBus.getDefault().postSticky(movie);
//跳轉(zhuǎn)到ActivityB
startActivity(new Intent(this, ActivityB.class));
//ActivityB中的代碼
//訂閱事件
EventBus.getDefault().register(this);
//獲取訂閱前ActivityA發(fā)送的粘性事件
@Subscribe(sticky = true)
public void getDataFromOtherActivity(Movie movie) {
//得到ActivityA的Movie對象,進(jìn)行具體操作。
}
如果大家還有其他使用場景,歡迎留言分享~
3.3 補(bǔ)充
1)EventBus僅保存最新發(fā)送的粘性事件。
2)手動(dòng)獲取、移除粘性事件
//手動(dòng)獲取粘性事件
MovieEvent movieEvent = EventBus.getDefault().getStickyEvent(MovieEvent.class);
if(movieEvent != null) {
//移除粘性事件
EventBus.getDefault().removeStickyEvent(movieEvent);
}
3)
發(fā)送粘性事件后,對于在發(fā)送前就已經(jīng)訂閱事件的訂閱者,它們都會(huì)收到類型相符的粘性事件,不管它們的事件處理方法是否聲明為sticky=true。
而對于在發(fā)送后才進(jìn)行訂閱事件的訂閱者,其事件處理方法必須聲明為sticky=true才能收到類型相符的粘性事件。
4. 使用index優(yōu)化
在3.0版本之前,為了保證性能,EventBus在遍歷尋找訂閱者的回調(diào)方法時(shí)使用反射而不是注解,而在3.0版本由于采用注解,從下圖可以看到,其性能比2.4版本要下降很多。
為了在使用注解的情況下保證高性能,EventBus提供了通過開啟Index來提升性能的方法,從下圖可以看到,開啟了Index之后,其性能提高了許多倍。
下面介紹如何生成和開啟索引。
4.1 生成索引
網(wǎng)上很多關(guān)于Index的文章中,是通過添加以下配置來生成的
//project下的build.gradle文件
buildscript {
dependencies {
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}
}
//module下的build.gradle文件
apply plugin: 'com.neenbedankt.android-apt'
apt {
arguments {
eventBusIndex "com.dev.base.MyEventBusIndex"
}
}
dependencies {
compile 'org.greenrobot:eventbus:3.0.0'
apt 'org.greenrobot:eventbus-annotation-processor:3.0.1'
}
但是,如果你的項(xiàng)目中也使用了ButterKnife庫,那么添加上面的配置后會(huì)導(dǎo)致ButterKnife無法正常工作;另外,android-apt的作者已在官網(wǎng)發(fā)表聲明表示后續(xù)將不會(huì)繼續(xù)維護(hù)android-apt,所以并不推薦這個(gè)方式實(shí)現(xiàn),而是使用Android推出的官方插件annotationProcessor來替代apt。
通過annotationProcessor來設(shè)置生成index的配置會(huì)更加便捷,如下:
//module下的build.gradle文件
android{
defaultConfig {
//...省略其他配置
javaCompileOptions {
annotationProcessorOptions {
arguments = [ eventBusIndex : "com.dev.base.MyEventBusIndex" ]
}
}
}
}
dependencies {
compile 'org.greenrobot:eventbus:3.0.0'
annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.0.1'
}
添加以上配置后,編譯 運(yùn)行 項(xiàng)目,執(zhí)行了一次“發(fā)送事件”操作后,如果在\build\generated\source\apt\debug\項(xiàng)目包名\下生成了你指定的Index類,則表示生成index成功,如下圖所示。
4.2 開啟索引
生成index之后,在Appliction中調(diào)用addIndex()方法開啟即可。
EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();
5. 簡單封裝
public class EventBusManager {
//開啟Index加速
public static void openIndex() {
EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();
}
//訂閱事件
public static void register(Object subscriber) {
if(!EventBus.getDefault().isRegistered(subscriber)){
EventBus.getDefault().register(subscriber);
}
}
//取消訂閱
public static void unregister(Object subscriber) {
if (EventBus.getDefault().isRegistered(subscriber)) {
EventBus.getDefault().unregister(subscriber);
}
}
//終止事件繼續(xù)傳遞
public static void cancelDelivery(Object event) {
EventBus.getDefault().cancelEventDelivery(event);
}
//獲取保存起來的粘性事件
public static <T> T getStickyEvent(Class<T> classType){
return EventBus.getDefault().getStickyEvent(classType);
}
//刪除保存中的粘性事件
public static void removeStickyEvent(Object event) {
EventBus.getDefault().removeStickyEvent(event);
}
//發(fā)送事件
public static void postEvent(Object event){
EventBus.getDefault().post(event);
}
//發(fā)送粘性事件
public static void postStickyEvent(Object event) {
EventBus.getDefault().postSticky(event);
}
}
DevRing/Demo中已對EventBus進(jìn)行了封裝,在Activity和Fragment中,只需重寫isUseEventBus()方法并返回true,則會(huì)自動(dòng)對該頁面進(jìn)行訂閱和解除訂閱的操作,詳情請查閱代碼。
6. 混淆
在proguard-rules.pro文件中添加以下內(nèi)容進(jìn)行混淆配置
#EventBus開始
-keepattributes *Annotation*
#如果使用了EventBus index進(jìn)行優(yōu)化加速,就必須加上這個(gè)
-keepclassmembers class ** {
@org.greenrobot.eventbus.Subscribe <methods>;
}
-keep enum org.greenrobot.eventbus.ThreadMode { *; }
#如果使用了Async類型的線程
-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {
<init>(java.lang.Throwable);
}
#EventBus結(jié)束