安卓基础开发库,让开发简单点。
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来实现通信,
可以参考//www.greatytc.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结束