EventBus 3.0使用与解析
这两天学习了下EventBus,这里总结一下。EventBus已经不是什么新鲜的东西了,现在已经更新到3.0版本,下面以3.0为主进行总结。
什么是EventBus
看到EventBus这个单词,不知道大家能联想到什么,从字面意思上来看Event是事件,Bus是公共汽车的意思,那么事件和公共汽车之间有怎么样的联系?我的直观想法是Bus应该是个载体,事件通过Bus这个载体来传递。
我们看下官方解释:
EventBus is an open-source library for Android using the publisher/subscriber pattern to solve the problem of loose coupling. EventBus enables central communication to decoupled classes with just a few lines of code – simplifying the code, removing dependencies, and speeding up app development.
EventBus是一个针对Android采用发布/订阅模式来解决松散耦合关系的开源框架。能够用简单的代码使得组件间通信解耦,减少中间的依赖以及加速应用程序开发。通过官方解释,我们应该知道,EventBus采用的是发布/订阅即我们所说的观察者模式,通过解耦发布者和订阅者来简化Android中的事件传递。
这里的事件并不是事件分发中的事件(action_down,action_up等),而是类似于消息处理机制中的消息,因此EventBus里的事件其实就是一个消息实体对象,这个消息实体对象可以是我们网络返回的数据对象,也可以是组件中用到的某个开关变量等。
EventBus模型图(来自官网):
发布者通过post方法发送事件到Bus这个公共汽车,然后所有的订阅者就能够收到该事件,至于事件的处理就是订阅者自己的事情了。至于这里的过程是怎么样的,后面分析源码就会知道。
如何使用EventBus
1.添加依赖
1
|
compile 'de.greenrobot:eventbus:3.0.0-beta1'
|
2.定义事件对象
1
|
public class MessageEvent {
|
这里定义了一个POJO,里面有一个属性——消息,在实际的应用中可以是任意的实体类对象,比如网络返回的不同的POJO对象。
3.准备订阅者
1
|
|
订阅事件函数用@Subscribe注解标注,括号里是线程模式,这里代表事件订阅者函数在主线程执行
4.注册EventBus
1
|
|
在Activity的onStart()方法里注册EventBus,当然也可以在其他的地方进行注册,这里以Activity举例。
5.发送事件
1
|
EventBus.getDefault().post(new MessageEvent("Hello everyone!"));
|
这句代码可以在你想要执行的任何地方执行,然后所有订阅了MessageEvent事件的订阅者都会收到该事件,前提是订阅者所在的那个类里,必须注册了EventBus。
6.注销EventBus
1
|
|
记得在Activity不可见时,一定要记得注销EventBus。
代码很简单,你可以在任意的地方发送消息,然后Activity里的订阅者会收到该事件并作出处理,这里是弹出了一个Toast。
通过上面的使用步骤,我们发现EventBus很想Broadcast,都需要注册以及注销,类似于广播,我们在需要的地方发送一个广播,广播接收者会接收到这个消息并进行处理。在上面的代码中,我们看到订阅者函数@Subscribe注解的括号里有个threadMode,下面就解释下threadMode。
关于线程模式
线程模式主要是用来指定事件处理函数需要运行的线程,因为事件处理函数需要做的操作是不同的,比如更新UI,那么就需要在主线程,如果做耗时操作,比如网络请求,肯定是需要在子线程中进行,因此这个线程模式非常重要。在EventBus中有四种线程模式:
- MainThread:如果使用事件处理函数指定了线程模型为MainThread,那么不论事件是在哪个线程中发布出来的,该事件处理函数都会在UI线程中执行。该方法可以用来更新UI,但是不能处理耗时操作。
- PostThread:如果使用事件处理函数指定了线程模型为PostThread,那么该事件在哪个线程发布出来的,事件处理函数就会在那个线程中运行,也就是说发布事件和接收事件在同一个线程。在线程模型为PostThread的事件处理函数中尽量避免执行耗时操作,因为它会阻塞事件的传递,甚至有可能会引起ANR。
- BackgroundThread:如果使用事件处理函数指定了线程模型为BackgroundThread,那么如果事件是在UI线程中发布出来的,那么该事件处理函数就会在新的线程中运行,如果事件本来就是子线程中发布出来的,那么该事件处理函数直接在发布事件的线程中执行。在此事件处理函数中禁止进行UI更新操作。
- Async:如果使用事件处理函数指定了线程模型为Async,那么无论事件在哪个线程发布,该事件处理函数都会在新建的子线程中执行。同样,此事件处理函数中禁止进行UI更新。
关于Sticky Event
什么是Sticky Event呢,直译的话就是黏性事件,什么意思?我们举个例子,假如现在app中有个定位的功能,这个位置需要每隔一定事件就更新一次,即我们每隔一定时间就发送一次该事件,现在跳转到了一个页面,这个页面需要获取这个位置信息,但是事件发送是在这个页面注册Event Bus之前发送的,如果是普通事件的话,自然接收不到此时的消息,而如果发送的是黏性事件的话,那么就可以获取最近所发送的那个事件消息。
这就是黏性事件的一个例子,黏性事件就是在事件发送后进行注册,依然能够接收到最新发送的事件,具体使用例子可以参考下面链接:
EventBus原理解析
按照EventBus的使用过程,就从register注册开始分析。
1
|
EventBus.getDefault().register(this);
|
进入到register函数里
1
|
public void register(Object subscriber) {
|
通过调用findSubscriberMethods查找该类中的所有订阅者函数即用@Subscribe标注的那些函数,保存到list中,然后进行遍历,调用subscribe将订阅者函数与注册类关联。接着我们看findSubscriberMethods里面做了那些操作。
1
|
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass, boolean forceReflection) {
|
这个方法主要先从缓存中查找是否有订阅者函数,没有的话再通过反射或者index的方式查找这些方法,如果找到则保存在缓存中,并返回,其他的方法,就不在具体分析了,可以查看相关源码。需要说一下这个缓存,这个缓存是一个hashmap,key就是注册的类,value是一个list集合,保存着订阅者函数。
接着看那个循环遍历中的subscribe(subscriber, subscriberMethod)方法。
1
|
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
|
首先是获取根据订阅者方法,拿到事件类型,然后根据事件类型找到对应的订阅集合,根据优先级,将订阅添加进入,注意这里的订阅即Subscription是有Subscriber和SubscribeMethod组成的类。接着会根绝订阅者,得到该订阅者所有的订阅事件队列,并将当前事件保存在里面。最后会判断该订阅方法是否是Sticky方法,如果是的话,再加入到stickyEvents并调用checkPostStickyEventToSubscription(newSubscription, stickyEvent)方法,将最后的事件发送给当前的订阅者。
最后看一下post方法
1
|
public void post(Object event) {
|
得到PostingThreadState,这是一个静态内部类,类里面有一个事件队列,判断是否在主线程的boolean变量等。
1
|
|
上面的post方法最终会调用到postSingleEvent方法,而该方法会先去eventTypesCache得到该事件对应类型的的父类及接口类型,没有缓存则查找并插入缓存。循环得到的每个类型和接口,调用 postSingleEventForEventType 函数发布每个事件到每个订阅者。
1
|
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
|
查找该事件订阅者订阅者队列,调用 postToSubscription 函数向每个订阅者发布事件。
1
|
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
|
通过查看源码发现,这些类大部分都在EventBus这个类中。所以,EventBus类负责所有对外暴露的 API,其中的 register()、post()、unregister() 函数配合上自定义的 EventType 及事件响应函数即可完成核心功能。
EventBus 默认通过静态函数 getDefault() 获取单例,我们也可以通过EventBusBuilder或构造函数新建一个EventBus,每个新建的 EventBus 发布和订阅事件都是相互隔离的,即一个 EventBus 对象中的发布者发布事件,另一个 EventBus 对象中的订阅者不会收到该订阅。
EventBus源码并不复杂,通过一步步查看源码,可以比较容易掌握它的内部实现原理的,上面只贴出了比较重要的几个函数,要想全部理解,还需要去看相关的细节实现。
EventBus应用
关于EventBus应用,官方也有讲到,这里贴一张图好了,具体的就附在后面链接了。
这张图是EventBus关于在Activity中的应用,比较好理解。具体的可以看demo
最后
感谢:
http://greenrobot.org/eventbus/



浙公网安备 33010602011771号