当前位置:网站首页 > 技术博客 > 正文

eventbus怎么用



EventBus简介

EventBus是一个开源库,由GreenRobot开发而来,是用于Android开发的 “事件发布订阅总线”, 用来进行模块间通信、解藕。它可以使用很少的代码,来实现多组件之间的通信。
Android系统内置的事件通讯存在缺点:
Android系统中的事件通信则是 handler (消息机制) 和 BroadCastReceiver (广播机制), 通过它们可以实现组件之间的事件通讯。缺点在于,代码量多、组件之易产生藕合引用。

EventBus产生的背景

当我们进行项目开发的时候,经常会遇到组件与组件之间、组件与后台线程之间的通信, 比如:子线程中执行数据请求,数据请求成功后,通过 Handler 或者 BroadCast 来通知UI更新。 两个Fragment之间可以通过Listener进行通信,但是问题来了,当程序越来越大时,就会要写很多的代码, 而且导致代码严重的耦合问题。为此 ,EventBus 应运而生。

EventBus工作流程图解

Publisher使用post发出一个Event事件,Subscriber在onEvent()函数中接收事件。

EventBus 是一款在 Android 开发中使用的发布/订阅事件总线框架,基于观察者模式,将事件的接收者和发送者分开,简化了组件之间的通信,使用简单、效率高、体积小!下边是官方的 EventBus 原理图:

     

EventBus的优势

1,简化组件之间的通讯方式
2,对通信双方进行解藕
3,使用ThreadMode灵活切换工作线程
4,速度快、性能好
5,库比较小,不占内存

EventBus缺点

1、使用的时候有定义很多event类
2、event在注册的时候会调用反射去遍历注册对象的方法在其中找出带有@subscriber标签的方法,性能不高。
3、需要自己注册和反注册,如果忘了反注册就会导致内存泄漏

EventBus环境配置

1,依赖导入
在app module的builde.gradle文件中导入依赖库:
 
  
2,配置混淆
必须配置,否则会出现,debug环境正常,release环境接收不到事件的问题
 
  

EventBus的使用

EventBus事件三部曲:Subscriber、Event、Publisher。
Subscriber   —— EventBus的register方法,会接收到一个Object对象。
Event           —— EventBus的post()方法中传入的事件类型 (可以是任意类型)。
Publisher     —— EventBus的post()方法。
1,创建一个事件类
 
     
2,在需要订阅事件的模块中,注册EventBus     
注意事项:
1,该方法有且仅有一个参数
2,必须用public修饰,不能使用static或者abstract
3,需要添加@Subscribe()注解
 
    
3,创建订阅者发起通知
使用eventbus.post(eventMessage) 或者 eventbus.postSticky(eventMessage)来发起事件
 
  

Subscribe注解介绍

Subscribe是EventBus自定义的注解,共有三个参数(可选):threadMode、boolean sticky、int priority。 完整的写法如下
 
 
    

1、ThreadMode 模式

用来设置onReceiveMsg()方法,将在哪种线程环境下被调用,共有五种模式:
1.1 POSTING: 默认模式
表示发送事件 post() 发生在哪个线程,接收事件 onReceiveMsg() 就发生在哪个线程环境中。
使用场景:
这种模式不需要线程切换的一些判断逻辑, 直接分发至相同的线程环境,速度快、耗时少。
 
    

1.2  MAIN / MAIN_ODERED: 主线程接收事件

表示无论事件在什么线程环境发布 post(),事件的接收总是在主线程环境执行。
二者之间的区别:
1.2.1 对于MAIN模式而言:
如果post事件也在主线程环境,就会阻塞post事件所在的线程环境,通俗点讲,就是在连续的多个post事件的情况下,只有在接收事件的方法执行完,才会执行下一个post事件。
如果post事件不在主线程环境,并且在主线程接收事件中存在耗时操作的话,属于是非阻塞的。
1.2.2 对于MAIN_ORDERED模式而言,无论post事件在哪种线程环境,它的执行流程都是非阻塞的。
1.3  BACKGROUND:
不管post事件发生在那个线程环境, 事件接收始终在一个子线程中执行。
1.4  ASYNC:
该模式表示,不管post事件处于哪种线程环境,事件接收处理总是在子线程。
           
2、sticky黏性
sticky是一个boolean类型,默认值为false,默认不开启黏性sticky特性,那么什么是sticky特性呢?
上面的例子都是对订阅者 (接收事件) 先进行注册,然后在进行post事件。那么sticky的作用就是:订阅者可以先不进行注册,如果post事件已经发出,再注册订阅者,同样可以接收到事件,并进行处理
其实就是在sticky场景下,EventBus对事件进行了保存而已。
   
 
     

3、priority

