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
2
3
4
5
6
7
8
9
10
11
12
13
public class MessageEvent {
private String message;

public MessageEvent(String message) {
this.message = message;
}
public String getMessage() {
return message;
}

public void setMessage(String message) {
this.message = message;
}

这里定义了一个POJO,里面有一个属性——消息,在实际的应用中可以是任意的实体类对象,比如网络返回的不同的POJO对象。

3.准备订阅者

1
2
3
4
@Subscribe(threadMode = ThreadMode.MainThread)
public void onMessageEvent(MessageEvent message) {
Toast.makeText(getApplicationContext(),message.getMessage(),Toast.LENGTH_SHORT).show();
}

订阅事件函数用@Subscribe注解标注,括号里是线程模式,这里代表事件订阅者函数在主线程执行

4.注册EventBus

1
2
3
4
5
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}

在Activity的onStart()方法里注册EventBus,当然也可以在其他的地方进行注册,这里以Activity举例。

5.发送事件

1
EventBus.getDefault().post(new MessageEvent("Hello everyone!"));

这句代码可以在你想要执行的任何地方执行,然后所有订阅了MessageEvent事件的订阅者都会收到该事件,前提是订阅者所在的那个类里,必须注册了EventBus。

6.注销EventBus

1
2
3
4
5
@Override
public void onStop() {
EventBus.getDefault().unregister(this);
super.onStop();
}

记得在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之前发送的,如果是普通事件的话,自然接收不到此时的消息,而如果发送的是黏性事件的话,那么就可以获取最近所发送的那个事件消息。

这就是黏性事件的一个例子,黏性事件就是在事件发送后进行注册,依然能够接收到最新发送的事件,具体使用例子可以参考下面链接:

Sticky Event使用

EventBus原理解析

按照EventBus的使用过程,就从register注册开始分析。

1
EventBus.getDefault().register(this);

进入到register函数里

1
2
3
4
5
6
7
8
9
10
11
12
13
public void register(Object subscriber) {
//获取注册的类对象
Class<?> subscriberClass = subscriber.getClass();
// @Subscribe in anonymous classes is invisible to annotation processing, always fall back to reflection
//判断该类是否是匿名类
boolean forceReflection = subscriberClass.isAnonymousClass();

List<SubscriberMethod> subscriberMethods =
subscriberMethodFinder.findSubscriberMethods(subscriberClass, forceReflection);
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}

通过调用findSubscriberMethods查找该类中的所有订阅者函数即用@Subscribe标注的那些函数,保存到list中,然后进行遍历,调用subscribe将订阅者函数与注册类关联。接着我们看findSubscriberMethods里面做了那些操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass, boolean forceReflection) {
//将注册类的类名作为key 该key会作为键值存储在hashmap中
String key = subscriberClass.getName();
List<SubscriberMethod> subscriberMethods;
//先从缓存中寻找是否有订阅函数
synchronized (METHOD_CACHE) {
subscriberMethods = METHOD_CACHE.get(key);
}
//有的话 直接返回
if (subscriberMethods != null) {
return subscriberMethods;
}
//这里的index是EventBus生成的一个类SubscriberIndex
//forceReflection就是那个判断是否匿名类的变量
if (INDEX != null && !forceReflection) {
subscriberMethods = findSubscriberMethodsWithIndex(subscriberClass);
if (subscriberMethods.isEmpty()) {
subscriberMethods = findSubscriberMethodsWithReflection(subscriberClass);
}
} else {
subscriberMethods = findSubscriberMethodsWithReflection(subscriberClass);
}
if (subscriberMethods.isEmpty()) {
throw new EventBusException("Subscriber " + subscriberClass
+ " and its super classes have no public methods with the @Subscribe annotation");
} else {
//加入到缓存中 并返回
synchronized (METHOD_CACHE) {
METHOD_CACHE.put(key, subscriberMethods);
}
return subscriberMethods;
}
}

这个方法主要先从缓存中查找是否有订阅者函数,没有的话再通过反射或者index的方式查找这些方法,如果找到则保存在缓存中,并返回,其他的方法,就不在具体分析了,可以查看相关源码。需要说一下这个缓存,这个缓存是一个hashmap,key就是注册的类,value是一个list集合,保存着订阅者函数。

接着看那个循环遍历中的subscribe(subscriber, subscriberMethod)方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
//从订阅方法中拿到订阅事件的类型
Class<?> eventType = subscriberMethod.eventType;
//通过订阅事件类型,找到所有的订阅(Subscription)
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
//创建订阅(由subscriber和subscriberMethod组成)并保存到订阅集合中
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<Subscription>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
if (subscriptions.contains(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
}
}
synchronized (subscriptions) {
int size = subscriptions.size();
for (int i = 0; i <= size; i++) {
if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
subscriptions.add(i, newSubscription);
break;
}
}
}
//将这个订阅事件加入到订阅者的订阅事件列表中
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<Class<?>>();
typesBySubscriber.put(subscriber, subscribedEvents);
}

