• 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

  • 相关阅读:
     selenium webdriver test
    V8 初次接触(Qt5) 1+1=2 博客频道 CSDN.NET
    C++11 FAQ中文版
    做技术的,因为年龄和颈椎问题,想逐渐脱离码农状态,大家对3035岁职业规划有什么好的建议? 知乎
    一些idea
    如何来区分是我写的还是我转载的,
    firecurl
    python为什么叫好不叫座
    QTextCodec中的setCodecForTr等终于消失了 (Qt5) 1+1=2 博客频道 CSDN.NET
    Charles Web Debugging Proxy • HTTP Monitor / HTTP Proxy / HTTPS & SSL Proxy / Reverse Proxy
  • 原文地址:https://www.cnblogs.com/moderate-fish/p/7687656.html
Copyright © 2020-2023  润新知