前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Guava中的异步事件处理方案很优雅!

Guava中的异步事件处理方案很优雅!

作者头像
Bug开发工程师
发布于 2020-02-19 13:37:55
发布于 2020-02-19 13:37:55
3K00
代码可运行
举报
文章被收录于专栏:码农沉思录码农沉思录
运行总次数:0
代码可运行

点击上方“码农沉思录”,选择“设为星标”

优质文章,及时送达

简述

EventBus是Guava的事件处理机制,是设计模式中的观察者模式(生产/消费者编程模型)的优雅实现,在应用中可以处理一些异步任务。对于事件监听和发布订阅模式,EventBus是一个非常优雅和简单解决方案,我们不用创建复杂的类和接口层次结构。

EventBus实际上是一个消息队列,Event Source发送一个消息到EventBus,然后再由EventBus将消息推送到所监听的Listener。

EventBus基本用法

1. 创建Listener

我们可以通过@Subscribe注解将任意的类的方法变为一个Listener。

代码语言:javascript
代码运行次数:0
运行
复制
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class SimpleListener {
    private final static Logger LOGGER = LoggerFactory.getLogger(SimpleListener.class);

    /**
     * 一个简单的Listener方法
     * @param event Guava规定此处只能有一个参数
     */
    @Subscribe
    public void doAction(final String event){
        if (LOGGER.isInfoEnabled()){
            LOGGER.info("Received event [{}] and will take a action", event);
        }
    }
}
代码语言:javascript
代码运行次数:0
运行
复制

2. 创建EventBus并发送消息

然后我们可以定义一个EventBus,先将上面的这个Listener类进行注册,通过Post方法即可向其发送消息。

代码语言:javascript
代码运行次数:0
运行
复制
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class SimpleEventBusExample {
    public static void main(String[] args) {
        final EventBus eventBus = new EventBus();
        //注册Listener
        eventBus.register(new SimpleListener());
        System.out.println("post the simple event.");
        //向订阅者发送消息
        eventBus.post("Simple Event");
    }
}
代码语言:javascript
代码运行次数:0
运行
复制

Listener之间的继承关系

本小节我们来测试一下,当两个Listener之间有继承关系时,只注册子类的Listener到EventBus。然后发送向EventBus发送消息,父类Listener是否会接收到消息呢?

首先定义一个抽象类的Listener。

代码语言:javascript
代码运行次数:0
运行
复制
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public abstract class AbstractListener {
    private final static Logger LOGGER = LoggerFactory.getLogger(AbstractListener.class);

    @Subscribe
    public void commonTask(final String event){
        if (LOGGER.isInfoEnabled()){
            LOGGER.info("Received event [{}] will be handle by {}.{}", new Object[]{event,this.getClass().getSimpleName(),"commonTask"});
        }
    }
}
代码语言:javascript
代码运行次数:0
运行
复制

然后定义两个实现类BaseListener和ConcreteListener。

代码语言:javascript
代码运行次数:0
运行
复制
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class BaseListener extends AbstractListener{
    private final static Logger LOGGER = LoggerFactory.getLogger(AbstractListener.class);

    @Subscribe
    public void baseTask(final String event){
        if (LOGGER.isInfoEnabled()){
            LOGGER.info("Received event [{}] will be handle by {}.{}", new Object[]{event,this.getClass().getSimpleName(),"baseTask"});
        }
    }
}

public class ConcreteListener extends BaseListener {

    private final static Logger LOGGER = LoggerFactory.getLogger(AbstractListener.class);

    @Subscribe
    public void conTask(final String event){
        if (LOGGER.isInfoEnabled()){
            LOGGER.info("Received event [{}] will be handle by {}.{}", new Object[]{event,this.getClass().getSimpleName(),"conTask"});
        }
    }
}
代码语言:javascript
代码运行次数:0
运行
复制

定义EventBus:

代码语言:javascript
代码运行次数:0
运行
复制
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class InheritListenersEventBusExample {
    public static void main(String[] args) {
        final EventBus eventBus = new EventBus();
        eventBus.register(new ConcreteListener());
        System.out.println("post the string event.");
        eventBus.post("I am String event");
        System.out.println("post the int event.");
        eventBus.post(1000);
    }
}
代码语言:javascript
代码运行次数:0
运行
复制

