EventBus,輕鬆實現跨元件跨執行緒通訊
安卓基礎開發庫,讓開發簡單點。
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.jianshu.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結束
相關文章
- 【React】元件通訊 - 跨層通訊React元件
- 詳解 CmProcess 跨程式通訊的實現
- Java的通過管道來實現執行緒通訊Java執行緒
- 【Java多執行緒】輕鬆搞定Java多執行緒(二)Java執行緒
- 自己實現一個Electron跨程式訊息元件元件
- 輕鬆解決跨域問題跨域
- 跨源通訊、跨域訪問跨域
- Java多執行緒-執行緒通訊Java執行緒
- c#基礎,單執行緒,跨執行緒訪問和執行緒帶引數C#執行緒
- QT中跨執行緒警告的處理QT執行緒
- 一行程式碼實現Android的跨程式呼叫與通訊行程Android
- [iptables] 基於iptables實現的跨網路通訊
- 簡單跨程式使用EventBus
- Java執行緒通訊Java執行緒
- libuv執行緒通訊執行緒
- Android跨程式通訊Android
- 『動善時』JMeter基礎 — 38、JMeter中實現跨執行緒組關聯JMeter執行緒
- HarmonyOS NEXT應用開發之使用AKI輕鬆實現跨語言呼叫
- Java多執行緒學習——執行緒通訊Java執行緒
- Vue元件通訊中eventBus的使用Vue元件
- 多執行緒通訊軟體——登入介面的實現執行緒
- Java之執行緒通訊Java執行緒
- Android執行緒間通訊Android執行緒
- JUC執行緒高階---執行緒控制通訊Condition執行緒
- 多執行緒之間通訊及執行緒池執行緒
- java多執行緒5:執行緒間的通訊Java執行緒
- Java多執行緒學習(3)執行緒同步與執行緒通訊Java執行緒
- Spring Boot 通過CORS實現跨域Spring BootCORS跨域
- Jmeter 跨執行緒組引數傳遞的方法JMeter執行緒
- Jmeter-全域性變數跨執行緒組使用JMeter變數執行緒
- 利用訊號量實現執行緒順序執行執行緒
- 子執行緒與UI執行緒的通訊(委託)執行緒UI
- Android中的執行緒通訊Android執行緒
- 深入理解執行緒通訊執行緒
- 多執行緒Demo學習(執行緒的同步,簡單的執行緒通訊)執行緒
- CentOS 7安裝etcd和flannel實現docker跨物理機通訊CentOSDocker
- WPF使用事件聚合器,實現任意頁面跨頁通訊事件
- 一個SystemC執行緒與SystemVerilog執行緒通訊的例子執行緒