EventBus使用与原理剖析

一.基础使用

1.概述

1.1定义

EventBus is a publish/subscribe event bus optimized for Android.

1.2优点

1.简化组件间的通讯。

2.事件发送者和接收者解耦。

3.很好地执行活动,片段和背景线程,避免复杂和容易出错的依赖关系和生命周期的问题,使您的代码更简单。

4.快速

5.小(大约50K)

1.3比较

1.简化子线程和主线程之间或两个子线程之间的消息传递
通常我们都会在一个线程中做一些耗时操作得到数据之后需要发送到另一个线程去处理数据(这个线程可以是主线程去更新UI,或子线程继续处理接下来的任务)。这些通信都需要用到Handler。我们需要定义Handler,并重写handleMessage方法去处理数据,在使用处定义Message对象携带数据并发送到指定的Handler中
2.代替BroadcastReceiver/Intent
不同于安卓的BroadcastReceiver/Intent,EventBus就是普通的java类,它提供很简单易用的API调用。EventBus适用于更多的场景,并不需要你麻烦的设置Intent,设置携带数据,然后从Intent中取出数据,或者定义广播和广播接受者。同时,EventBus的开销会低,对于输出传送方和接收方没有那么高的耦合。
3.简化数据传输
比如:(1)在一个Activity中横向同时加载两个Fragment,左边的ragment是列表,右边的Fragment是详情展示。这时候涉及到Activity和Fragment,两个Fragment之间的通信(2)当使用startService启动一个Service时,你需要和Service相互通信时(3)有些场景需要使用接口处理问题时(4)在应用程序运行时可能有多个界面,多处都需要使用到一个数据时,这个数据只是需要临时存储,并不需要落地时。

2.使用方法

2.1定义事件

Events are POJO (plain old Java object) without any specific requirements.

publicclassMessageEvent{

publicfinalString message;

publicMessageEvent(String message) {

this.message = message;

}

}

2.2准备订阅者

// This method will be called when a MessageEvent is posted

public void onEvent(MessageEvent event){

Toast.makeText(getActivity(), event.message, Toast.LENGTH_SHORT).show();

}

2.3发布事件

可以从代码的任何一个部分发布事件,所有订阅这个类型的事件的订阅者都可以收到这个事件。

EventBus.getDefault().post(newMessageEvent("Hello everyone!"));

3.主要类图

EventBus类负责所有对外暴露的API,其中的register()、post()、unregister()函数配合上自定义的EventType及事件响应函数即可完成核心功能。

EventBus可以通过getDefault来获取单例,也可以自己通过创建或者EventBusBuilder来新建一个,不过EventBus是相互隔离的。

getDefault()

进程中创建一个EventBus的单例返回。

register(Object subscriber)

注册一个订阅者到接收事件。订阅者必须调用'unregister()'当它们不再接收事件。

订阅者有一个事件句柄方法用他们的名字识别,通常是叫“onEvent”。事件句柄方法必须有一个参数,事件。如果事件句柄方法被调用在一个特殊的线程,一个修改将被用到方法的名字上。有效的修改符合ThreadMode枚举之一。例如,如果一个方法被调用在由EventBus在UI/主线程,这将被称为“onEventMainThread”。

unregister(Object subscriber)

反注册一个订阅者

post(Object event)

发送一个事件到event bus。

4.线程分发和线程模型

4.1线程模型

Event可以为你处理线程:事件可以被发布到与发布线程不同的线程中。
一般用法是处理UI,在安卓中UI的处理需要在主线程中完成,其他处理比如网络请求,耗时操作不能在主线程中处理。EventBus帮助你处理这些任务并且同步到UI线程(不用深入研究线程转换,使用AsyncTask等等)

ThreadMode有四种:
PostThread,MainThread,BackgroundThread,Async.

4.2四种订阅函数

onEvent:如果使用onEvent作为订阅函数,那么该事件在哪个线程发布出来的,onEvent就会在这个线程中运行,也就是说发布事件和接收事件线程在同一个线程。使用这个方法时,在onEvent方法中不能执行耗时操作,如果执行耗时操作容易导致事件分发延迟。

onEventMainThread:如果使用onEventMainThread作为订阅函数,那么不论事件是在哪个线程中发布出来的,onEventMainThread都会在UI线程中执行,接收事件就会在UI线程中运行,这个在Android中是非常有用的,因为在Android中只能在UI线程中跟新UI,所以在onEvnetMainThread方法中是不能执行耗时操作的。

onEvnetBackground:如果使用onEventBackgrond作为订阅函数,那么如果事件是在UI线程中发布出来的,那么onEventBackground就会在子线程中运行,如果事件本来就是子线程中发布出来的,那么onEventBackground函数直接在该子线程中执行。

