订阅函数查找
当一个对象调用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