EventBus3.0源码学习(3) SubscriberMethodFinder

订阅函数查找

当一个对象调用register向EventBus注册时,查找对象中所有接收订阅事件的函数被封装在SubscriberMethodFinder类中。findSubscriberMethods的实现如下:

 1 List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
 2     List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
 3     if (subscriberMethods != null) {
 4         return subscriberMethods;
 5     }
 6 
 7     if (ignoreGeneratedIndex) {
 8         subscriberMethods = findUsingReflection(subscriberClass);
 9     } else {
10         subscriberMethods = findUsingInfo(subscriberClass);
11     }
12     if (subscriberMethods.isEmpty()) {
13         throw new EventBusException("Subscriber " + subscriberClass
14                 + " and its super classes have no public methods with the @Subscribe annotation");
15     } else {
16         METHOD_CACHE.put(subscriberClass, subscriberMethods);
17         return subscriberMethods;
18     }
19 }

若ignoreGeneratedIndex为true,则调用findUsingReflection进行查找,否则调用findUsingInfo进行查找。EvenBus提供了额外的注解处理器,能在编译期完成订阅函数查找以提升性能。但此处我们只关注反射查找。findUsingReflection的实现如下:

1 private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
2     FindState findState = prepareFindState();
3     findState.initForSubscriber(subscriberClass);
4     while (findState.clazz != null) {
5         findUsingReflectionInSingleClass(findState);
6         findState.moveToSuperclass();
7     }
8     return getMethodsAndRelease(findState);
9 }

第4-5行,遍历subscriberClass的超类体系,调用findUsingReflectionInSingleClass查找当前clazz的所有订阅函数。findUsingReflectionInSingleClass的代码如下:

 1 private void findUsingReflectionInSingleClass(FindState findState) {
 2     Method[] methods;
 3     try {
 4         // This is faster than getMethods, especially when subscribers are fat classes like Activities
 5         methods = findState.clazz.getDeclaredMethods();
 6     } catch (Throwable th) {
 7         // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
 8         methods = findState.clazz.getMethods();
 9         findState.skipSuperClasses = true;
10     }
11     for (Method method : methods) {
12         int modifiers = method.getModifiers();
13         if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
14             Class<?>[] parameterTypes = method.getParameterTypes();
15             if (parameterTypes.length == 1) {
16                 Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
17                 if (subscribeAnnotation != null) {
18                     Class<?> eventType = parameterTypes[0];
19                     if (findState.checkAdd(method, eventType)) {
20                         ThreadMode threadMode = subscribeAnnotation.threadMode();
21                         findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
22                                 subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
23                     }
24                 }
25             } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
26                 String methodName = method.getDeclaringClass().getName() + "." + method.getName();
27                 throw new EventBusException("@Subscribe method " + methodName +
28                         "must have exactly 1 parameter but has " + parameterTypes.length);
29             }
30         } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
31             String methodName = method.getDeclaringClass().getName() + "." + method.getName();
32             throw new EventBusException(methodName +
33                     " is a illegal @Subscribe method: must be public, non-static, and non-abstract");
34         }
35     }
36 }

第3-10行,clazz.getDeclaredMethods()只返回当前clazz中声明的函数,而clazz.getMethods()将返回clazz的所有函数(包括继承自父类和接口的函数),因此,此时skipSuperClasses被置为true,阻止递归查找父类。

第13行,只考虑public且非static、abstract、synthetic、bridge的方法(bridge方法主要是范型实现中用到(参见[1]))。第15行,函数有且只有一个参数。第16行,函数被@Subscribe注解修饰。第18-23行,将函数加入subscriberMethods集合。findState.checkAdd用于判断是否需要将当前method加入,其实现如下:

 1 static class FindState {
 2     boolean checkAdd(Method method, Class<?> eventType) {
 3         // 2 level check: 1st level with event type only (fast), 2nd level with complete signature when required.
 4         // Usually a subscriber doesn't have methods listening to the same event type.
 5         Object existing = anyMethodByEventType.put(eventType, method);
 6         if (existing == null) {
 7             return true;
 8         } else {
 9             if (existing instanceof Method) {
10                 if (!checkAddWithMethodSignature((Method) existing, eventType)) {
11                     // Paranoia check
12                     throw new IllegalStateException();
13                 }
14                 // Put any non-Method object to "consume" the existing Method
15                 anyMethodByEventType.put(eventType, this);
16             }
17             return checkAddWithMethodSignature(method, eventType);
18         }
19     }
20 
21     private boolean checkAddWithMethodSignature(Method method, Class<?> eventType) {
22         methodKeyBuilder.setLength(0);
23         methodKeyBuilder.append(method.getName());
24         methodKeyBuilder.append('>').append(eventType.getName());
25 
26         String methodKey = methodKeyBuilder.toString();
27         Class<?> methodClass = method.getDeclaringClass();
28         Class<?> methodClassOld = subscriberClassByMethodKey.put(methodKey, methodClass);
29         if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) {
30             // Only add if not already found in a sub class
31             return true;
32         } else {
33             // Revert the put, old class is further down the class hierarchy
34             subscriberClassByMethodKey.put(methodKey, methodClassOld);
35             return false;
36         }
37     }
38 }

第5行,anyMethodByEventType存储<eventType, method>映射关系,若existing为空,则表示eventType第一次出现。一般情况下,一个对象只会有一个订阅函数处理特定eventType。

第9-17行,处理一个对象有多个订阅函数处理eventType的情况,此时,anyMethodByEventType中eventType被映射到一个非Method对象(即this)。

第22-24行,由于存在多个订阅函数处理eventType,此时,单纯使用eventType作为key已经无法满足要求了,因此,使用method.getName() + ">" + eventType.getName()作为methodKey,并使用subscriberClassByMethodKey存储<methodKey, methodClass>的映射关系。

第28-36行,如果methodClassOld或者methodClass是methodClassOld的子类,则将<methodKey, methodClass>放入,否则不放入。满足函数名相同、参数类型相同且被@Subscribe修饰的函数,在一个类中不可能存在两个;考虑类继承体系,若这样的两个函数分别来自父类和子类,则最终被加入的是子类的函数。

函数再过头来看,第10行,调用checkAddWithMethodSigature加入<existing, eventTpye>,第12行的throw理论上是不可能执行到的,第17行,调用checkAddWithMethodSigature加入<method, eventTpye>。

[参考文献]

[1] https://stackoverflow.com/questions/5007357/java-generics-bridge-method

 

posted on 2017-10-19 16:04  游不动の鱼  阅读(514)  评论(0编辑  收藏  举报