安卓基礎開發庫,讓開發簡單點。
DevRing & Demo地址:https://github.com/LJYcoder/DevRing
學習/參考地址:
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是一個基于發布/訂閱的事件總線(數據通信框架),它簡化了組件之間、線程之間的數據通信操作,并且耦合度低、開銷小。
3.0版本后,使用注解來聲明訂閱者函數及其相關屬性,使得操作流程更加便捷,還提供index幫助提升其性能。
(如果你不喜歡用EventBus,而想用RxJava自己封裝一個RxBus來實現通信,
可以參考http://www.lxweimin.com/p/3a3462535b4d)
介紹
下面從 配置、基本使用、粘性事件、使用index優化、簡單封裝、混淆 這幾個部分來介紹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. 基本使用
使用步驟分為定義事件、訂閱事件、發送事件、處理事件、取消訂閱五步
2.1 定義事件
先定義一個你打算發送的事件類,里面添加你要發送的數據變量。
變量的類型除了基本數據類型,也可以是自定義的實體類。
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);
//確保之前未訂閱過,再調用訂閱語句,以免報錯
if(!EventBus.getDefault().isRegistered(subscriber)){
EventBus.getDefault().register(subscriber);
}
}
....
}
2.3 發送事件
在你要發送事件的地方,調用
EventBus.getDefault().post(new MovieEvent(1));
這里需要注意,發送的事件是屬于引用傳遞,也就是說,發送事件后,你在事件處理函數中對接收到的事件進行了修改,那么發送源頭的事件也會跟著改變。所以如果不想影響到發送源頭的數據,建議new對象后再發送。
另外,EventBus提供了一個方法用于發送粘性事件,粘性事件相關內容會在下面另外介紹。
EventBus.getDefault().postSticky(new MovieEvent(1));
2.4 處理事件
在接收事件的地方添加處理事件的方法,請與訂閱事件方法處于同一個類下,以保證能成功訂閱事件
public class MovieActivity{
....
//聲明處理事件的方法
@Subscribe
public void handlerEvent(MovieEvent event) {
//處理事件
int count = event.getCount();
...
}
....
}
你可以自定義處理事件方法的名稱,但必須加上@Subscribe注解來聲明該方法為事件接收處理方法。
方法的參數用于指定你要接收事件類型。比如參數為MovieEvent event,則表示接收類型為MovieEvent的事件,其他類型的事件將不會接收到。
另外@Subscribe里面可以對 處理事件時所在的線程、事件接收的優先級、是否為粘性事件 進行設置
- 處理事件時所在的線程
@Subscribe(threadMode = 線程類型)
線程類型有以下四種選擇:
- ThreadMode.POSTING:默認的類型。表示處理事件時所在的線程將會與事件發送所在的線程一致,也就是兩者的執行都處于同一個線程。
- ThreadMode.MAIN:表示處理事件時所在的線程將切換為UI主線程。如果發送事件所在的線程本來就是UI主線程,則不會切換。
- ThreadMode.BACKGROUND:表示處理事件時所在的線程將切換為后臺線程。如果發送事件所在的線程本來就是后臺線程,則不會切換。
- ThreadMode.ASYNC:表示處理事件時所在的線程將會切換為一個新建的獨立子線程。
- 優先級
@Subscribe(priority = 100)
priority用于指定接收事件的優先級,默認值為0。
優先級高的事件處理函數將先收到發送的事件,你可以在優先級高的事件處理函數中攔截事件,不讓它繼續往下傳遞,攔截方法如下
EventBus.getDefault().cancelEventDelivery(event);
- 粘性事件
@Subscribe(sticky = true)
sticky用來聲明是否接收訂閱前就已發出的粘性事件,默認值為false,具體介紹請看后面的“粘性事件”
2.5 取消訂閱
在退出或者不需要接收事件時,取消訂閱
public class MovieActivity{
....
@Override
protected void onDestroy() {
super.onDestroy();
//取消訂閱
if (EventBus.getDefault().isRegistered(subscriber)) {
EventBus.getDefault().unregister(subscriber);
}
}
....
}
3. 粘性事件
一般我們的使用流程為:訂閱事件---》發送事件---》接收處理事件。
那如果現在希望發送事件---》訂閱事件---》接收處理事件,這可以實現嗎?答案是可以的。
EventBus提供的粘性事件便可實現這一場景。
3.1 使用步驟
使用步驟和普通事件的基本一樣,但有兩點需注意:
- 注冊事件接收的操作(EventBus.getDefault().register(this);)需在控件初始化后再執行,否則會接收不到,這里建議放在onStart的生命周期中執行。
- 事件的發送調用的是postSticky(event),事件處理函數需聲明@Subscriber(sticky = true)。
//事件發送方
//發送粘性事件
EventBus.getDefault().postSticky(new MovieEvent(1));
//事件接收處理方
//發送完粘性事件后再進行訂閱事件
EventBus.getDefault().register(this);//注冊事件接收
//接收處理訂閱前發出的粘性事件
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
public void handleEvent(MovieEvent event) {
//處理事件
int count = event.getCount();
}
3.2 使用場景
這里舉一個使用場景:Activity間跳轉傳值。參考自http://www.cnblogs.com/ldq2016/p/5387444.html
我們平時都是使用Intent攜帶數據來實現,如果要傳遞的是自定義的實體類,還需要進行序列化操作。下面大致演示如何使用EventBus的粘性事件來實現這一場景。
ActivityA跳轉到ActivityB,并將Movie對象傳遞過去。
//ActivityA中的代碼
Movie movie = new Movie();
//發送粘性事件,傳送movie
EventBus.getDefault().postSticky(movie);
//跳轉到ActivityB
startActivity(new Intent(this, ActivityB.class));
//ActivityB中的代碼
//訂閱事件
EventBus.getDefault().register(this);
//獲取訂閱前ActivityA發送的粘性事件
@Subscribe(sticky = true)
public void getDataFromOtherActivity(Movie movie) {
//得到ActivityA的Movie對象,進行具體操作。
}
如果大家還有其他使用場景,歡迎留言分享~
3.3 補充
1)EventBus僅保存最新發送的粘性事件。
2)手動獲取、移除粘性事件
//手動獲取粘性事件
MovieEvent movieEvent = EventBus.getDefault().getStickyEvent(MovieEvent.class);
if(movieEvent != null) {
//移除粘性事件
EventBus.getDefault().removeStickyEvent(movieEvent);
}
3)
發送粘性事件后,對于在發送前就已經訂閱事件的訂閱者,它們都會收到類型相符的粘性事件,不管它們的事件處理方法是否聲明為sticky=true。
而對于在發送后才進行訂閱事件的訂閱者,其事件處理方法必須聲明為sticky=true才能收到類型相符的粘性事件。
4. 使用index優化
在3.0版本之前,為了保證性能,EventBus在遍歷尋找訂閱者的回調方法時使用反射而不是注解,而在3.0版本由于采用注解,從下圖可以看到,其性能比2.4版本要下降很多。
為了在使用注解的情況下保證高性能,EventBus提供了通過開啟Index來提升性能的方法,從下圖可以看到,開啟了Index之后,其性能提高了許多倍。
下面介紹如何生成和開啟索引。
4.1 生成索引
網上很多關于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'
}
但是,如果你的項目中也使用了ButterKnife庫,那么添加上面的配置后會導致ButterKnife無法正常工作;另外,android-apt的作者已在官網發表聲明表示后續將不會繼續維護android-apt,所以并不推薦這個方式實現,而是使用Android推出的官方插件annotationProcessor來替代apt。
通過annotationProcessor來設置生成index的配置會更加便捷,如下:
//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'
}
添加以上配置后,編譯 運行 項目,執行了一次“發送事件”操作后,如果在\build\generated\source\apt\debug\項目包名\下生成了你指定的Index類,則表示生成index成功,如下圖所示。
4.2 開啟索引
生成index之后,在Appliction中調用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);
}
}
//終止事件繼續傳遞
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);
}
//發送事件
public static void postEvent(Object event){
EventBus.getDefault().post(event);
}
//發送粘性事件
public static void postStickyEvent(Object event) {
EventBus.getDefault().postSticky(event);
}
}
DevRing/Demo中已對EventBus進行了封裝,在Activity和Fragment中,只需重寫isUseEventBus()方法并返回true,則會自動對該頁面進行訂閱和解除訂閱的操作,詳情請查閱代碼。
6. 混淆
在proguard-rules.pro文件中添加以下內容進行混淆配置
#EventBus開始
-keepattributes *Annotation*
#如果使用了EventBus index進行優化加速,就必須加上這個
-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結束