一、 概述
EventBus 是适用于 Android 和 Java 的发布/订阅事件总线。主要功能是替代 Intent、Handler、BroadCast 在 Activity、Fragment、Service 线程之间传递消息。EventBus 能够简化应用组件间的通信,解耦 (有效分离) 事件的发送者和接收者,避免复杂和容易出错的依赖和生命周期问题,开销小,代码更优雅。但是需要注意的是 EventBus 不能用于进程间通信。 Andorid 组件间通信,可能都是用 Handler 消息机制或者广播机制来实现通信,但是它们代码量大,组件上容易产生耦合 。为什么选择使用 EventBus 来做通信?
二、 使用步骤
- 定义事件对象 事件对象可以试任意java类型,没有特殊要求,比如String、int、自定义类等。
public class MessageEvent {
public String type;
}
- 在接收消息的页面注册事件
EventBus.getDefault().register(this);
register(Object subscriber):EventBus订阅事件的方法,通过EventBus.getDefault()获取事件总线实例;参数subscriber为订阅者,订阅者有处理事件的方法,并且必须添加@Subscribe注解。 只有注册了订阅事件,才会接收到消息。注意通常根据Activity和Fragment的生命周期注册和注销事件。
- 订阅者实现事件处理方法
也称为“订阅者方法”,当发布对应事件类型时,该方法被调用(在接收消息的页面)。
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEvent(MessageEvent event){
//接收事件后
}
- @Subscribe: 必须使用@Subscribe注解来定义订阅者方法,否则事件处理方法无法生效。
- threadMode: 线程模式,表示在哪个线程里面执行,ThreadMode.MAIN表示在主线程执行该方法。
- onEvent(MessageEvent event): 事件处理方法名onEvent()自定义(即任意取名);参数类型MessageEvent为定义接收事件的对象类型,要与发布事件的类型一致,否则无法接收时间。
- 发布事件
EventBus.getDefault().post(Object event);
- post(Object event): EventBus发送事件的方法,参数event为事件对象,是Object任意类型,这里发送的类型需要与接收时间的类型一致。当前与事件类型匹配的所有已注册的事件都会接收到。
- 在接收消息的页面注销(解除注册)事件
EventBus.getDefault().unregister(this);
- unregister(Object subscriber): 订阅者注销事件的方法,如果事件不需要使用了必须调用该方法注销事件。当消息页面不存在或者不需要事件了注销该事件。 三、 项目实践
- 添加依赖
implementation 'org.greenrobot:eventbus:3.2.0'
- 定义事件对象
public class MessageEvent {
public String type;
public MessageEvent(String type){
this.type = type;
}
}
这个类很简单,只定义了一个参数type,构造时传入一个字符串,它是用于我们发送事件的事件对象携带参数的封装类,在下面订阅者方法接收的参数中也是以MessageEvent为接收类型才能接收到。两个类型要一致才能成功的接收到发出的数据。 3. 在接收消息的页面注册和注销事件
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EventBus.getDefault().register(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
发布事件前需要明确接收消息的页面已经创建并且注册了时间,另外粘性事件能实现先发布事件,后续再注册事件,这样也能接收事件,后面说明 4. 订阅者实现事件处理的方法
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEvent(MessageEvent event){
//接收事件后
Toast.makeText(this,event.type,Toast.LENGTH_SHORT).show();
}
- 发布事件
EventBus.getDefault().post(new MessageEvent("2"));
四、 粘性事件的使用
在普通事件中,订阅者(接收消息的页面)要先注册,才能接收到发布的事件;也就是说接收消息的页面还没创建或者未注册订阅者,那么处理事件的方法根本无法接收发送者(发送消息的页面)发布的事件。EventBus提供了一种粘性事件,能在发送事件之后再订阅该事件也能接收到该事件。与普通事件不同,普通事件是先注册后发布,粘性事件可以现发布后注册。
- 粘性事件的事件函数处理方法,需要在注解中添加sticky=true标识,表示该事件是粘性事件:
@Subscribe(threadMode = ThreadMode.MAIN,sticky = true)
public void onStickEvent(MessageEvent event){
//接收事件后
ToastUtils.show(event.type);
}
- 使用PostSticky()发布粘性事件:
EventBus.getDefault().postSticky(new MessageEvent("2"));
发布粘性事件后,EventBus将会一直存在粘性事件,在不需要粘性事件的时候需要及时移除,移除的方法有下面几个:
//移除指定的粘性事件
removeStickyEvent(Object event);
//移除指定对象类型的粘性事件
removeStickyEvent(Class<T> eventType);
//移除所有粘性事件
removeAllStickyEvents();
举例,创建两个Activity,在StickySendActivity中向StickyReceiveActivity发布粘性事件,发布完后跳转到StickyReceiveActivity中,StickyReceiveActivity创建并注册EventBus,订阅粘性事件的处理方法:
public class StickySendActivity extends AppCompatActivity implements View.OnClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sticky_send);
findViewById(R.id.btn_send).setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_send://发布事件
//4.发布粘性事件
EventBus.getDefault().postSticky("StickySendActivity发送粘性事件");
startActivity(new Intent(this, StickyReceiveActivity.class));
break;
}
}
}
点击发送粘性事件按钮向StickyReceiveActivity发布粘性事件,发布完后跳转到StickyReceiveActivity中。
public class StickyReceiveActivity extends AppCompatActivity{
private TextView mTv_content;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sticky_receive);
mTv_content = findViewById(R.id.tv_content);
//1.注册事件
EventBus.getDefault().register(this);
}
//3.接收StickySendActivity粘性事件处理, sticky = true表示是粘性事件
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
public void onStickyEvent(String str) {
//TODO 接收事件后Do something
mTv_content.setText("onStickyEvent:接收到" + str);
Toast.makeText(this, "onStickyEvent:接收到" + str, Toast.LENGTH_SHORT).show();
EventBus.getDefault().removeStickyEvent(this)//移除粘性事件
}
@Override
protected void onDestroy() {
super.onDestroy();
//2.反注册事件
EventBus.getDefault().unregister(this);
}
}
StickyReceiveActivity 创建并注册 EventBus,订阅粘性事件的处理方法,接收到粘性事件发送的数据,显示在 mTv_content 控件上并吐司提示,然后通过 removeStickyEvent(this) 移除当前的粘性事件。
五、 EventBus的几种线程模式 默认为ThreadMode.POSTING
| 线程 | 含义 | 特点 |
|---|---|---|
| POSTING | 默认线程,表示订阅者将在发布事件的同一线程中被调用 | 适用于不需要主线程就可以在短时间内完成的简单任务,避免了线程切换,开销更小 |
| MAIN | 表示在Android中,订阅者在Android主线程中被调用 | 如果发布事件的线程是主线程,订阅者的事件处理函数将直接被调用。如果发布事件的线程不是主线程,则将事件加入主线程队列中,排队等待执行;因此这里不能进行耗时操作,注意不能阻塞主线程 |
| MAIN_ORDERED | 表示在Android中,订阅者在Android主线程中被调用 | 与MAIN不同的是,无论发布事件的线程是在哪个线程,事件都将发送到主线程队列总是排队等待传递。注意不能阻塞主线程 |
| BACKGROUND | 表示在Android中,订阅者在后台线程中被调用 | 在Android中,如果发布线程不是主线程,订阅者的事件处理函数直接使用该线程,如果发布线程是主线程,那么事件处理函数会开启一个后台线程,有序分发事件,注意不能阻塞后台线程,这里不能进行UI操作;如果不是在Android上,总是使用一个后台线程 |
| ASYNC | 表示无论发布线程是什么线程,订阅者都会创建一个新的子线程执行 | 使用于耗时操作,尽量避免同时触发大量的耗时较长的异步操作,EventBus使用线程池高效的复用已经完成异步操作的线程 |
那么说明 POSTING 模式的订阅者处理函数线程与发布线程一致,MAIN 和 MAIN_ORDERED 模式的订阅者处理函数线程为主线程,BACKGROUND 和 ASYNC 模式的订阅者处理函数线程为后台线程。
五、 几种线程模式使用
接收消息页面
//订阅者的接收事件处理函数
//处理函数执行线程与发布线程一致
@Subscribe(threadMode = ThreadMode.POSTING)
public void onEventPosting(MessageEvent event){
Log.e(TAG,"onEventPosting:" + Thread.currentThread().getName());
}
//处理函数执行线程为主线程
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMain(MessageEvent event){
Log.e(TAG,"onEventMain:" + Thread.currentThread().getName());
}
//处理函数执行线程为主线程
@Subscribe(threadMode = ThreadMode.MAIN_ORDERED)
public void onEventMainOrdered(MessageEvent event){
Log.e(TAG,"onEventMainOrdered:" + Thread.currentThread().getName());
}
//处理函数执行线程为后台线程
@Subscribe(threadMode = ThreadMode.BACKGROUND)
public void onEventBackground(MessageEvent event){
Log.e(TAG,"onEventBackground:" + Thread.currentThread().getName());
}
//处理函数执行线程为后台线程
@Subscribe(threadMode = ThreadMode.ASYNC)
public void onEventAsync(MessageEvent event){
Log.e(TAG,"onEventAsync:" + Thread.currentThread().getName());
}
发送消息页面
EventBus.getDefault().post(new MessageEvent("1"));
打印数据如下
2021-11-30 15:13:09.268 15370-15370/com.htkj.an.myproject E/MainActivity: onEventMain:main
2021-11-30 15:13:09.268 15370-16389/com.htkj.an.myproject E/MainActivity: onEventAsync:pool-1-thread-1
2021-11-30 15:13:09.268 15370-15370/com.htkj.an.myproject E/MainActivity: onEventPosting:main
2021-11-30 15:13:09.268 15370-15370/com.htkj.an.myproject E/MainActivity: onEventMainOrdered:main
2021-11-30 15:13:09.268 15370-16390/com.htkj.an.myproject E/MainActivity: onEventBackground:pool-1-thread-2
可以看出POSTING模式的线程与发布线程一致,都是主线程;MAIN 和 MAIN_ORDERED 模式的线程为主线程,BACKGROUND 和 ASYNC 模式线程为后台线程。
- 事件的优先级 EventBus支持定义订阅者事件处理方法时指定事件传递的优先级。默认情况下,事件优先级为0,数值越大,优先级越高,在相同线程模式下,优先级高的比优先级低的先收到事件。
@Subscribe(threadMode = ThreadMode.MAIN,priority = 0)
public void onEventMain(MessageEvent event){
}
————————————————
参考原文地址