priority是优先级,是一个int类型,默认值为0。值越大,优先级越高,越优先接收到事件。
值得注意的是,只有在post事件和事件接收处理,处于同一个线程环境的时候,才有意义。

清晰讲解:EventBus核心原理其实保存这三张图就可以弄懂了,收藏一下 - 知乎

本文主要是从 EventBus 使用的方式入手,来分析 EventBus 背后的实现原理,以下内容基于eventbus:3.1.1版本,主要包括如下几个方面的内容:

  • Subscribe注解
  • 注册事件订阅方法
  • 取消注册
  • 发送事件
  • 事件处理
  • 粘性事件
  • Subscriber Index
  • 核心流程梳理

一、Subscribe注解

EventBus3.0 开始用注解配置事件订阅方法,不再使用方法名了,例如:

 
  

其中事件类型可以是 Java 中已有的类型或者我们自定义的类型。具体看下注解的实现:

 
  

所以在使用注解时可以根据需求指定、、三个属性。

其中属性有如下几个可选值:

  • ThreadMode.POSTING,默认的线程模式,在那个线程发送事件就在对应线程处理事件,避免了线程切换,效率高。
  • ThreadMode.MAIN,如在主线程(UI线程)发送事件,则直接在主线程处理事件;如果在子线程发送事件,则先将事件入队列,然后通过 Handler 切换到主线程,依次处理事件。
  • ThreadMode.MAIN_ORDERED,无论在那个线程发送事件,都先将事件入队列,然后通过 Handler 切换到主线程,依次处理事件。
  • ThreadMode.BACKGROUND,如果在主线程发送事件,则先将事件入队列,然后通过线程池依次处理事件;如果在子线程发送事件,则直接在发送事件的线程处理事件。
  • ThreadMode.ASYNC,无论在那个线程发送事件,都将事件入队列,然后通过线程池处理。

二、注册事件订阅方法

注册事件的方式如下:

 
  

其中是一个单例方法,保证当前只有一个实例:

 
  

继续看做了些什么:

 
  

在这里又调用了的另一个构造函数来完成它相关属性的初始化:

 
  

就是一个默认的EventBusBuilder:

 
  

如果有需要的话,我们也可以通过配置来更改的属性,例如用如下方式注册事件:

 
  

有了的实例就可以进行注册了:

 
  

可以看到方法主要分为查找和注册两部分,首先来看查找的过程,从开始:

 
  

流程很清晰,即先从缓存中查找,如果找到则直接返回,否则去做下一步的查找过程,然后缓存查找到的集合,根据上边的注释可知方法会被调用:

 
  

方法会在当前要注册的类以及其父类中查找订阅事件的方法,这里出现了一个类,它是的内部类,用来辅助查找订阅事件的方法,具体的查找过程在方法,它主要通过反射查找订阅事件的方法:

 
  

到此方法中流程就分析完了,我们已经找到了当前注册类及其父类中订阅事件的方法的集合。接下来分析具体的注册流程,即中的方法:

 
  

这就是注册的核心流程,所以方法主要是得到了、两个 HashMap。我们在发送事件的时候要用到,完成事件的处理。当取消 EventBus 注册的时候要用到、,完成相关资源的释放。

三、取消注册

接下来看,EventBus 如何取消注册:

 
  

核心的方法就是:

 
  

内容很简单,继续看方法:

 
  

所以在方法中,释放了、中缓存的资源。

四、发送事件

当发送一个事件的时候,我们可以通过如下方式:

 
  

可以看到,发送事件就是通过方法完成的:

 
  

所以方法先将发送的事件保存的事件队列,然后通过循环出队列,将事件交给方法处理:

 
  

方法中,根据属性,决定是否向上遍历事件的父类型,然后用方法进一步处理事件:

 
  

方法核心就是遍历发送的事件类型对应的Subscription集合,然后调用方法处理事件。

五、处理事件

接着上边的继续分析,内部会根据订阅事件方法的线程模式,间接或直接的以发送的事件为参数,通过反射执行订阅事件的方法。

 
  
 
  

另外一种是先将事件入队列(其实底层是一个List),然后做进一步处理,我们以为例简单的分析下,其中是类的一个实例,来看该类的主要实现:

 
  

所以的方法主要就是将、对象封装成一个对象,然后保存到队列里,之后通过切换到主线程,在方法将中将对象循环出队列,交给方法进一步处理:

 
  

这个方法很简单,主要就是从中取出之前保存的、,然后用反射来执行订阅事件的方法,又回到了第一种处理方式。所以的核心就是先将将事件入队列,然后通过Handler从子线程切换到主线程中去处理事件。

和也类似,内部都是先将事件入队列,然后再出队列,但是会通过线程池去进一步处理事件。

六、粘性事件

