背景
最近项目做组件化,需要进行组件化的通信,有时候可能会出现异步的情况,事件接收方还没准备好事件就已经发送过来了。这时候想到了EventBus的粘性事件。什么是粘性事件呢,通俗点讲就是不达目的不罢休的事件,事件保证能发送到订阅者手里。
发送粘性事件
EventBus发送粘性事件非常简单,一段代码就可以搞定:
EventBus.getDefault().postSticky(event)
接收粘性事件
接收粘性事件也非常简单,注册方法上加个sticky = true即可
@Subscribe(threadMode = ThreadMode.MAIN,sticky = true)
public void loginSuccessEvent(event: LoginSuccessEvent){}
粘性事件代码解析
一行代码就能实现这件神奇的事情,那么中间到底发生了什么呢,下面我们分析下源码:
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);
}
从代码中发现首先会把粘性事件用stickyEvents缓存起来,然后像普通事件一样post出去。
stickyEvents是一个Map集合,加入这个集合里面会发生什么呢?
private final Map<Class<?>, Object> stickyEvents;
通过跟踪我们会发现以下代码
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) {
....省略N段代码...
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);
}
}
}
从上面可以看出在register事件的时候回调用subscribe方法,在subscribe中会处理stickyEvents中的粘性事件通过checkPostStickyEventToSubscription方法发送出去,继续跟踪发现最终都会走到下面这个方法里面
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);
}
}
subscription.subscriberMethod.method.invoke(subscription.subscriber, event)就是执行在订阅者那边定义的处理事件的方法,至此整个流程就结束了。
看到这里想必大家都发现了,粘性事件有可能会收到两次,注意我这里用的是有可能。
让我们分析以下两种场景:
第一种订阅者Activity已经启动了,这时候事件通过postSticky之后调用post发送,而stickyEvents中缓存的事件不会发送,这是为什么呢。前面分析stickyEvents中事件的处理是在事件register的时候,而这时候粘性事件还没有发送所以只会发送一次。
第二种订阅者Activity还没启动但是粘性事件发送了,这时候通过post方式发送的消息是收不到的,然后当订阅者Activity中register事件时就会再发送一遍粘性事件。
综述,最终事件只会发送一遍。