onEventAsync:使用这个函数作为订阅函数,那么无论事件在哪个线程发布,都会创建新的子线程在执行onEventAsync.

二.框架原理

本次分析从两个方向深入,一个是从注册开始,一个是从发送消息开始。从这两个方向就能大致了解eventbus的运作原理。

1.注册原理

1.1 MainActivity

EventBus.getDefault().register(this);
EventBus的注册就从这里开始。

1.2 EventBus.getDefault()

public
static
 EventBus 
getDefault
()
{
if
 (defaultInstance == 
null
) {
synchronized
 (EventBus.class) {
if
 (defaultInstance == 
null
) {
defaultInstance = 
new
 EventBus();
}
}
}
return
 defaultInstance;
}

这是一句很典型的单例写法,整个eventbus在项目中是以单例的形式出现的。在初始化这块调用的是EventBusBuilder的默认参数。这也是Builder模式比较常用的。

1.3 EventBus.register(Object subscriber)

public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}

这里首先调用findSubscriberMethods()方法根据当前订阅者的类名查找到该类的所有订阅者函数。在获取完所有订阅者函数后调用subscribe方法。

1.4 EventBus.subscribe(subscriber, subscriberMethod)

这里是注册函数的核心,分成三个部分:

通过subscriptionsByEventType得到这个Event类型所有订阅者信息队列,然后根据优先级将当前订阅者信息插入到队列里面。

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;
}
}

在typesBySubscriber中得到当前订阅者订阅的所有事件队列,将此事件保存到队列中,用于后续取消订阅。

List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);

检查这个事件是否是Sticky事件,如果是则从stickyEvents事件保存队列中取出该事件类型最后一个事件发送给当前订阅者。

if (subscriberMethod.sticky) {
if (eventInheritance) {
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);
}
}

1.5 EventBus.unregister(Object subscriber)

最后就是反注册,这里就比较简单了。
首先从typesBySubscriber获取当前订阅者,然后找到此订阅者的所有类型,将此订阅者的所有类型从subscriptionsByEventType表里删除。接着再把此订阅者从typesBySubscriber中删除。

public synchronized void unregister(Object subscriber) {
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
if (subscribedTypes != null) {
for (Class<?> eventType : subscribedTypes) {
unsubscribeByEventType(subscriber, eventType);
}
typesBySubscriber.remove(subscriber);
} else {
Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}

2. POST的原理

2.1 MainActivity

public
class
MessageEvent
{
public
final
 String message;
public
MessageEvent
(String message)
{
this
.message = message;
}
}
}
public
void
onEventMainThread
(MessageEvent event)
{ }
EventBus.getDefault().post(
new
 MessageEvent(
"
hello eventbus
"
));

EventBus.getDefault().post(this);

2.2 EventBus.post(Object event)

这个函数将收到的event发送到event bus。首先将此事件保存到currentPostingThreadState的事件队列里。然后查看当前是否有事件在发送,然后调用postSingleEvent()函数发送。

2.3 EventBus.postSingleEvent()

首先调用lookupAllEventTypes()获取所有事件的类型,然后循环调用postSingleEventForEventType()函数发送事件。

2.4 EventBus.lookupAllEventTypes(...)

这里从当前事件中获取父类和接口,一直往上循环获取直到最后。然后保存到eventTypesCache变量里。

2.5 EventBus.postSingleEventForEventType(...)

这里就是获取每个事件,然后通过循环发送这些事件,会将事件的参数添加到PostingThreadState结构体里传到postToSubscription()函数来发送,这里就能区分是主界面线程还是非界面线程。最后再把参数都反初始化。

2.6 EventBus.postToSubscription(Subscription subscription, ...)

这个函数就是主要的分发函数,根据每个事件的threadMode来分发到各自相应的回调函数。

private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
case POSTING:
invokeSubscriber(subscription, event);
break;
case MAIN:
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
mainThreadPoster.enqueue(subscription, event);
}
break;
case BACKGROUND:
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);
}
}

这里主要就是分发到4个不同的函数。

2.7 invokeSubscriber(Subscription subscription, Object event)

void invokeSubscriber(Subscription subscription, Object event) {
try {
subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
} catch (InvocationTargetException e) {
handleSubscriberException(subscription, event, e.getCause());
} catch (IllegalAccessException e) {
throw new IllegalStateException("Unexpected exception", e);
}
}

总结:

我们完整的源码分析就结束了,总结一下:register会把当前类中匹配的方法,存入一个map,而post会根据实参去map查找进行反射调用。其实不用发布者,订阅者,事件,总线这几个词或许更好理解,以后大家问了EventBus,可以说,就是在一个单例内部维持着一个map对象存储了一堆的方法;post无非就是根据参数去查找方法,进行反射调用。

results matching ""

    No results matching ""