背景
参考文章
概述
- EventBus是一个发布/订阅的事件总线简化了应用程序各个组件间(Activity、service、Fragment等)组件和后台线程间的通信。EventBus主要角色如下:
- Event 传递的事件对象。
- Subscriber 事件的订阅者。
- Publisher 事件的发布者。
- ThreadMode 定义函数在何种线程中执行。
- 订阅者订阅事件到总线,发送者发布事件,官方给出的它们的关系图如下:
使用EventBus
build.gradle添加引用
- github里面可以找到最新的版本,现在是3.0
|
|
定义一个事件类型
- 这个事件类型实体类,可以自己定义:
|
|
订阅/取消订阅事件
- 订阅事件进行三步设置,注册、取消注册和定义事件接受函数,
|
|
- 在界面销毁时取消订阅
|
|
- 定义事件接收方法,方法的名字自己定义,使用注解@Subscribe()表示
|
|
- 使用@Subscribe()注解表明这个方法是EventBus事件接收的方法。
- 事件接收方法的参数必须和EventBus发送的事件类是同一类型,比如例子中是EventBusMsg。
- 在@Subscribe()中可以设置threadMode来设置不同的模式,ThreadMode.Main表示运行在主线程。
发布事件
- 比较简单,调用EventBus的post方法即可
|
|
- 上面的流程是要先订阅事件,再发布事件才能接收到事件。
EventBus 事件回调方法线程(Delivery Thread)
- EventBus会处理事件接收线程的问题,比如:在线程A中发布事件,事件传递到线程B中执行,这个功能主要用在处理更新UI的问题上。EventBus可以帮助我们处理UI和后台任务的同步问题,EventBus有四种线程模式,可以通过@Subscribe()朱姐的threadMode参数指定:
ThreadMode.POSTING
- POSTING是默认参数,即接收事件的方法和发布消息的方法运行在同一个线程中。这种模式优点是:开销最小,不需要切换线程。
|
|
ThreadMode:MAIN
- 事件接收方会运行在UI线程,不过这里注意:如果发布事件的线程是主线程,那么事件就会直接发布给订阅者,不会进行线程切换,也相当于使用了POSTING模式。另外,在这个模式下不要在事件接收方法中进行耗时操作,防止UI线程阻塞:
|
|
ThreadMode:BACKGROUND
事件接收方法将会运行在一个后台线程中,但是事件不可以并发执行:
- 如果发布事件本身就在后台线程中,那么事件接收方就运行在这个线程中,不再另外开启新的线程。
- 如果发布事件在主线程中,那么EventBus会开一个后台线程(只有一个)然后在该线程中依次处理所有事件。
不过要注意:事件处理方法应当尽快返回,避免阻塞后台线程。
|
|
ThreadMode:ASYNC
- 在ASYNC模式下事件接收方总是运行在一个发布事件线程和主线程之外独立的线程中,发布事件不必依赖订阅事件处理结果,因此此模式适合于在事件处理中有比较耗时的操作的情况,为了避免同一时间有过多的运行线程,EventBus使用线程池和事件处理完成通知来限制最大的线程并发数
|
|
EventBus 高级一点的用法:粘性事件(Stick Events)
- 在上面提到的基本使用中我们提到的模式都是必须先订阅事件然后再发布事件,不过EventBus还有另一种用法:粘性事件,这种模式允许先发布事件再订阅事件,EventBus会将粘性事件保存到内存中然后发送给订阅者,订阅者也可以主动查询粘性事件。
定义事件实体类
- 和上面定义事件是一样的:
|
|
发布粘性事件
- 发布粘性事件使用postSticky()方法:
|
|
订阅粘性事件
- 订阅粘性事件和订阅普通事件是一样的,都使用EventBus的注册,取消注册及事件接收函数:
注册
- 注意不能重复注册,可能造成程序崩溃1234public void getStickyMsg(View view) {// 注册,注意不要进行多次注册,否则程序可能崩溃.可以设置一个标志.EventBus.getDefault().register(EventBusSendActivity.this);}
取消注册
|
|
定义事件接收方法
- 粘性事件接收方法需要在@Subscribe()注解中添加sticky=true参数。
|
|
手动获取粘性事件
|
|
- removeStickyEvent 返回的是被删除的事件。我们获取了该粘性事件后,EventBus并不会主动删除粘性事件。(上面的订阅方式也不会删除)。
使用EventBusAnnotationProcessor
- 在EventBus 3中引入了EventBusAnnotationProcessor(注解分析生成索引)技术,大大提高了EventBus的运行效率。
gradle配置
- 除了上面导入org.greenrobot:eventbus:3.0.0之外,还需要额外导入一些内容,在项目gradle的dependencies中引入apt编译插件:
|
|
- 在App的build.gradle中应用apt插件,并设置apt生成的索引的包名和类名:
|
|
初始化EventBus
|
|
其他
- 其他方面和不使用索引加速都是一样的。
源码分析
EventBus.getDefault()
|
|
- 上面的源码看出,EventBus.getDefault()代码,其实是获取了EventBus类的单例,若该单例未实例化,那么会根据DEFAULE_BUILDER采用构造者模式去实例化该单例,在EventBus构造器中初始化了一堆成员变量,接下来的操作中会用到。
register(Object subscriber)
- 事件订阅者要调用register(Object subscriber)方法来进行注册:
|
|
- 而 SubscriberMethod 其实就是订阅方法的包装类:
|
|
- 为了性能,首先会去缓存中查找是否存在类对应的SubscriberMethod list,如果存在就直接返回了。
- 接下来会判断ignoreGeneratedIndex,这个就是上面提到的索引加速,如果使用索引加速,ignoreGeneratedIndex就是false。否则是true。不过默认是false,也就是默认会去查找索引生成的文件。接下来我们直接看findUserInfo的代码:
|
|
- 接下来再看subscribe(subscriber, subscriberMethod) 方法:
|
|
- register的流程图如下:
post(object event)
- 接下来看post方法的执行逻辑:
|
|
- currentPostingThreadState.get()获得是当前线程的PostingThreadState对象。其实currentPostingThreadState是一个ThreadLocal,ThreadLocal是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,而这数据时不会与其他线程共享的。其实现原理是:在每个thread中会有一个threadLocals,它其实是一个ThreadLocal.ThreadLocalMap,Map中的key是ThreadLocal,value就是我们存储的变量副本。
- 接下来看看postSingleEvent都做了什么:
|
|
- 接下来看postSingleEventForEventType的代码
|
|
- post过程的流程图如下:
unregister
|
|
- 接下来再看unsubscribeByEventType方法
|
|
- 这个方法很简单,就是从subscriptionsByEventType列表中将对应eventType的Subscription移除。
总结
- EventBus是非常好用的一款消息订阅/发布开源库,可以帮助我们省去很多像Handler+thread这类的代码,同时在EventBus3.0中,使用注解的方式取代之前的onEvent开头的方法来写事件接收方法,使代码更加的清晰。
- 另外从设计上来说,可以将事件发送者和接收者解耦。