简介
EventBus能够简化各组件间的通信,能有效的分离事件发送方和接收方(解耦),能避免复杂和容易出错的依赖性和生命周期问题。采用的是发布/订阅设计模式。它能简化应用程序内各组件间、组件与后台线程间的通信。在Android常用于Activity、Fragment和后台Service之间通信、传递数据。
pom依赖:
<dependency>
<groupId>org.greenrobot</groupId>
<artifactId>eventbus</artifactId>
<version>3.1.1</version>
</dependency>
实现原理
每一个eventBus都是订阅对象Subscribe、被订阅对象event的保存者。当订阅对象向eventBus注册自己时,eventBus会解析出Subscribe所订阅的event.并以两个维度记录下来。
第一个维度subscriptionsByEventType:以Map<event.class, list<Subscription.class>>。其中Subscription是Subscribe和event处理方法的包装。
第二个维度typesBySubscriber:以Map<Subscribe.class, List<event.class>>记录下来。
当事件发布者向eventBus发布了even之后,eventBus就会从对应的记录中获取订阅者以及处理方法,再使用反射的方式执行event处理方法
EventBus四要素
- 事件(Event):又可称为消息,本文中统一用事件表示。其实就是一个对象,可以是网络请求返回的字符串,也可以是某个开关状态等等。事件类型(EventType)指事件所属的 Class。事件分为一般事件和 Sticky 事件,相对于一般事件,Sticky 事件不同之处在于,当事件发布后,再有订阅者开始订阅该类型事件,依然能收到该类型事件最近一个 Sticky 事件。
- 订阅者(Subscriber):订阅某种事件类型的对象。当有发布者发布这类事件后,EventBus 会执行订阅者的 onEvent 函数,这个函数叫事件响应函数。在3.1.1之后可以使用注解的方式来实现响应函数,并不需要固定的函数才能接收订阅数据。订阅者存在优先级,优先级高的订阅者可以取消事件继续向优先级低的订阅者分发,默认所有订阅者优先级都为 0。
- 发布者(Publisher):发布某事件的对象,发布通过 register 接口将某个订阅添加到事件总线中,unregister 接口退订。通过 post 接口发布事件。
- ThreadMode
使用流程
- 第一步 创建一个基本类,也就是事件-消息 用于订阅者和发布者之间传递的的数据。使用 @Subscribe 注释处理方法
@Data
public class Information {
private String id;
private String name;
public Information(String id, String name) {
this.id = id;
this.name = name;
}
@Override
public String toString(){
return ToStringBuilder.reflectionToString(this);
}
}
- 第二步 创建一个订阅者,用于订阅发布者发布的消息。
public class SubscribeEvent {
private Logger LOGGER = LoggerFactory.getLogger(SubscribeEvent.class);
@Subscribe(threadMode = ThreadMode.XXX)
public void onAsyncEvent(Information information) {
if (null == information) return;
LOGGER.info(String.format("%s【AsyncEvent POSTING模式】接收到事件,消息内容为%s", Thread.currentThread().getName(), information.toString()));
}
@Subscribe(threadMode = ThreadMode. XXX)
public void onAsyncEvent1(Information1 information1) {
if (null == information1) return;
LOGGER.info(String.format("%s【AsyncEvent1 POSTING模式】接收到事件,消息内容为%s", Thread.currentThread().getName(), information1.toString()));
}
}
四种模式
EventBus3.0有四种线程模型,四种模型都是用来定义执行方式。
先定义一个事件:
/**
* @author
* @description 第一步:定义事件,普通的java对象
**/
@Data
public class AsyncEvent {
//信息
private String msg;
public AsyncEvent(String msg) {
this.msg = msg;
}
@Override
public String toString() {
return "msg:" + msg;
}
}
@Subscribe(threadMode = ThreadMode.POSTING)
这种模式下,订阅者处理方法的执行和发布者(post()所在线程)使用同一个线程 ,对于一些耗时间的操作会占用大量的时间。所以当方法是耗时操作时,不建议在主线程中试用这种模式。该模式下传递事件开销最小,因为它完全避免了线程切换。--可以使用在java中。
订阅者
public class PostingEventSubscriber {
/**
* 默认POSTING模式
*
* @param event
*/
@Subscribe(threadMode = ThreadMode.POSTING)
public void onPostingEvent(AsyncEvent event) {
if (null == event) return;
System.out.println(String.format("%s[POSTING模式]接收到事件,%s", Thread.currentThread().getName(), event.toString()));
}
}
测试方法-发布者:两个不同线程执行发布流程
public static void main(String[] args) {
Thread mainThread = Thread.currentThread();
mainThread.setName("mainThread");
AsyncEventSubscriber asyncEventSubscriber = new AsyncEventSubscriber();
EventBus.getDefault().register(asyncEventSubscriber);
AsyncEvent asyncEvent = new AsyncEvent("hello world, async");
System.out.println("主线程中发布事件开始");
EventBus.getDefault().post(asyncEvent);
System.out.println("主线程中发布事件结束");
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
Thread childThread = Thread.currentThread();
childThread.setName("subThread");
AsyncEvent asyncEvent = new AsyncEvent("hello world, async");
System.out.println("子线程中发布事件开始");
EventBus.getDefault().post(asyncEvent);
System.out.println("子线程中发布事件结束");
}
});
thread.start();
}
执行结果:
主线程中发布事件开始
mainThread[POSTING模式]接收到事件,POSTING:hello world, async
主线程中发布事件结束
子线程中发布事件开始
subThread[POSTING模式]接收到事件,POSTING:hello world, async
子线程中发布事件结束
@Subscribe(threadMode = ThreadMode.MAIN)
这种模式下,订阅者处理方法方法在主线程中执行,与POSTING 差别不大。在Android开发中使用。
订阅对象:
/**
* MAIN模式
* @param event
*/
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMainEvent(AsyncEvent event) {
if (null == event) return;
System.out.println(String.format("%s[MAIN模式]接收到事件,消息内容为%s", Thread.currentThread().getName(), event.toString()));
}
测试-发布者:
public static void main(String[] args) {
Thread mainThread = Thread.currentThread();
mainThread.setName("mainThread");
AsyncEventSubscriber asyncEventSubscriber = new AsyncEventSubscriber();
EventBus.getDefault().register(asyncEventSubscriber);
AsyncEvent asyncEvent = new AsyncEvent("hello world, async");
System.out.println("主线程中发布事件开始");
EventBus.getDefault().post(asyncEvent);
System.out.println("主线程中发布事件结束");
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
Thread childThread = Thread.currentThread();
childThread.setName("subThread");
AsyncEvent asyncEvent = new AsyncEvent("hello world, async");
System.out.println("子线程中发布事件开始");
EventBus.getDefault().post(asyncEvent);
System.out.println("子线程中发布事件结束");
}
});
thread.start();
}
结果:
主线程中发布事件开始
mainThread[POSTING模式]接收到事件,POSTING:hello world, async
主线程中发布事件结束
子线程中发布事件开始
subThread[POSTING模式]接收到事件,POSTING:hello world, async
子线程中发布事件结束
@Subscribe(threadMode = ThreadMode.MAIN_ORDERED)
这种模式下,订阅者处理方法和MAIN的方法一样,只不过是判断依据不同--andriod中使用。
/**
* MAIN_ORDERED模式
*
* @param event
*/
@Subscribe(threadMode = ThreadMode.MAIN_ORDERED)
public void onAsyncEvent(AsyncEvent event) {
if (null == event) return;
System.out.println(String.format("%s[MAIN_ORDERED模式]接收到事件,%s", Thread.currentThread().getName(), event.toString()));
}
@Subscribe(threadMode = ThreadMode.ASYNC)
这种模式下,不论发布线程是否为主线程,订阅者处理方法都使用一个空闲线程来执行。和BackgroundThread不同的是,Async类的所有线程是相互独立的,因此不会出现卡线程的问题。适用场景:长耗时操作。
这种模式下,会使用一个线程池来执行线程。线程池是默认的:
ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool()
我们可以自己指定线程池。EventBusBuilder 提供了方法传入我们自己的线程池。
public EventBusBuilder executorService(ExecutorService executorService) {
this.executorService = executorService;
return this;
}
订阅者:
/**
* ASYNC模式
*
* @param event
*/
@Subscribe(threadMode = ThreadMode.ASYNC)
public void onAsyncEvent(AsyncEvent event) {
if (null == event) return;
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(String.format("%s[ASYNC模式]接收到事件,%s", Thread.currentThread().getName(), event.toString()));
}
测试-发布者:
public static void main(String[] args) {
//设置EventBus的线程池
EventBusBuilder builder = EventBus.builder();
builder.executorService(Executors.newFixedThreadPool(10));
EventBus eventBus = builder.build();
Thread mainThread = Thread.currentThread();
mainThread.setName("mainThread");
AsyncEventSubscriber asyncEventSubscriber = new AsyncEventSubscriber();
eventBus.register(asyncEventSubscriber);
AsyncEvent asyncEvent = new AsyncEvent("hello world, async");
System.out.println("主线程中发布事件开始");
eventBus.post(asyncEvent);
System.out.println("主线程中发布事件结束");
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
Thread childThread = Thread.currentThread();
childThread.setName("subThread");
AsyncEvent asyncEvent = new AsyncEvent("hello world, async");
System.out.println("子线程中发布事件开始");
eventBus.post(asyncEvent);
System.out.println("子线程中发布事件结束");
}
});
thread.start();
}
结果:
主线程中发布事件开始
主线程中发布事件结束
子线程中发布事件开始
子线程中发布事件结束
pool-2-thread-1[ASYNC模式]接收到事件,msg:hello world, async
pool-2-thread-2[ASYNC模式]接收到事件,msg:hello world, async
@Subscribe(threadMode = ThreadMode.BACKGROUND)
这种模式下,在后台线程中执行响应方法。如果发布线程不是主线程,则直接调用订阅者的事件响应函数,否则启动唯一的后台线程(BackgroundPoster)去处理。由于后台线程是唯一的,当事件超过一个的时候,它们会被放在队列中依次执行,因此该类响应方法虽然没有PostThread类和MainThread类方法对性能敏感,但最好不要有重度耗时的操作或太频繁的轻度耗时操作, 以造成其他操作等待。适用场景:操作轻微耗时且不会过于频繁,即一般的耗时操作都可以放在这里;使用这种模式的事件处理程序应该尽快返回以避免阻塞后台线程。--可以在java中使用
订阅对象:
/**
* BACKGROUND模式
*
* @param event
*/
@Subscribe(threadMode = ThreadMode.BACKGROUND)
public void onAsyncEvent(AsyncEvent event) {
if (null == event) return;
//暂停线程
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(String.format("%s[BACKGROUND模式]接收到事件,消息内容为%s", Thread.currentThread().getName(), event.toString()));
}
测试方式-发布者:
public static void main(String[] args) {
Thread mainThread = Thread.currentThread();
mainThread.setName("mainThread");
AsyncEventSubscriber asyncEventSubscriber = new AsyncEventSubscriber();
EventBus.getDefault().register(asyncEventSubscriber);
AsyncEvent asyncEvent = new AsyncEvent("hello world, async");
System.out.println("主线程中发布事件开始");
EventBus.getDefault().post(asyncEvent);
System.out.println("主线程中发布事件结束");
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
Thread childThread = Thread.currentThread();
childThread.setName("subThread");
AsyncEvent asyncEvent = new AsyncEvent("hello world, async");
System.out.println("子线程中发布事件开始");
EventBus.getDefault().post(asyncEvent);
System.out.println("子线程中发布事件结束");
}
});
thread.start();
测试结果:
主线程中发布事件开始
主线程中发布事件结束
子线程中发布事件开始
子线程中发布事件结束
pool-1-thread-1[BACKGROUND模式]接收到事件,消息内容为msg:hello world, async
pool-1-thread-1[BACKGROUND模式]接收到事件,消息内容为msg:hello world, async