结论:注册了一个Listener,使用eventBus发送消息它的父类的Subscribe也会对此消息进行处理。

Subscriber

1. 不同类型参数的Subscribe

本小节我们来测试一下,向EventBus发送消息后,当有多个不同类型的Subscribe时,它们是怎么进行通信的呢?

定义Subscribe

代码语言:javascript
代码运行次数:0
运行
复制
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class MultipleEventListeners {
    private final static Logger LOGGER = LoggerFactory.getLogger(MultipleEventListeners.class);

    @Subscribe
    public void task1(final String event){
        if (LOGGER.isInfoEnabled()){
            LOGGER.info("Received event [{}] and will take a action by ==task1==", event);
        }
    }

    @Subscribe
    public void task2(final String event){
        if (LOGGER.isInfoEnabled()){
            LOGGER.info("Received event [{}] and will take a action by ==task2==", event);
        }
    }

    @Subscribe
    public void intTask(final Integer event){
        if (LOGGER.isInfoEnabled()){
            LOGGER.info("Received event [{}] and will take a action by ==intTask==", event);
        }
    }
}
代码语言:javascript
代码运行次数:0
运行
复制

定义EventBus:

代码语言:javascript
代码运行次数:0
运行
复制
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class MultipleEventBusExample {
    public static void main(String[] args) {
        final EventBus eventBus = new EventBus();
        eventBus.register(new MultipleEventListeners());
        System.out.println("post the string event.");
        eventBus.post("I am String event");
        System.out.println("post the int event.");
        eventBus.post(1000);
    }
}
代码语言:javascript
代码运行次数:0
运行
复制

结果:

结论:eventBus会根据Listener的参数类型的不同,分别向不同的Subscribe发送不同的消息。

结论:eventBus会根据Listener的参数类型的不同,分别向不同的Subscribe发送不同的消息。

event

1. 继承关系的event

代码语言:javascript
代码运行次数:0
运行
复制
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class Apple extends Fruit {

    public Apple(String name) {
        super(name);
    }
}
public class Fruit {
    private final String name;

