前言#
为了让文章的篇幅保持一个短小精悍的程度,所以决定把EventBus的分析拆分成:注册解绑和发送Event的处理过程两部分。
今天来看看注册解绑是怎么操作的。
正文#
EventBus首先需要注册,才能得到Event的响应,这里省略了某些类的源码,直接看过程:
/**
* 这里来注册广播,并且提示接收Event的方法必须被Subscribe注解
* */
public void register(Object subscriber) {
// 先得到类名
Class> subscriberClass = subscriber.getClass();
// 找到这类被注解的方法
List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
// 遍历查找到的被注解的方法
for (SubscriberMethod subscriberMethod : subscriberMethods) {
// 把注册的类和被注解的方法保存起来
subscribe(subscriber, subscriberMethod);
}
}
}
在注册的方法中,先根据注册对象的Class,找到内部被注解的方法,然后对每个被注解的方法再一次进行处理。我们先看看是怎么得到Class中被注解的方法的,SubscriberMethodFinder是用来查找某个Class中被注解方法的辅助类,看一下findSubscriberMethods(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;
}
}
首先先从缓存中获取,这样之前已经查找过的内容就可以直接得到了,提升了某一个Class反复注册的速度,例如我们经常在onResume和onPause注册和解绑,这样就不用担心效率的问题了。
然后是判断我们在EventBusBuilder中的设置,是否要忽略我们之前生成的文件,建议不要忽略,我们知道编译生成的Java文件,已经把Class和被注解方法都保存到一个Map集合里了,就省去了查询的时间。
不过我们还要去看看他是怎么得到Class被注解的方法的,打开findUsingReflection()方法:
/**
* 通过反射的到类的被注解的方法
* */
private List findUsingReflection(Class> subscriberClass) {
// 得到一个查找类对象
FindState findState = prepareFindState();
// 设置要查找的类
findState.initForSubscriber(subscriberClass);
// 开始循环查找
while (findState.clazz != null) {
// 通过反射查找类中被注解的方法
findUsingReflectionInSingleClass(findState);
// 接着查找父类
findState.moveToSuperclass();
}
// 得到刚才循环查找的结果
return getMethodsAndRelease(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();
// 如果是public 并且忽略几种类的情况(抽象类,静态类等)
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
// 得到方法参数类型数组
Class>[] parameterTypes = method.getParameterTypes();
// 参数的个数是1
if (parameterTypes.length == 1) {
// 得到这个方法的Subscribe注解
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
// 如果这个方法被Subscribe注解了
if (subscribeAnnotation != null) {
// 得到参数的class类型
Class> eventType = parameterTypes[0];
// 检查是否添加成功
if (findState.checkAdd(method, eventType)) {
// 获取注解的ThreadMode的值
ThreadMode threadMode = subscribeAnnotation.threadMode();
// 还记得注解库中的那段乱遭的代码吗,他们的作用是一样的,把类和对应的注解的方法,保存起来
// 请注意eventType,这个是把参数的类型作为eventType
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");
}
}
}
我把两个方法一起贴出来了,连起来其实是多个遍历的过程:
1、从当前类开始,一直遍历到最顶端的父类,到Java类和android类为止。
2、遍历当前类中所有的方法,判断方法是否有Subscribe注解,并且符合要求,要求和在编译库中的是一样的,这里就不再重复说明了。
刚才是如果忽略了编译生成的文件,获取Class中被注解的方法,下面再看看正常情况如果得到被注解的方法的,打开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);
}
/**
* 获取被注解的方法的信息
* */
private SubscriberInfo getSubscriberInfo(FindState findState) {
// 如果查找类本身有注解相关的信息,因为是异步的,有可能这个类还是被回收
if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) {
SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();
// 判断现在要查找的class 和 之前查找的class是否相同,如果相同直接返回结果
if (findState.clazz == superclassInfo.getSubscriberClass()) {
return superclassInfo;
}
}
// 这里可以通过Builder来设置subscriberInfoIndexes,添加我们生成的类
// 然后就可以直接从生产的类中直接查找信息
if (subscriberInfoIndexes != null) {
for (SubscriberInfoIndex index : subscriberInfoIndexes) {
SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
if (info != null) {
return info;
}
}
}
return null;
}
如果是正常的使用,首先会从查找类中查询结果,例如之前查询过这个Class并且结果还没有被回收,然后再从生成的编译文件中获取,如果还是没有,只能通过反射再去查询一遍,确保没有被漏掉的被注解方法。
getMethodsAndRelease方法就不用说明了,他就是获取FindState中查询出来的List而已。
到此为止,获取注册对象中被注解的方法就到此结束了,我们又需要回到注册方法,看看对于每一个被注解的方法,EventBus都做了哪些处理,打开subscribe()方法:
/**
* 把注册的类和对应的被注解的方法保存起来
* */
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
// 得到对应的Event,也就是参数的类型
Class> eventType = subscriberMethod.eventType;
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
CopyOnWriteArrayList subscriptions = subscriptionsByEventType.get(eventType);
// 增加了一个判空,添加在到subscriptionsByEventType中
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);
}
}
// 遍历对应的subscriptions
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;
}
}
// 这里把注册的类保存了起来,最终还要从typesBySubscriber解绑
List> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
// 把绑定的EventType放入list中
subscribedEvents.add(eventType);
// 判断是否是StickyEvent,带有上一次缓存的广播
if (subscriberMethod.sticky) {
// 如果StickyEvent可以被继承
if (eventInheritance) {
// 找到StickyEvent的Set集合
Set, Object>> entries = stickyEvents.entrySet();
// 遍历集合中的元素
for (Map.Entry, Object> entry : entries) {
// 得到注册StickyEvent的Class
Class> candidateEventType = entry.getKey();
// 判断是否是集成关系
if (eventType.isAssignableFrom(candidateEventType)) {
// 得到Class对应的Event
Object stickyEvent = entry.getValue();
// 检查是否是要给当前注册的类,返回StickyEvent
// 也就是注册了之后,仍然可以得到之前缓存的StickyEvent
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
代码有些长,其实整体的操作可以分为三个部分:
1、根据Event的类型,保存对应的Class和被注解的方法的列表,并且根据优先级排序,这样优先级高的,就能先得到Event。
2、把注册的对象的Class和被注解的方法保存起来,这样方便解绑时的操作。
3、终于看到了StickyEvent了,如果是StickyEvent,从StickyEvent中的缓存中找到对应的缓存,响应给正在注册的对象。
这样注册的流程就结束了,中间我忽略了一些方法的介绍,但是我都加了注释,而且内部的实现是很简单,要不这篇博客真的是越写越长了。
最后看一下解绑:
/**
* 解绑注册
* */
public synchronized void unregister(Object subscriber) {
// 获取对象的被注解的方法
List> subscribedTypes = typesBySubscriber.get(subscriber);
if (subscribedTypes != null) {
// 先循环移除绑定的Event
for (Class> eventType : subscribedTypes) {
unsubscribeByEventType(subscriber, eventType);
}
// 解绑
typesBySubscriber.remove(subscriber);
} else {
Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
/**
* 移除对象绑定的EventType
* */
private void unsubscribeByEventType(Object subscriber, Class> eventType) {
List 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--;
}
}
}
}
其实是和注册相反的操作,先把被注解的方法一个个的移除,最后移除这个Class,但是中间少了很多的判断等等的操作,就变成了短短几行。
总结#
我们刚刚分析EventBus整体的流程,有了注释代码的一步步操作就清晰多了,但是为了减少篇幅,有些方法没有给大家介绍,但是不要着急,这些方法中我都添加了注释,保证大家能够看明白,包括之后要分析到的Post Event的过程的注释,都已经上传到了github中,大家可以去看更多的分析注释。
添加了注释的EventBus源码地址