好多分析EventBus的文章,喜欢上来就贴源码,我看了好多次总是迷迷糊糊的,这次花时间彻底整理一下EventBus,发现EventBus核心其实就是三幅图,这三幅图涉及的是三个HashMap表,弄懂这三幅图那么EventBus就懂了。
先看一段在activity中注册和反注册EventBus的代码。
onStart{
EventBus.getDefault().register(this);
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEvent1(Event1 event) {
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEvent2(Event2 event) {
}
onStop{
EventBus.getDefault().register(this);
}
看上面的代码,注册监听的是activity,称为subscriber,在activity中监听了Event1和Event2两个事件,现在在另一个位置执行了一段代码:
EventBus.getDefault().post(new Event1());
这个时候,activity中的onEvent1就会收到事件。下面引入第一幅图:
如图所示,一个Subscribe对应多个Event,Subsribe就是上面通过register方法注册的对象,比如activity。这幅图对应EventBus中一个Map结构:
private final Map<Object, List<Class<?>>> typesBySubscriber;
EventBus会在对象register时,使用反射机制,遍历对象的方法,将带有@Subscribe标签并且合法的方法加入到typesBySubscriber。typesBySubscriber是HashMap形式,key是注册的对象本身,由于一个注册的对象中可能有多个监听事件,所以value是用list保存的Event。
看下register方法中如何处理的
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
}
上面的代码主要做两件事:1、通过反射遍历注册对象的方法,获取其中带有@Subscribe标签的方法并且放在一个列表中,最后以注册对象为key,@Subscribe的方法列表作为value放在HashMap中,就是上图的形式。
思考
1、为什么要将注册监听对象作为key,监听事件列表作为value放在HashMap中?
要弄懂一个问题,EventBus是观察者模式,上面的activity也就是subscribe是订阅者,activity中的event是订阅事件,一个订阅者可以订阅多个事件,移除一个订阅者的监听事件时,应该将其中所有的event的事件移除。
也就是说在反注册的时候,会通过Subsribe来查找到其中所有额event进行反注册。
这种表关系是event和subsciption的对应关系,比如在Android中多个activity可能会注册监听同一个event事件,所以在执行:
EventBus.getDefault().post(new Event1());
的时候所有注册监听了Event1的监听都会要会收到回调,看下subsciption的结构
subsciption中包含,订阅的事件和订阅者本身,上面中所有的event就是订阅的事件,在Android中订阅的事件代码如下:
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEvent(Event event) {
}
而subsriber就是订阅者比如会在activity的onstart中执行
....
EventBus.getDefault().register(this);
那么subsribe就是activity。
思考 为什么需要保存Event和subsribe对应的关系表?
这是因为一个Event可能会有被多个subsribe订阅,所以有当执行post(Event)的时候会查找到所有订阅了Event事件的subscribe并调用其中的event方法。下面看下post方法:
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;
}
}
post和postSticky主要都会调用到上面的方法,上面方法中subscriptionsByEventType.get(eventClass)
就是通过event的类型找上面的表中找到对应的subscriptions进行通知的。
在看第三幅图之前思考一个问题,postSticky到底是怎么执行的?为什么先执行postSticky,后执行register还是可以监听到event事件?
先看postSticky代码:
public void postSticky(Object event) {
synchronized (stickyEvents) {
stickyEvents.put(event.getClass(), event);
}
// Should be posted after it is putted, in case the subscriber wants to remove immediately
post(event);
}
原来执行postSticky的时候会将event.getclass和event保存起来,然后再看下subscribe代码:
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).
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);
先判断注册监听的event是不是sticky的如果是就会用stickEvents表中找到stickyEvent如果如果注册的事件event和stickyEvent一样那么就会执行一次postToSubscription方法,也就是调用注册的方法执行。
EventBus支持监听方法在不同线程中进行回调,可以通过ThreadMode来进行设置用法如下:
@Subscribe(threadMode = ThreadMode.MainThread)
public void onNewsEvent(NewsEvent event){
String message = event.getMessage();
mTv_message.setText(message);
}
ThreadMode总共有四种方式:
1、PostThread
事件的处理在和事件的发送在相同的进程,所以事件处理时间不应太长,不然影响事件的发送线程。
2、MainThread
事件的处理会在UI线程中执行。事件处理时间不能太长,这个不用说的,长了会ANR的。
3、BackgroundThread
如果事件是在UI线程中发布出来的,那么事件处理就会在子线程中运行,如果事件本来就是子线程中发布出来的,那么事件处理直接在该子线程中执行。所有待处理事件会被加到一个队列中,由对应线程依次处理这些事件,如果某个事件处理时间太长,会阻塞后面的事件的派发或处理。
4、Async
事件处理会在单独的线程中执行,主要用于在后台线程中执行耗时操作,每个事件会开启一个线程。
1、要理解EventBus就要从register,unRegister,post,postSticky方法入手。要理解register实质上是将订阅对象(比如activity)中的每个带有subscriber的方法找出来,最后获得调用的就是这些方法。订阅对象(比如activity)是一组event方法的持有者。
2、后注册的对象中sticky方法能够收到之前的stickyEvent方法的原因是EventBus中维护了stickyEvent的hashMap表,在subsribe注册的时候就遍历其中有没有注册监听stickyEvent如果有就会执行一次回调。
1、使用的时候有定义很多event类,
2、event在注册的时候会调用反射去遍历注册对象的方法在其中找出带有@subscriber标签的方法,性能不高。
3、需要自己注册和反注册,如果忘了反注册就会导致内存泄漏