一般情况,我们使用 EventBus 都是准备好订阅事件的方法,然后注册事件,最后在发送事件,即要先有事件的接收者。但粘性事件却恰恰相反,我们可以先发送事件,后续再准备订阅事件的方法、注册事件。

由于这种差异,我们分析粘性事件原理时,先从事件发送开始,发送一个粘性事件通过如下方式:

 
  

来看方法是如何实现的:

 
  

方法主要做了两件事,先将事件类型和对应事件保存到中,方便后续使用;然后执行继续发送事件,这个方法就是之前发送的方法。所以,如果在发送粘性事件前,已经有了对应类型事件的订阅者,及时它是非粘性的,依然可以接收到发送出的粘性事件。

发送完粘性事件后,再准备订阅粘性事件的方法,并完成注册。核心的注册事件流程还是我们之前的方法中的方法,前边分析方法时,有一段没有分析的代码,就是用来处理粘性事件的:

 
  

可以看到,处理粘性事件就是在 EventBus 注册时,遍历,如果当前要注册的事件订阅方法是粘性的,并且该方法接收的事件类型和中某个事件类型相同或者是其父类,则取出中对应事件类型的具体事件,做进一步处理。继续看处理方法:

 
  

最终还是通过方法完成粘性事件的处理,这就是粘性事件的整个处理流程。

七、Subscriber Index

回顾之前分析的 EventBus 注册事件流程,主要是在项目运行时通过反射来查找订事件的方法信息,这也是默认的实现,如果项目中有大量的订阅事件的方法,必然会对项目运行时的性能产生影响。其实除了在项目运行时通过反射查找订阅事件的方法信息,EventBus 还提供了在项目编译时通过注解处理器查找订阅事件方法信息的方式,生成一个辅助的索引类来保存这些信息,这个索引类就是Subscriber Index,其实和 ButterKnife 的原理类似。

要在项目编译时查找订阅事件的方法信息,首先要在 app 的 build.gradle 中加入如下配置:

 
  

然后在项目的 Application 中添加如下配置,以生成一个默认的 EventBus 单例:

 
  

之后的用法就和我们平时使用 EventBus 一样了。当项目编译时会在生成对应的类:


对应的源码如下:

 
  

其中是一个HashMap,保存了当前注册类的 Class 类型和其中事件订阅方法的信息。接下来分析下使用 Subscriber Index 时 EventBus 的注册流程,我们先分析:

 
  

首先创建一个,然后通过方法添加索引类的实例:

 
  

即把生成的索引类的实例保存在集合中,然后用创建默认的 EventBus实例:

 
  
 
  

即用当前对象创建一个 EventBus 实例,这样我们通过配置的 Subscriber Index 也就传递到了EventBus实例中,然后赋值给EventBus的 成员变量。之前我们在分析 EventBus 的方法时已经见到了:

 
  
 
  

由于我们现在使用了 Subscriber Index 所以不会通过来反射解析订阅事件的方法。我们重点来看都做了些什么:

 
  

就是在前边方法中创建的,保存了项目中的索引类实例,即的实例,继续看索引类的方法,来到了类中:

 
  

即根据注册类的 Class 类型从 查找对应的,如果我们在注册类中定义了订阅事件的方法,则 不为空,进而上边方法中成立,到这里主要的内容就分析完了,其它的和之前的注册流程一样。

所以 Subscriber Index 的核心就是项目编译时使用注解处理器生成保存事件订阅方法信息的索引类,然后项目运行时将索引类实例设置到 EventBus 中,这样当注册 EventBus 时,从索引类取出当前注册类对应的事件订阅方法信息,以完成最终的注册,避免了运行时反射处理的过程,所以在性能上会有质的提高。项目中可以根据实际的需求决定是否使用 Subscriber Index。

八、小结

结合上边的分析,我们可以总结出、、的核心流程:

                                                                       register

                                                                           post

                                                           unregister

到这里 EventBus 几个重要的流程就分析完了,整体的设计思路还是值得我们学习的。和 Android 自带的广播相比,使用简单、同时又兼顾了性能。但如果在项目中滥用的话,各种逻辑交叉在一起,也可能会给后期的维护带来困难。

版权声明


相关文章:

  • 数字图像基本处理实验2025-06-22 11:29:59
  • ds3231各型号区别2025-06-22 11:29:59
  • 一句话木马get2025-06-22 11:29:59
  • ipad做电脑第二屏幕windows2025-06-22 11:29:59
  • 指针数组的指针2025-06-22 11:29:59
  • 大小端的概念2025-06-22 11:29:59
  • 流量监控是干嘛的2025-06-22 11:29:59
  • 装饰模式详解2025-06-22 11:29:59
  • ajax请求的五个步骤代码2025-06-22 11:29:59
  • pymysql sqlalchemy2025-06-22 11:29:59