前言
在EventBus源码分析(一)中我们介绍了事件的注册、发送与反注册,本文的粘性事件是基于上篇文章,不同于一般事件,粘性事件主要在注册的时候有些不同的处理。
一、事件注册
粘性事件的注册方式是这样的:
@Subscribe(threadMode = ThreadMode.MAIN,sticky = true)
public void pdmEventMainThread(Student student) {
Log.e(TAG,"onEventMainThread: " + student.getContent());
}
主要是在订阅方法的注解里面加入了粘性标,设置sticky = true;
我们看下注解类:
public @interface Subscribe {
ThreadMode threadMode() default ThreadMode.POSTING;
/**
* If true, delivers the most recent sticky event (posted with
* {@link EventBus#postSticky(Object)}) to this subscriber (if event available).
*/
boolean sticky() default false;
/** Subscriber priority to influence the order of event delivery.
* Within the same delivery thread ({@link ThreadMode}), higher priority subscribers will receive events before
* others with a lower priority. The default priority is 0. Note: the priority does *NOT* affect the order of
* delivery among subscribers with different {@link ThreadMode}s! */
int priority() default 0;
}
总共三个参数
threadMode代表事件接收的线程,取值范围是POSTING 、MAIN 、BACKGROUND 、ASYNC
- POSTING:事件的处理在和事件的发送在相同的线程,所以事件处理时间不应太长,不然影响事件的发送线程。
- MAIN:事件的处理会在UI线程中执行,事件处理不应太长时间
- BACKGROUND :事件的处理会在一个后台线程中执行,尽管是在后台线程中运行,事件处理时间不应太长。如果事件分发在主线程,件会被加到一个队列中,由一个线程依次处理这些事件,如果某个事件处理时间太长,会阻塞后面的事件的派发或处理。如果事件分发在后台线程,事件会立即执行处理
- ASYNC :事件处理会在单独的线程中执行,主要用于在后台线程中执行耗时操作
threadMode 默认为POSTING,事件的处理与发送在相同的线程中
sticky代表是否是粘性是事件 boolean类型
默认为false
priority代表事件的优先级
默认为0 值越大 优先级越高
在上篇文章中的第一小节,事件的注册中,有两个的地方需要注意下,这两个地方是对粘性事件的处理:
/**
* 解析订阅方法
* @param findState
*/
private void findUsingReflectionInSingleClass(FindState findState) {
Method[] methods;
try {
// This is faster than getMethods, especially when subscribers are fat classes like Activities
// 获取订阅者里面的所有方法
methods = findState.clazz.getDeclaredMethods();
} catch (Throwable th) {
// Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
methods = findState.clazz.getMethods();
findState.skipSuperClasses = true;
}
for (Method method : methods) {
int modifiers = method.getModifiers();
// 解析方法类型
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
Class>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length == 1) {
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
if (subscribeAnnotation != null) {
Class> eventType = parameterTypes[0];
// 第一次进入返回true 重复注册返回false
if (findState.checkAdd(method, eventType)) {
// 拿到注解类型里面的值
ThreadMode threadMode = subscribeAnnotation.threadMode();
findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
}
}
} else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
String methodName = method.getDeclaringClass().getName() + "." + method.getName();
throw new EventBusException("@Subscribe method " + methodName +
"must have exactly 1 parameter but has " + parameterTypes.length);
}
} else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
String methodName = method.getDeclaringClass().getName() + "." + method.getName();
throw new EventBusException(methodName +
" is a illegal @Subscribe method: must be public, non-static, and non-abstract");
}
}
}
在这个解析订阅方法的方法里,我们拿到了订阅方法的注解类型以及对应的值,并保存到了findState对象中的subscriberMethods集合中中,并返回这个集合到register中
public void register(Object subscriber) {
Class> subscriberClass = subscriber.getClass();
// 返回注册的订阅者中的订阅方法集
List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
// 遍历循环处理订阅者中的订阅方法
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
第二个要注意的地方就是在subscribe方法中:
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
// 获取订阅方法的事件类型
Class> eventType = subscriberMethod.eventType;
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
/**
* 通过订阅者中的订阅方法集 正常为情况下为null 如果多次注册 或者是在activity退出的时候没有反注册 就会报错
* 并将订阅者与其中的订阅方法集储存起来
*/
CopyOnWriteArrayList subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
if (subscriptions.contains(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
}
}
/**
* 遍历将订阅方法添加进集合
*/
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> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
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).
Set, Object>> entries = stickyEvents.entrySet();
for (Map.Entry, 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);
}
}
}
回忆下上篇文章,这个方法主要是将订阅者中的订阅方法和事件类型进行分类储存
- 以订阅者中的事件类型为key,该事件类型对应的该订阅者和订阅方法为value保存到subscriptionsByEventType中,
- 以订阅者为key,该订阅者中的所有的事件类型的list集合为value保存到typesBySubscriber集合中。
如果该事件是粘性事件,eventInheritance是由EventBuild赋值的,默认为true,具体作用没有我没有细究,不过在走完if方法体中的内容后,可以一起去看看else中的方法体。
stickyEvents是在发送粘性事件的时候才赋值的
// 发送事件
EventBus.getDefault().postSticky(new MessageEvent("Hello everyone!"));
// 事件的处理 并保存到stickyEvents
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);
}
所以在首次注册本事件类型的粘性事件时,这个if代码块是不会走的,所以也就有了
可以先发送事件,而后再注册事件的情况 。 可以这么理解,事件只要发送了,无论是在发送前注册还是发送后注册,订阅者都能收到事件,这就是粘性事件。
假设我们已经注册了 过了 去看看if代码块中的逻辑:
for循环里面有个判断如果当前的事件类型与stickyEvents集合中的事件类是父>子关系的 那就执行checkPostStickyEventToSubscription方法。
private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
if (stickyEvent != null) {
// If the subscriber is trying to abort the event, it will fail (event is not tracked in posting state)
// --> Strange corner case, which we don't take care of here.
postToSubscription(newSubscription, stickyEvent, 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 MAIN_ORDERED:
if (mainThreadPoster != null) {
mainThreadPoster.enqueue(subscription, event);
} else {
// temporary: technically not correct as poster not decoupled from subscriber
invokeSubscriber(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);
}
}
后面就跟正常的事件发送一致了。
我们再来看下else代码块中的逻辑,他是直接从stickyEvents获取 然后执行很简单
这两个代码块的区别就是在for循环中的if判断语句,如果不是为if,那if代码块中的代码也完全可以写成else中的,我们结合下注释和isAssignableFrom 大致推测就是
如果post(A),A extends B implements C,那么onEvent(A)、onEvent(B)、onEvent(C)会被调用
具体的结果,还是由你们来校验吧。
粘性事件的处理到这里基本就完了,下面就是EventBus提供的一些其他的关于粘性事件的接口
获取某一个粘性事件,可以判断此粘性事件是否存在
public T getStickyEvent(Class eventType) {
synchronized (stickyEvents) {
return eventType.cast(stickyEvents.get(eventType));
}
}
删除某一个粘性事件(类类型的)
public T removeStickyEvent(Class eventType) {
synchronized (stickyEvents) {
return eventType.cast(stickyEvents.remove(eventType));
}
}
这个删除粘性事件是将stickyEvents集合中的粘性事件删除了 但是事件本身还是自带sticky = true属性,但是不影响
删除某一个粘性事件(对象类型的)
public boolean removeStickyEvent(Object event) {
synchronized (stickyEvents) {
Class> eventType = event.getClass();
Object existingEvent = stickyEvents.get(eventType);
if (event.equals(existingEvent)) {
stickyEvents.remove(eventType);
return true;
} else {
return false;
}
}
}
两个都是一样的,只是入参不一样
删除所有粘性事件 很强
public void removeAllStickyEvents() {
synchronized (stickyEvents) {
stickyEvents.clear();
}
}