EventBus整体思路和优化

整体思路

一个最简化的订阅,注册,发布事件

发了一个事件,如何回调相关注册的方法呢?

最重要的Map,subscriptionsByEventType,一个事件对应多个订阅订阅包含了订阅者订阅方法

一个订阅包含两个信息,谁去做(订阅者),做什么(交给用户实现的部分),怎么做(线程如何调度,threadMode)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

//根据事件找到订阅
private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;

final class Subscription {
final Object subscriber; // who
final SubscriberMethod subscriberMethod; // what and how
}

public class SubscriberMethod {
final Method method; // do what
final ThreadMode threadMode; // how to do
final Class<?> eventType;
final int priority;
final boolean sticky;
}

关于注销订阅

订阅建立后,订阅者接受相关的事件的订阅就会被触发,做该做的事了,可若是订阅者不想订阅了,不想继续受到该之前订阅的事件了。(或者,生命周期结束了,若不注销订阅,EventBus实例会保留对该订阅者的引用,所以要注销该订阅者的所有订阅)

是的,一个订阅者可以拥有多个订阅,所以,为了注销该订阅者,还得引入

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

// 一个订阅者对应订阅了什么事件类型
private final Map<Object, List<Class<?>>> typesBySubscriber;

// 1. 注册时,添加该订阅者,以及该订阅者订阅了什么关系(方便注销时,改动subscriptionsByEventType)
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);


// 2. 注销时
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 {
logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}


// 3. 注销时,内部调用,迫使subscriptionsByEventType移除该订阅者中的订阅
/** Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber. */
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions != null) {
int size = subscriptions.size();
for (int i = 0; i < size; i++) {
Subscription subscription = subscriptions.get(i);
if (subscription.subscriber == subscriber) {
subscription.active = false;
subscriptions.remove(i);
i--;
size--;
}
}
}
}

优化

编译时注解处理器生成索引

通过生成索引,EventBus可加快注册的速度,这在Android中是一个很好的优化,毕竟在onCreate方法中耗时太久会导致卡顿。

索引内部使用流程

注册时,需要生成subscriptionsByEventType,对应一个订阅者,即找到一个

List subscriberMethods ,

而寻找时

1
2
3
4
5
if (ignoreGeneratedIndex) {
subscriberMethods = findUsingReflection(subscriberClass);
} else {
subscriberMethods = findUsingInfo(subscriberClass);
}

可以看到有一个变量,ignoreGeneratedIndex 是否忽视生成索引,默认情况为false,即会尝试从索引查找的。

(在这里,若是没有配置生成索引,其实在配置时设置忽略索引,避免多走一些不必要的路)

而进入findUsingInfo()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
findState.subscriberInfo = getSubscriberInfo(findState);//尝试获取索引生成信息
if (findState.subscriberInfo != null) { //若有索引,则可直接添加该方法了
SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
for (SubscriberMethod subscriberMethod : array) {
if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
findState.subscriberMethods.add(subscriberMethod);
}
}
} else { //否则,还是得倒退到使用索引的情况。
findUsingReflectionInSingleClass(findState);
}
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}

而findState中的subscriberInfo是什么呢?

1
2
3
4
5
6
7
8
9
10
11

/** Base class for generated index classes created by annotation processing. */
public interface SubscriberInfo {
Class<?> getSubscriberClass(); //订阅者该类的Class作为Key

SubscriberMethod[] getSubscriberMethods(); //标记有@Subscriber的方法

SubscriberInfo getSuperSubscriberInfo(); //该订阅者父类的信息

boolean shouldCheckSuperclass();
}

可以看到,这个类定义了EventBus对于一个订阅者,它关注什么信息

注意,文档中说明,对于索引使用是有条件的,如下

Index Preconditions

Note that only @Subscriber methods can be indexed for which the subscriber AND event class are public. Also, due to technical limitations of Java’s annotation processing itself, @Subscribe annotations are not recognized inside of anonymous classes.

When EventBus cannot use an index, it will automatically fallback to reflection at run time. Thus it will still work, just a bit slower.

  • 订阅者和事件的类必须是public
  • 在匿名类中的@Subscriber标记的方法,注解处理器无法识别
  • 故,当使用索引失败时,会倒退到使用发射

忽视事继承性

即事件B继承A,若是订阅了事件A,发送事件B,是可以接收到的。

eventInheritance默认是为true的,支持事件的继承性。

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
//发布一个事件
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
if (eventInheritance) {
List<Class<?>> eventTypes = lookupAllEventTypes(eventClass); //寻找所有该类型的所有父类
int countTypes = eventTypes.size();
for (int h = 0; h < countTypes; h++) {
Class<?> clazz = eventTypes.get(h);
subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
}
} else {
subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
}

//d
/** Looks up all Class objects including super classes and interfaces. Should also work for interfaces. */
private static List<Class<?>> lookupAllEventTypes(Class<?> eventClass) {
synchronized (eventTypesCache) {
List<Class<?>> eventTypes = eventTypesCache.get(eventClass);
if (eventTypes == null) {
eventTypes = new ArrayList<>();
Class<?> clazz = eventClass;
while (clazz != null) {
eventTypes.add(clazz); //添加本身
addInterfaces(eventTypes, clazz.getInterfaces());//添加实现的接口
clazz = clazz.getSuperclass(); //继续遍历其父类
}
eventTypesCache.put(eventClass, eventTypes);
}
return eventTypes;
}
}

若是在项目中不会用到事件类型的继承性,也能省不少

比如,事件类型类为String.class,会迭代5次

1
2
3
4
5
6
eventTypes = {ArrayList@908}  size = 5
0 = {Class@322} "class java.lang.String"
1 = {Class@325} "interface java.io.Serializable"
2 = {Class@324} "interface java.lang.Comparable"
3 = {Class@323} "interface java.lang.CharSequence"
4 = {Class@326} "class java.lang.Object"