subscribedEvents.add(eventType);

if (subscriberMethod.sticky) {
if (eventInheritance) {
// Existing sticky events of all subclasses of eventType have to be considered.
// Note: Iterating over all events may be inefficient with lots of sticky events,
// thus data structure should be changed to allow a more efficient lookup
// (e.g. an additional map storing sub classes of super classes: Class -> List<Class>).
Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
for (Map.Entry<Class<?>, Object> entry : entries) {
Class<?> candidateEventType = entry.getKey();
if (eventType.isAssignableFrom(candidateEventType)) {
Object stickyEvent = entry.getValue();
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}

首先是获取根据订阅者方法,拿到事件类型,然后根据事件类型找到对应的订阅集合,根据优先级,将订阅添加进入,注意这里的订阅即Subscription是有Subscriber和SubscribeMethod组成的类。接着会根绝订阅者,得到该订阅者所有的订阅事件队列,并将当前事件保存在里面。最后会判断该订阅方法是否是Sticky方法,如果是的话,再加入到stickyEvents并调用checkPostStickyEventToSubscription(newSubscription, stickyEvent)方法,将最后的事件发送给当前的订阅者。

最后看一下post方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public void post(Object event) {
PostingThreadState postingState = currentPostingThreadState.get();
List<Object> eventQueue = postingState.eventQueue;
eventQueue.add(event);

if (!postingState.isPosting) {
postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper();
postingState.isPosting = true;
if (postingState.canceled) {
throw new EventBusException("Internal error. Abort state was not reset");
}
try {
while (!eventQueue.isEmpty()) {
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}

得到PostingThreadState,这是一个静态内部类,类里面有一个事件队列,判断是否在主线程的boolean变量等。

1
2
3
4
5
6
7
8
9

final static class PostingThreadState {
final List<Object> eventQueue = new ArrayList<Object>();
boolean isPosting;
boolean isMainThread;
Subscription subscription;
Object event;
boolean canceled;
}

上面的post方法最终会调用到postSingleEvent方法,而该方法会先去eventTypesCache得到该事件对应类型的的父类及接口类型,没有缓存则查找并插入缓存。循环得到的每个类型和接口,调用 postSingleEventForEventType 函数发布每个事件到每个订阅者。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
subscriptions = subscriptionsByEventType.get(eventClass);
}
if (subscriptions != null && !subscriptions.isEmpty()) {
for (Subscription subscription : subscriptions) {
postingState.event = event;
postingState.subscription = subscription;
boolean aborted = false;
try {
postToSubscription(subscription, event, postingState.isMainThread);
aborted = postingState.canceled;
} finally {
postingState.event = null;
postingState.subscription = null;
postingState.canceled = false;
}
if (aborted) {
break;
}
}
return true;
}
return false;
}

查找该事件订阅者订阅者队列,调用 postToSubscription 函数向每个订阅者发布事件。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
case PostThread:
invokeSubscriber(subscription, event);
break;
case MainThread:
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
mainThreadPoster.enqueue(subscription, event);
}
break;
case BackgroundThread:
if (isMainThread) {
backgroundPoster.enqueue(subscription, event);
} else {
invokeSubscriber(subscription, event);
}
break;
case Async:
asyncPoster.enqueue(subscription, event);
break;
default:
throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
}
}

通过查看源码发现,这些类大部分都在EventBus这个类中。所以,EventBus类负责所有对外暴露的 API,其中的 register()、post()、unregister() 函数配合上自定义的 EventType 及事件响应函数即可完成核心功能。

EventBus 默认通过静态函数 getDefault() 获取单例,我们也可以通过EventBusBuilder或构造函数新建一个EventBus,每个新建的 EventBus 发布和订阅事件都是相互隔离的,即一个 EventBus 对象中的发布者发布事件,另一个 EventBus 对象中的订阅者不会收到该订阅。

EventBus源码并不复杂,通过一步步查看源码,可以比较容易掌握它的内部实现原理的,上面只贴出了比较重要的几个函数,要想全部理解,还需要去看相关的细节实现。

EventBus应用

关于EventBus应用,官方也有讲到,这里贴一张图好了,具体的就附在后面链接了。

这张图是EventBus关于在Activity中的应用,比较好理解。具体的可以看demo

最后

感谢:

http://greenrobot.org/eventbus/

http://www.cnblogs.com/yydcdut/p/4651208.html

http://liuling123.com/2016/01/EventBus-explain.html

posted @ 2017-08-09 15:51  天涯海角路  阅读(234)  评论(0)    收藏  举报