EventBus是怎样管理事件总线的, 是不是最佳处理消息的方案?让我们一同来学习和认识.
EventBus由来
在面对应用程序内组件间, 组件与后台线程间的通信. 又如我们在处理耗时操作时, 等到耗时操作完成后通过Handler或者Broadcast去更新UI, 但是面对多个Activity之间需要通过Listener通信完成. 但是这些问题我们可以通过EventBus来完成.它是通过发布/订阅的方式来管理事件总线 .通过注解和反射机制将订阅者连同订阅函数保存起来, 然后在发送订阅的时候遍历订阅函数数组进行调用, 但是这种形式会影响到EventBus的执行效力.
EventBus的介绍
EventBus出自greenrobot, 与GreenDao是一家. 而EventBus3.0框架采用了建造者模式, 并加入注解, 通过注解的方式告知订阅函数运行在哪一个线程中.
EventBus主要角色
- Event 传递的事件对象
- Subscriber 事件的订阅者
- Publisher事件的发布者
- ThreadMode 定义函数在那种线程中执行
官网给出的各种角色的协作图
EventBus使用
- 在build.gradle中添加引用
compile 'org.greenrobot:eventbus:3.0.0'
2. 定义一个事件类型
public class EventInfoBean {
public String msg;
public EventInfoBean(String msg) {
this.msg = msg;
}
}
3.订阅事件
EventBus.getDefault().register(this);
4. 发布事件
EventBus.getDefault().post(new EventInfoBean("eventBus发送的消息"))
5. 解除订阅(一般放在onDestroy()中)
EventBus.getDefault().unregister(this);
6.订阅事件处理 @Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(EventInfoBean info) {
Toast.makeText(getApplicationContext(), "接收到的消息..." + info.msg, Toast.LENGTH_SHORT).show();
}
7.订阅事件的优先级
EventBus事件的优先级类似于广播的优先级, 优先级越高优先得到消息
@Subscribe(threadMode = ThreadMode.MAIN,priority = 100)
public void onDataSynEvent(EventInfoBean event) {
Log.e(TAG, "event---->" + event.msg);
}
8.终止事件传递
EventBus在发送有序广播的时候可以终止广播的往下传递
EventBus.getDefault().cancelEventDelivery(event) ;
9. 代码混淆
-keepattributes *Annotation*
-keepclassmembers class ** {
@org.greenrobot.eventbus.Subscribe <methods>;
}
-keep enum org.greenrobot.eventbus.ThreadMode { *; }
-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {
<init>(java.lang.Throwable);
}
EventBus线程模型说明
ThreadMode.MAIN
无论任何线程中发布事件, 事件处理函数都会在main线程中执行.该方法可以用来更新UI, 但是不能处理耗时操作
ThreadMode.BACKGROUND
如果发布事件是在UI线程中发布, 那么事件处理函数会在新的线程中运行, 如果发布事件本来就在子线程中发布的, 那么事件处理函数在发布事件的线程中执行. 此事件处理函数不可以进行UI更新操作
ThreadMode.POSTING
事件处理和事件发布在同一个线程中. 在事件处理函数中尽量避免执行耗时操作, 因为它会阻塞事件的传递, 有可能会引起ANR
ThreadMode.ASYNC
不管事件在哪个线程中发布, 该事件处理函数都会在新建的子线程中执行. 事件处理函数中禁止UI更新操作
实例
事件处理函数
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEventMain(FirstEvent firstEvent) {
Log.d(TAG, "接收到的消息.1.." + Thread.currentThread().getName());
}
@Subscribe(threadMode = ThreadMode.ASYNC)
public void onMessageEventAsync(FirstEvent firstEvent) {
Log.d(TAG, "接收到的消息.2.." + Thread.currentThread().getName());
}
@Subscribe(threadMode = ThreadMode.BACKGROUND)
public void onMessageEventBackground(FirstEvent firstEvent) {
Log.d(TAG, "接收到的消息.3.." + Thread.currentThread().getName());
}
@Subscribe(threadMode = ThreadMode.POSTING)
public void onMessageEventPosting(FirstEvent firstEvent) {
Log.d(TAG, "接收到的消息.4.." + Thread.currentThread().getName());
}
从UI线程中发布事件
发布事件
send.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
EventBus.getDefault().post(new FirstEvent("在同一个线程中执行"));
}
});
输出log
02-27 10:26:29.727 14358-14358/com.quanzi.eventbusdemo D/SameThreadActivity: 接收到的消息.1..main
02-27 10:26:29.727 14358-14358/com.quanzi.eventbusdemo D/SameThreadActivity: 接收到的消息.4..main
02-27 10:26:29.728 14358-14420/com.quanzi.eventbusdemo D/SameThreadActivity: 接收到的消息.2..pool-1-thread-1
02-27 10:26:29.729 14358-14421/com.quanzi.eventbusdemo D/SameThreadActivity: 接收到的消息.3..pool-1-thread-2
从子线程中发布事件
发布事件
send.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
new Thread(new Runnable() {
@Override
public void run() {
EventBus.getDefault().post(new FirstEvent("在同一个线程中执行"));
}
}).start();
}
});
输出log
02-27 10:30:45.027 14523-14541/com.quanzi.eventbusdemo D/SameThreadActivity: 接收到的消息.3..Thread-799
02-27 10:30:45.027 14523-14541/com.quanzi.eventbusdemo D/SameThreadActivity: 接收到的消息.4..Thread-799
02-27 10:30:45.027 14523-14542/com.quanzi.eventbusdemo D/SameThreadActivity: 接收到的消息.2..pool-1-thread-1
02-27 10:30:45.036 14523-14523/com.quanzi.eventbusdemo D/SameThreadActivity: 接收到的消息.1..main
EventBus粘性事件
EventBus的粘性事件类似于广播中的粘性广播. 粘性广播在实际开发中使用的比较少.粘性广播中订阅/解除订阅和普通事件一样, 但是在处理订阅函数时需要添加注解 sticky = true
处理粘性事件
@Subscribe(threadMode = ThreadMode.MAIN,sticky = true)
public void onDataSynEvent(FirstEvent event){
Log.d(TAG,"sticky..." + event.getMsg() + "..." + Thread.currentThread().getName());
}
发送粘性事件
EventBus.getDefault().postSticky(new FirstEvent("粘性事件..."));
移除粘性事件
粘性广播属于常驻广播, 如果不需要可以移除粘性事件
EventBus.getDefault().removeStickyEvent(new FirstEvent());
或调用移除所有粘性事件
EventBus.getDefault().removeAllStickyEvents();
EventBus的优缺点
优点 : 简化了各组件间的通信方式, 实现了解耦让业务代码更加简洁, 可以动态设置事件处理线程以及优先级
缺点 : 每一个事件必须自定义一个事件类, 造成事件类过多, 从而增加了维护的成本
EventBus3.0与2.X比较
1.代码更加简洁
2.性能更优. 由于EventBus 2.x 是采用反射的方式对整个注册类中的所有方法进行扫描来完成注册,因此在性能上有所影响。而EventBus3.0中提供EventBusAnnotationProcessor注解处理器在编译期通过读取Subscribe注解并解析、处理其中所包含的信息,再生成java类来保存所有订阅者的信息,从而比在运行时使用反射来获得这些订阅者的信息速度要快
以上是我个人对EventBus3.0的一些认识和理解,要是有什么地方不对的,欢迎各位同仁指正