在这一篇文章中,我们会介绍在调用 EventBus.getDefault().register(this); 之后都发生了什么
首先介绍一下各种名词的含义:
- subscriber 类的成员函数有用 @Subscribe() 注解修饰的都能称为subscriber(订阅者)
- Subscription 封装subscriber和subscriberMethod的实体类
然后介绍一下EventBus中比较关键的几个Map类型的属性:
private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;//Key为Event(事件实体),Value为封装好的Subscription(订阅者)集合
private final Map<Object, List<Class<?>>> typesBySubscriber;//Key为Subscriber(订阅者),Value为订阅者感兴趣的Event(事件)集合
private final Map<Class<?>, Object> stickyEvents;//置顶事件集合,Key为Event实体类,Value为具体Event对象
register过程
//EventBus.class
/**
* Registers the given subscriber to receive events. Subscribers must call {@link #unregister(Object)} once they
* are no longer interested in receiving events.
*
* Subscribers have event handling methods that must be annotated by {@link Subscribe}.
* The {@link Subscribe} annotation also allows configuration like {@link
* ThreadMode} and priority.
*/
public void register(Object subscriber) {
Class subscriberClass = subscriber.getClass();
List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
调用register()方法需要传入订阅者;这个订阅者可以为调用者本身,也可以为其他订阅者
在 register() 方法内部会调用 SubscriberMethodFinder.findSubscriberMethods(Class subscriberClass) 方法来获取订阅者内部可以接收订阅事件的方法,方法实现如下:
//SubscriberMethodFinder.class
List findSubscriberMethods(Class subscriberClass) {
List subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
if (ignoreGeneratedIndex) {
subscriberMethods = findUsingReflection(subscriberClass);
} else {
subscriberMethods = findUsingInfo(subscriberClass);
}
if (subscriberMethods.isEmpty()) {
throw new EventBusException("Subscriber " + subscriberClass
+ " and its super classes have no public methods with the @Subscribe annotation");
} else {
METHOD_CACHE.put(subscriberClass, subscriberMethods);
return subscriberMethods;
}
}
注意到作者使用了缓存机制METHOD_CACHE,变量声明如下:private static final Map, List> METHOD_CACHE = new ConcurrentHashMap<>();
可见作者还考虑了多线程的情况,使用这个缓存机制可以减少重复获取订阅者处理事件的方法,提高运行效率
关于ignoreGeneratedIndex就不得不提一下在 EventBus高级使用姿势 中提到的EventBus 3.X新特性Subscriber Index,如果看过上篇文章,那么理解这个变量的含义就不困难了,ignoreGeneratedIndex的默认值为false,也就是默认采用生成的index获取订阅者的方法。
这里我们首先分析 findUsingReflection(Class subscriberClass) 方法,看名字就能猜出来是采用反射方法获取订阅者方法,具体实现上源码:
private List findUsingReflection(Class subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
findUsingReflectionInSingleClass(findState);
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
很简短的一段代码,在代码中我们遇到了新的朋友FindState,这个类是SubscriberMethodFinder的内部类,具体内容后面再讲,注意关键代码是while循环,首先是用反射方法获取订阅者方法,然后转移至父类,获取父类事件处理函数,一直到java或Android提供的系统类,完成对订阅者方法的遍历,找出所有的订阅方法
现在看一下FindState类中的属性
final List subscriberMethods = new ArrayList<>();
final Map anyMethodByEventType = new HashMap<>();
final Map subscriberClassByMethodKey = new HashMap<>();
final StringBuilder methodKeyBuilder = new StringBuilder(128);
Class subscriberClass;
Class clazz;
boolean skipSuperClasses;
SubscriberInfo subscriberInfo;
这里主要说明一下subscriberClass和clazz,其他见名思意(作者这点做的很不错),subscriberClass代表的是订阅者本身,也就是register函数的参数,自始至终不会变;clazz会在遍历方法过程中变化,比如目前在遍历父类方法,clazz变量就指向父类
回到findSubscriberMethods()函数,除了findUsingReflection()方法还有findUsingInfo()方法,现在我们分析一下findUsingInfo()方法,先上函数实现:
private List 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,注意这是一个接口,具体实现在Subscriber Index过程中实现的,第一次调用getSubscriberInfo()方法会从build过程中生成的index中取得subscriberInfo,然后每调用一次getSubscriberInfo()方法都会去的父类的subscriberInfo,这点需要注意一下,感兴趣的可以看subscriberInfo()代码,可以发现如果subscriberInfo为空的话,会采用反射方法获取订阅者方法,这和我们前一篇文章中介绍的特性一致
这里需要特别说明一下findState设计的巧妙之处:在SubscriberMethodFinder内部有一个属性FIND_STATE_POOL,这是一个大小为4的FindState数组:
- 在prepareFindState()阶段,会从FIND_STATE_POOL中取出不为空的FindState对象,然后将对应位置置空,返回FindState对象,这样可以避免多线程读写冲突以及读写不一致情况发生
- 在getMethodsAndRelease()阶段,会遍历FIND_STATE_POOL,找到为空的位置将使用完的FindState对象放进去,可以在下一次的prepareFindState()阶段复用,这样可以减少实例化对象所产生的额外消耗
回到register()方法中,既然我们拿到了订阅者内部的订阅函数,下一步应该是注册这些函数到EventBus中,别慌,我们可以发现会对得到的subscriberMethods列表循环调用subscribe()方法,那么这个方法具体是做什么的呢,我猜应该是订阅函数,嗯看名字就知道,下面我们来分析分析:
// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class eventType = subscriberMethod.eventType;
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
CopyOnWriteArrayList subscriptions = subscriptionsByEventType.get(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<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
//置顶事件,在注册EventBus的时候就会发送事件到订阅者的时间处理函数中
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>> = 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);
}
}
}
首先是根据事件类型取得同类型事件的订阅者集合,然后根据事件处理方法的优先级确定插入位置,然后根据订阅者类型插入订阅者事件集合,最后就是处理置顶之间,在源码中可以发现:置顶事件的发送是在注册EventBus阶段
至此,注册EventBus过程分析完了,如有不对的地方,欢迎在评论中指正