    public Fruit(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    @Override public String toString() {
        return "Fruit{" +
            "name='" + name + '\'' +
            '}';
    }
}
代码语言:javascript
代码运行次数:0
运行
复制

定义对应的Listener:

代码语言:javascript
代码运行次数:0
运行
复制
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class FruitEaterListener {
    private final static Logger LOGGER = LoggerFactory.getLogger(FruitEaterListener.class);

    @Subscribe
    public void eat(Fruit event){
        if (LOGGER.isInfoEnabled()){
            LOGGER.info("Fruit eat[{}]. ", event);
        }
    }
    @Subscribe
    public void eat(Apple event){
        if (LOGGER.isInfoEnabled()){
            LOGGER.info("Apple eat[{}]. ", event);
        }
    }
}
代码语言:javascript
代码运行次数:0
运行
复制

定义EventBus:

代码语言:javascript
代码运行次数:0
运行
复制
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class InheritEventsBusExample {
    public static void main(String[] args) {
        final EventBus eventBus = new EventBus();
        eventBus.register(new FruitEaterListener());
        eventBus.post(new Apple("apple"));

        System.out.println("---------------------");
        eventBus.post(new Fruit("Fruit"));
    }
}
代码语言:javascript
代码运行次数:0
运行
复制

结论:当作为参数的event之间有继承关系时,使用eventBus发送消息,eventt的父类listener也会对此消息进行处理。

DeadEvent

当EventBus发布了一个事件,但是注册的订阅者中没有找到处理该事件的方法,那么EventBus就会把该事件包装成一个DeadEvent事件来重新发布;我们在应用中可以提供如下的事件处理方法来处理DeadEvent。

创建listener:

代码语言:javascript
代码运行次数:0
运行
复制
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class DeadEventListener {
    @Subscribe
    public void handle(DeadEvent event){
        //获取事件源
        System.out.println(event.getSource());//DEAD-EVENT-BUS
        //获取事件
        System.out.println(event.getEvent());//DeadEventListener event
    }
}
代码语言:javascript
代码运行次数:0
运行
复制

创建EventBus:

代码语言:javascript
代码运行次数:0
运行
复制
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class DeadEventBusExample {
    public static void main(String[] args) {
        //重写EventBus的toString方法,使eventBus的名称为DEAD-EVENT-BUS
        final EventBus eventBus = new EventBus(){
            @Override public String toString() {
                return "DEAD-EVENT-BUS";
            }
        };
        DeadEventListener deadEventListener = new DeadEventListener();
        eventBus.register(deadEventListener);
        eventBus.post("DeadEventListener event");
        eventBus.post("DeadEventListener event");

    }
}
代码语言:javascript
代码运行次数:0
运行
复制

EventBus之异常处理

在默认情况下,EventBus不会对异常信息进行处理,异常信息也不会终止EventBus的运行,只会简单的打印出异常堆栈信息。

定义一个Listener:

代码语言:javascript
代码运行次数:0
运行
复制
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class ExceptionListener {

    private final static Logger LOGGER = LoggerFactory.getLogger(ExceptionListener.class);

    @Subscribe
    public void m1(final String event){
        if (LOGGER.isInfoEnabled()){
            LOGGER.info("Received event [{}] and will take m1", event);
        }
    }
    @Subscribe
    public void m2(final String event){
        if (LOGGER.isInfoEnabled()){
            LOGGER.info("Received event [{}] and will take m2", event);
        }
    }
    @Subscribe
    public void m3(final String event){
        throw new RuntimeException();
    }
}
代码语言:javascript
代码运行次数:0
运行
复制

定义一个EventBus:

代码语言:javascript
代码运行次数:0
运行
复制
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class ExceptionEventBusExample {
    public static void main(String[] args) {
        //在默认情况下,EventBus不会对异常信息进行处理,异常信息也不会终止EventBus的运行,只会简单的打印出异常堆栈信息。
        //在EventBus构造函数中传入SubscriberExceptionHandler来对异常信息进行处理
        //下面是通过lambda表达式来实现SubscriberExceptionHandler接口
        final EventBus eventBus = new EventBus((exception,context) -> {
            System.out.println(context.getEvent());//Exception event
            System.out.println(context.getEventBus());//defalut
            System.out.println(context.getSubscriber());//ExceptionListener
            System.out.println(context.getSubscriberMethod());//m3
        });
        eventBus.register(new ExceptionListener());
        eventBus.post("Exception event");
    }
}
代码语言:javascript
代码运行次数:0
运行
复制

结论:在默认情况下,EventBus不会对异常信息进行处理,异常信息也不会终止EventBus的运行,只会简单的打印出异常堆栈信息。可以在EventBus构造函数中传入一个SubscriberExceptionHandler对象来对异常信息进行处理。上述代码是通过lambda表达式来实现了一个SubscriberExceptionHandler接口。

祝大家在2020年工作顺路,家庭幸福,合家团圆

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-02-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码农沉思录 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
汇编语言之ARM32汇编
以上两种编译环境,使用的指令集都是一致的, 只是语法格式有不同,也就是宏指令,伪指令,伪操作不一样
乱码三千
2021/08/24
3.3K0
汇编语言之ARM32汇编
400 行 C 代码实现一个虚拟机
1. 引言 本文将教你编写一个自己的虚拟机(VM),这个虚拟机能够运行汇编语言编写的程序, 例如我朋友编写的 2048 或者我自己的 Roguelike。如果你会编程,但希望 更深入地了解计算机的内部原理以及编程语言是如何工作的,那本文很适合你。从零开始 写一个虚拟机听起来可能让人有点望而生畏,但读完本文之后你会惊讶于这件事原来如此简 单,并从中深受启发。 本文所说的虚拟机最终由 400 行左右 C 代码组成。理解这些代码只需要基本的 C/C++ 知识和二进制运算。这个虚拟机可以在 Unix 系统(包括 m
IT大咖说
2022/03/04
9810
赶紧收藏!u-boot代码分析与移植
BootLoader的目标是正确调用内核的执行,由于大部分的BootLoader都依赖于CPU的体系结构。因此大部分的BootLoader都分为两个步骤启动。依赖于CPU体系结构(如设备初始化等)的代码都放在stage1。而stage2一般使用C语言实现,能够实现更加复杂的功能,代码的可移植性也提高。
混说Linux
2022/07/14
7970
赶紧收藏!u-boot代码分析与移植
UCOSII系统移植详解「建议收藏」
1,处理器的C编译器能产生可重入型的代码,如果不行的话,那么就不能在任务之间随意的切换,因为当你切换到别的任务的时候,该任务在这个函数的数据就会被破坏。
全栈程序员站长
2022/08/19
2.4K0
eBPF 概述:第 2 部分:机器和字节码
我们在第 1 篇文章中介绍了 eBPF 虚拟机,包括其有意的设计限制以及如何从用户空间进程中进行交互。如果你还没有读过这篇文章,建议你在继续之前读一下,因为没有适当的介绍,直接开始接触机器和字节码的细节是比较困难的。如果有疑问,请看第 1 部分开头的流程图。
用户7686797
2021/11/23
8880
ARM汇编简单学习
ARM汇编语言是针对ARM架构设计的低级编程语言,用于直接操作硬件和编写高效的系统级程序。
cultureSun
2024/03/28
1870
STM32上的backtrace原理与分析
一般来说,1,2,3板子都是在开发者手上,一旦遇到bug,只要可以复现,基本上都可以排查出来,然后修复或者规避。但一旦进入到4,5阶段,产品已经成型之后,再想排查BUG就比较麻烦了。例如工厂测试阶段,有可能连续运行好几天或者好几个星期才能复现的问题,排查起来就十分的复杂。对于这种情况,backtrace是十分必要的。可以在离线的状态下分析系统的关键信息,通过函数的栈回溯,从而找到出错的对应的执行函数,然后结合程序设计,基本上大部分的bug基本上也可以找到。我之前写过一篇文章arm上backtrace的分析与实现原理。分析了在cortex-a上的分析情况。但是对于cortex-m来说,问题就会复杂许多,因为cortex-m对于固件的体积的限制以及特殊的架构,让backtrack的方案占用了过大的flash。这是设计者所不能接受的,而且更加难受的是cortex-m并没有栈回溯指针。这就让栈的深度的计算变的十分复杂。本文主要分析cortex-m的栈布局以及一些栈回溯的底层原理和方案。
bigmagic
2020/10/30
2.8K0
STM32上的backtrace原理与分析
ARM指令集介绍「建议收藏」
ARM 指令集是针对ARM体系架构设计的指令。在BootLoader引导的第一阶段以及内核的第一阶段都会有一个使用汇编语言编写的文件,在不跑操作系统的裸板中也有一段用来初始化开发板环境的汇编代码。所以无论是开发带操作系统的板子,还是裸板开发,汇编语言都很有必要学习一番,最少要了解一些常用的汇编指令。要想设计出性能超强的系统,ARM的工作原理是必须掌握的。
全栈程序员站长
2022/11/15
2.8K0
ARM指令集介绍「建议收藏」
C和汇编如何互相调用?嵌入式工程师必须掌握
内联汇编即在C中直接使用汇编语句进行编程,使程序可以在C程序中实现C语言不能完成的一些工作,例如,在下面几种情况中必须使用内联汇编或嵌入型汇编。
用户1605515
2020/12/17
2K0
linux内核学习(四)之回顾简单的汇编知识(一))
大家周末晚上好,今天给大家分享一些简单的汇编知识;说起汇编,不管是学习或者说工作中,都会或多或少的接触到,比如说学习中,在进入c语言编程世界之前,都会有一段汇编作为引导来进入c的;当然在实际开发当中,现在用汇编来开发的比较少,不是没有;做一为嵌入式软件工程师,我觉得还是非常有必要要掌握一些基本的汇编指令知识的,不要你会写汇编代码,要求自身会分析以.s结尾的文件里面的汇编代码就差不多了,看的懂常规汇编指令就行(这里顺便插一句题外话,我们知道一般ARM都是采用risc架构的,如果有网友对risc-v架构感兴趣的,可以来交流学习),好了,废话就不多说了,开始进入主题啦!
用户6280468
2022/03/21
5680
linux内核学习(四)之回顾简单的汇编知识(一))
【RTOS训练营】晚课学员问题
答: CPU大爷使用不同的地址,访问RAM,GPIO,FLASH。从这个角度看,GPIO、RAM、Flash地位相同。
韦东山
2022/09/08
5910
【RTOS训练营】晚课学员问题
手把手教你从零开始实现C++协程
简介 在上一篇文章 《微信终端自研C++协程框架的设计与实现》 中,我们介绍了异步编程的演化过程和 owl 协程的整体设计思路,因篇幅所限,上文中并没有深入到协程的具体实现细节。用 C++ 实现有栈协程,核心在于实现协程上下文切换,在 owl 协程的整体架构中,owl.context 位于最底层,所有上层 API 全部基于这一层来实现: 本文将详细讲解 C++ 协程上下文切换的底层原理,手把手教你从零开始实现 C++ 协程。 owl.context 接口设计 业界比较有名的上下文切换库有 uconte
微信终端开发团队
2021/12/10
4.3K0
OpenHarmony 内核源码分析(任务切换篇) | 看汇编如何切换任务
您一定注意到了TaskContext,说的全是它,这就是任务上下文结构体,理解它是理解任务切换的钥匙.它不仅在C语言层面出现,而且还在汇编层出现,TaskContext是连接或者说打通 C->汇编->C 实现任务切换的最关键概念.本篇全是围绕着它来展开.先看看它张啥样,LOOK!
小帅聊鸿蒙
2025/03/19
860
arm(2)| 汇编指令和伪指令
指令是CPU机器指令的助记符,经过编译后会得到一串10组成的机器码,可以由CPU读取执行。伪指令本质上不是指令(只是和指令一起写在代码中),它是编译器环境提供的,目的是用来指导编译过程,经过编译后伪指令最终不会生成机器码。所以指令和伪指令最大区别就是编译完之后会不会生成机器码。
飞哥
2020/07/10
2.8K0
arm(2)| 汇编指令和伪指令
OpenHarmony 内核源码分析(寄存器篇) | 宇宙最忙存储器
寄存器从大一的计算机组成原理就开始听到它,感觉很神秘,如梦如雾多年.揭开本质后才发现,寄存器就是一个32位的存储空间,一个int变量而已,但它的厉害之处在于极高频率的使用,让人不敢相信是怎么做到的,不管再复杂再牛牛的应用程序,电商也好,游戏,直播也罢,到了它这里都变成了有限的十几个寄存器在玩,简直太神奇了.
小帅聊鸿蒙
2025/03/18
740
OpenHarmony 内核源码分析(寄存器篇) | 宇宙最忙存储器
STM32F0单片机快速入门三 MCU启动过程
首先我们需要澄清一个问题,什么是 Startup Code,什么是 Bootloader?因为总看到有同学混用这两个概念。
用户2366192
2021/05/31
1.2K0
汇编程序调用c函数为什么需要设置栈?
之前看了很多关于uboot分析类的文章,其中提到为C语言的运行准备栈。而在uboot start.S汇编代码中,关于系统初始化,也看到栈指针初始化,即正确给栈指针sp赋值,却从来没看到有人解释,为何要这样做。接下来,我试图解释这个问题。
韦东山
2020/09/30
1.2K0
安卓逆向:这是一篇逆向基础函数在ARM32中的刨根问底。
通过向程序计数器 PC写入跳转地址值,可以实现在 4GB 的地址空间中的任意跳转,在跳转之前结合使用MOV LR,PC
小道安全
2021/01/18
3.6K1
arm汇编指令详解带实例_汇编buf指令
两个 S 用于不同的指令,名称相同,但是在不同的指令结合却有不同的作用
全栈程序员站长
2022/11/04
1.5K0
嵌入式:什么是ATPCS
ATPCS(ARM-Thumb Produce Call Standard)是ARM程序和Thumb程序中子程序调用的基本规则,目的是为了使单独编译的C语言程序和汇编程序之间能够相互调用。这些基本规则包括子程序调用过程中寄存器的使用规则、数据栈的使用规则和参数的传递规则。
timerring
2023/01/01
1.1K0
相关推荐
汇编语言之ARM32汇编
更多 >
LV.1
修己树人教育科技教研总监
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档