背景
event bus 数据总线, 数据发送者发送信息到总线, 数据接收者从总线接收数据. 大概类似于下飞机去取行李的时候, 旅客在行李传送带旁边等候自己的行李, 在这个场景下, 飞机相当于发送者, 旅客为接收者, 传送带就是我们的数据总线, 旅客上飞机就是一个注册的过程. 与我们后面准备学习的guava中的EventBus不同的是, 旅客是自己"拉取"的行李, 但是在guava中是总线根据注册信息推送消息到订阅者. 更恰当的例子是去餐厅吃饭, 顾客进入餐厅并坐下, 这就是注册. 顾客点餐这就是订阅. 厨师做出来的菜会根据点餐列表, 将菜送到顾客面前, 这就是消息的发送, 如果多个顾客点了同一道菜, 那么厨师会做多道菜(? 多个副本?), 并分别送到对应的顾客面前. 给我的感觉就像一个系统内部的一个MQ~
废话到此结束, 下面通过demo了解下如何使用guava中的EventBus
EventBus
环境
guava版本
<dependency>
<artifactId>guava</artifactId>
<groupId>com.google.guava</groupId>
<version>28.0-jre</version>
</dependency>
jdk1.8
Junit4
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
驾照
guava的EventBus使用上突出一个简单, 我们只需要在EventBus上注册监听, 然后往EventBus发送消息, 最后根据消息类型将消息发给监听者. 代码代码搞起搞起
// 建立一个数据工具类, 你是司机, 往哪开你说了算~
public class AwesomeEventBusDriver {
// 准备好车
private static EventBus eventBus = new EventBus();
// 上车
public static void register(Object object) {
eventBus.register(object);
}
// 发车
public static void publishAnything(Object object) {
eventBus.post(object);
}
}
// 学员
public class AwesomeStudent {
// 发车了要告诉我
@Subscribe
public void listen(String obj) {
System.out.println("科一" + obj);
}
}
跑起来~
@Test
public void 科一() {
AwesomeEventBusDriver.register(new AwesomeStudent());
AwesomeEventBusDriver.publishAnything("通过~");
}
Ok~ 科一过了
现在是科二, 我们不光想知道过没过, 还要知道得了几分
public class AwesomeStudent_1 {
@Subscribe
public void 科二(String obj) {
System.out.println("科二" + obj);
}
@Subscribe
public void 科二分数(Integer obj) {
System.out.println("科二分数" + obj);
}
}
考科二拉
@Test
public void 科二() {
AwesomeEventBusDriver.register(new AwesomeStudent_1());
AwesomeEventBusDriver.publishAnything("通过~");
AwesomeEventBusDriver.publishAnything(100);
}
可以看到不同的数据类型会进入不同的方法执行. ok, 科二满分通过.
还有考科三的同学一起查成绩
public class AwesomeStudent_2 {
@Subscribe
public void 科三分数(Double obj) {
System.out.println("科三" + obj);
}
}
@Test
public void 科三() {
AwesomeEventBusDriver.register(new AwesomeStudent_1());
AwesomeEventBusDriver.register(new AwesomeStudent_2());
AwesomeEventBusDriver.publishAnything(100);
AwesomeEventBusDriver.publishAnything(90.5);
}
多个类和多个方法区别不大.
最后科四~
// 教练来喽
public class AwesomeCoach {
@Subscribe
public void all(DeadEvent event) {
System.out.println(event);
}
}
// 自定义了两个类
public class AwesomeMessageEvent {
private String message;
public AwesomeMessageEvent(String message){
this.message = message;
}
@Override
public String toString() {
return "AwesomeEvent{" +
"message='" + message + '\'' +
'}';
}
}
public class AwesomeMoneyEvent {
private String message;
public AwesomeMoneyEvent(String message){
this.message = message;
}
@Override
public String toString() {
return "AwesomeMoneyEvent{" +
"message='" + message + '\'' +
'}';
}
}
@Test
public void 科四() {
AwesomeEventBusDriver.register(new AwesomeStudent_1());
AwesomeEventBusDriver.register(new AwesomeStudent_2());
AwesomeEventBusDriver.register(new AwesomeCoach());
AwesomeEventBusDriver.publishAnything(100);
AwesomeEventBusDriver.publishAnything(90.5);
AwesomeEventBusDriver.publishAnything(new AwesomeMessageEvent("教练通融下"));
AwesomeEventBusDriver.publishAnything(new AwesomeMoneyEvent("教练这是点小意思~"));
}
运行后会发现coach会接收到测试中最后两个自定义的消息类, 这就是DeadEvent的作用, 它的意思就是接收没有订阅者订阅的消息.
好了, 驾照拿到, 我们来总结下
1 消息总线EventBus有点像MQ.
2 EventBus使用非常简单, 我们只需要提供三部分: EventBus类, 订阅者(@Subscribe注解方法), 消息类(如果需要监听的话). 然后通过EventBus注册订阅者, 并发送消息给订阅者.
3 消息路由依据消息类型, 如果有多个订阅者就会发送多份消息. 如果发送的消息没有对应的订阅者, 则该消息会包含在DeadEvent中, 我们订阅该类消息, 专门处理没有人处理的消息. 如果有订阅者订阅的是Object类消息, 也就不会存在DeadEvent消息了.
上路
拿到驾照马上上路搞起~
public class AwesomeAsyncSubscriber {
// 我要上内存的车
@Subscribe
public void cpu(Integer money) {
System.out.println("cpu :" + money);
}
// 我要上笔记本的车
@Subscribe
public void laptop(Long money) throws InterruptedException {
int count = 3;
while (count > 0) {
count--;
Thread.sleep(1000);
}
System.out.println("laptop :" + money);
}
// 我要上不能说的车
@Subscribe
public void poxn(String seed) throws InterruptedException {
System.out.println("poxn " + seed);
System.out.println("downloading ...0%");
int count = 5;
while (count > 0) {
count--;
Thread.sleep(1000);
}
System.out.println("99%...100%");
}
}
开车~
@Test
public void 同步开车() {
AwesomeEventBusDriver.register(new AwesomeAsyncSubscriber());
AwesomeEventBusDriver.publishAnything("xxxx");
AwesomeEventBusDriver.publishAnything(2990);
AwesomeEventBusDriver.publishAnything(200L);
}
运行后, 敏锐的你肯定发现了…种子不好下啊~ 不是网速不行就是资源不行, 下不下来后面的同志就没办法接收到消息. 这就是EventBus的同步模式. 我们应该尽量避免在同步模式中使用耗时操作, 比如数据库操作, 网络请求等, 或者我们可以在接收到消息后异步执行耗时操作, 再或者我们可以直接异步接收消息.
修改下老司机
public class AwesomeEventBusDriver {
// 增加异步总线; 开两个线程
private static EventBus asyncEventBus = new AsyncEventBus(Executors.newFixedThreadPool(2));
private static EventBus eventBus = new EventBus();
// 注册到异步总线
public static void registerAsync(Object object) {
asyncEventBus.register(object);
}
// 发送消息到异步总线
public static void publishAsyncAnything(Object object) {
asyncEventBus.post(object);
}
public static void register(Object object) {
eventBus.register(object);
}
public static void publishAnything(Object object) {
eventBus.post(object);
}
}
异步发车
@Test
public void 异步开车() throws InterruptedException {
AwesomeEventBusDriver.registerAsync(new AwesomeAsyncSubscriber());
AwesomeEventBusDriver.publishAsyncAnything("xxxx");
AwesomeEventBusDriver.publishAsyncAnything(200L);
AwesomeEventBusDriver.publishAsyncAnything(2990);
int i = 0;
while (AwesomeAsyncSubscriber.running) {
Thread.sleep(1000);
i++;
System.out.println(i + "s");
}
}
上路总结
1 订阅者接收消息的顺序是和消息发送的顺序一致的(只是表现是这样的, 下一篇从源码中一探究竟)
2 同步发送时, 只有当订阅者处理完消息后才会发送下一个消息
3 异步发送时, 同时发送消息数量取决于定义EventBus时指定的线程数.
总结
以上通过几个简单的例子, 讲了下EventBus的简单使用, 当然学习最好的办法还是理论加实践, 后面会在项目中加入EventBus, 用来处理关键节点的日志表记录(就算用错了, 就是少几条日志, 还能接受~). 为了更好的使用, 下一篇会从阅读关键源码, 了解代码执行逻辑. 今天就这样, 下次带你上秋名山.