EventBus 普通事件register

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xiey94/article/details/82936556

EventBus 普通事件register

public void register(Object subscriber) {
        Class<?> subscriberClass = subscriber.getClass();
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        synchronized (this) {
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);
            }
        }
    }

注册方法好理解,先是找到注册者的Class,用作索引,去subscriberMethodFinder.findSubscriberMethods中寻找当前订阅者订阅的所有消息;
然后对这些订阅消息进行循环订阅;

那就先看看寻找订阅者所有订阅方法的subscriberMethodFinder.findSubscriberMethods方法

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
        List<SubscriberMethod> 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是一个Map,相当于一个缓存池,将所有的订阅方法以订阅者作为Key,订阅者中的所有订阅方法装载的集合作为Value,这样根据订阅者就可以直接找到订阅者中所有的订阅方法了;
如果订阅方法存在则直接返回,该方法结束,但是如果是第一次订阅进来,肯定是空的,就看第二个条件;这个是是否忽略注解器索引,默认是false;这里的注解器索引也是一个知识点,先放过;也就是我们默认执行

subscriberMethods = findUsingInfo(subscriberClass);

到最后如果还是空的,则抛出异常,因为订阅者中必须要有订阅方法;否则就将找到的订阅方法放到缓存池中,然后返回找到的方法;

private List<SubscriberMethod> 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 findState = prepareFindState();
findState.initForSubscriber(subscriberClass);

FindState看名字翻译就是一个寻找状态,主要就是在寻找订阅方法的过程中记录一些状态信息;
这两行是对订阅者寻找状态的一个初始化;

private FindState prepareFindState() {
        synchronized (FIND_STATE_POOL) {
            for (int i = 0; i < POOL_SIZE; i++) {
                FindState state = FIND_STATE_POOL[i];
                if (state != null) {
                    FIND_STATE_POOL[i] = null;
                    return state;
                }
            }
        }
        return new FindState();
    }
void initForSubscriber(Class<?> subscriberClass) {
            this.subscriberClass = clazz = subscriberClass;
            skipSuperClasses = false;
            subscriberInfo = null;
        }

内部维持了一个大小为4的状态池(为什么是4,初始化的时候就是4),循环状态池,只要有不为空的,就把第一个不为空的返回回来直接使用(省了创建对象的内存开销),然后把池子里的那个拿出来的位置空下来;如果池子都是空的,那就新建一个FindState对象,返回使用;同时指定一下,里面的clazz指向当前订阅者,默认不跳过父类(追寻订阅方法,可以一直往上追寻),subscriberInfo默认是null;

然后在当前状态的clazz不为空的情况下继续往下(clazz已经指向了当前订阅者,当然不为空了);获取subscriberInfo(这个也默认是null了);

private SubscriberInfo getSubscriberInfo(FindState findState) {
        if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) {
            SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();
            if (findState.clazz == superclassInfo.getSubscriberClass()) {
                return superclassInfo;
            }
        }
        if (subscriberInfoIndexes != null) {
            for (SubscriberInfoIndex index : subscriberInfoIndexes) {
                SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
                if (info != null) {
                    return info;
                }
            }
        }
        return null;
    }

第一个if条件我们默认是null,第二个我看了一下他的来龙去脉,唯一初始化的地方就是在创建SubscriberMethodFinder对象的时候,这个肯定是创建了(因为我们现在就位于SubscriberMethodFinder里面啊),那什么时候创建SubscriberMethodFinder对象呢,在EventBus的带参构造方法中,但是我们的EventBus是单例,默认是调用无参构造,无参构造里面调用的有参数构造,只是创建了一个EventBusBuilder对象传进来,却没有对里面的参数进行赋值,会不会EventBusBuilder里面默认创建该对象呢,去看看;没有,

public EventBusBuilder addIndex(SubscriberInfoIndex index) {
        if (subscriberInfoIndexes == null) {
            subscriberInfoIndexes = new ArrayList<>();
        }
        subscriberInfoIndexes.add(index);
        return this;
    }

我们没有对Builder设置过值,也就没有调用过这个方法,所以,他是null;

这里就可以看出来,上面的获取subscriberInfo还是null;既然为null,那我们就去调用

findUsingReflectionInSingleClass(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();
            if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
                Class<?>[] parameterTypes = method.getParameterTypes();
                if (parameterTypes.length == 1) {
                    Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                    if (subscribeAnnotation != null) {
                        Class<?> eventType = parameterTypes[0];
                        if (findState.checkAdd(method, eventType)) {
                            ThreadMode threadMode = subscribeAnnotation.threadMode();
                            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");
            }
        }
    }
methods = findState.clazz.getDeclaredMethods();

找到订阅者中的所有方法;然后对所有方法开始遍历筛选(筛选条件:是public操作符;参数只有一个;要有Subscribe 注解;)满足这些条件才算是订阅方法;然后判断是否添加到统计集合中来;如果可以添加,则将参数值进行包装,包装成SubscriberMethod,然后添加到寻找状态的统计集合中来;
看看是如何检查是否满足添加条件的

boolean checkAdd(Method method, Class<?> eventType) {
            // 2 level check: 1st level with event type only (fast), 2nd level with complete signature when required.
            // Usually a subscriber doesn't have methods listening to the same event type.
            Object existing = anyMethodByEventType.put(eventType, method);
            if (existing == null) {
                return true;
            } else {
                if (existing instanceof Method) {
                    if (!checkAddWithMethodSignature((Method) existing, eventType)) {
                        // Paranoia check
                        throw new IllegalStateException();
                    }
                    // Put any non-Method object to "consume" the existing Method
                    anyMethodByEventType.put(eventType, this);
                }
                return checkAddWithMethodSignature(method, eventType);
            }
        }
private boolean checkAddWithMethodSignature(Method method, Class<?> eventType) {
            methodKeyBuilder.setLength(0);
            methodKeyBuilder.append(method.getName());
            methodKeyBuilder.append('>').append(eventType.getName());

            String methodKey = methodKeyBuilder.toString();
            Class<?> methodClass = method.getDeclaringClass();
            Class<?> methodClassOld = subscriberClassByMethodKey.put(methodKey, methodClass);
            if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) {
                // Only add if not already found in a sub class
                return true;
            } else {
                // Revert the put, old class is further down the class hierarchy
                subscriberClassByMethodKey.put(methodKey, methodClassOld);
                return false;
            }
        }

将方法传进来,将参数类型传进来;

anyMethodByEventType是一个集合,里面用参数类型来作为Key,方法作为Value;不管什么方法,一进来就添加到anyMethodByEventType中;(Tips:集合的put的方法,如果之前有相同key的,就把之前对应的value返回,同时重置可以对应的value,如果没有这个key的,则直接添加到集合中并返回null);

将订阅方法添加到集合中,同时返回是否已存在的状态值;如果是null,说明是不存在相同参数类型的方法,直接返回true;如果存在相同参数类型的方法,先判断他是否是方法(Method)类型,因为我们的value正常情况下放的都是Method,是的话则进行签名检查,并将相同参数类型之前的方法和参数类型传进来;

methodKey是一个组装值,将方法和参数组装表示唯一作为Key;

subscriberClassByMethodKey是一个Map,里面全部是组装值作为Key,订阅者作为Value;上来就是将组装值和对应的订阅者放进集合中,判断是否存在过同名同参方法;一个订阅者中是不可能存在同名同参的情况的(谁也不允许啊),所以返回true;

这时候anyMethodByEventType中的相同Key的Value被替换为了SubscriberMethodFinder对象(我现在就在里面啊,也就是当前类对象);

if (existing instanceof Method) 

这个条件下次就发挥作用了

然后再次进行签名检查,不过参数变了,上一次是传入之前的方法,这一次是传入要覆盖的方法;同理只要不是同名同参方法就肯定返回true;

按道理讲,这里就结束了,我们该到下一步分析findUsingInfo中的findState.moveToSuperclass()方法了;但是暂停一下,因为我本人有个小疑惑;那就是

anyMethodByEventType.put(eventType, this);

这一行代码起到了什么作用?
加入我们第三次的订阅方法,仍然是相同参数类型的方法(当然,现实中没人会这没做,会被打的),按照流程
anyMethodByEventType中此时参数类型的Key对应的Value是this;那就返回的existing不是null,在else中也不是Method类型了,那就直接执行最后的检查签名方法;签名方法有个特点,只要不是同名同参方法就返回true;那这里就返回true;结果也就出来了,anyMethodByEventType是用来做标记的,省的两次检查浪费;

但,,,真的是这样吗?

再看看第四次,因为每次进来都是把参数往anyMethodByEventType里面扔,所以第三次进来的时候该参数类型对应的value 已经从this变成了第三次进来的方法;

连续几个同参不同名的订阅方法过来,anyMethodByEventType的变化

次数 参数类型Key对应的值(假设是String) 对应的Value值() put的返回值 处理结果
第一次 String event1 null 直接返回true
第二次 String event2 event1 执行两道检查;又执行了一次put,把this扔进来了
第二次 String this
第三次 String event3 this 执行最后一道签名检查
第四次 String event4 event3 执行两道检查;又执行了一次put,把this扔进来了
第四次 String this
第五次 String event5 this 执行最后一道签名检查
第六次 String event6 event5 执行两道检查;又执行了一次put,把this扔进来了
第六次 String this

看到这里,相信你也看出来了,这行代码没什么用啊,因为每次进来anyMethodByEventType都要先put一下,value值一直是变化的,也就不能用于标记了,那这行代码到底是用来干嘛的(一次能进,一次不能进,分流?),

那假设我们注释掉这行代码:

次数 参数类型Key对应的值(假设是String) 对应的Value值() put的返回值 处理结果
第一次 String event1 null 直接返回true
第二次 String event2 event1 执行两道检查,返回true
第三次 String event3 event2 执行两道检查,返回true
第四次 String event4 event3 执行两道检查,返回true
第五次 String event5 event4 执行两道检查,返回true
第六次 String event6 event5 执行两道检查,返回true

这么看来也无不可;就目前来看感觉只需要一个签名检查就够了;难道是因为代码没看完,后面有特殊用意?那这块我们先放放,等看完再说;

findState.moveToSuperclass();
void moveToSuperclass() {
            if (skipSuperClasses) {
                clazz = null;
            } else {
                clazz = clazz.getSuperclass();
                String clazzName = clazz.getName();
                /** Skip system classes, this just degrades performance. */
                if (clazzName.startsWith("java.") || clazzName.startsWith("javax.") || clazzName.startsWith("android.")) {
                    clazz = null;
                }
            }
        }

这个是干嘛的呢?就是是否往上追溯,对于该订阅者,当前订阅方法找完之后,是否要去父类中接着找订阅方法,默认是不跳过,接着找;这时clazz的指向就指向了订阅者的父类;然后在这个追溯过程中,如果追到头了,也就是追到了父类是系统方法,那也就真到头了,可以跳出追溯了;

既然是追溯到了父类,那我们再走一遍之前的流程,看能不能解答遗留的问题;

仍然是subscriberInfo=null;执行findUsingReflectionInSingleClass(findState);

里面仍然是筛选找到符合预添加的订阅方法;又到了if (findState.checkAdd(method, eventType));

这里就有的看了,因为是父类,存在子类继承,方法覆写的可能;也就会遇到同名同参方法;也就是anyMethodByEventType中有可能存在Key和Value与即将put进去的完全一样;

开始推论:
假设:子类(event1(String),event2(String))、父类(event1(String),event3(String))

如果是参数类型不同的,最快速,直接添加;反之参数类型相同的,就要着重考虑了;

追溯到了父类;event1入场了,返回的就是子类的event1,然后进入第一道签名检查,是直接返回true的(为啥?methodClassOld.isAssignableFrom(methodClass),methodClassOld=methodClass,看下面解释),进入第二道签名检查,

if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass))

该方法的第二个条件被打回(Tips:A.isAssignableFrom(B),A是B本身或者A是B的父类或者更高返回true,就是A>=B返回true)

因为第一道签名检查的时候subscriberClassByMethodKey中对应的Value是子类的class,第二次put进去父类的class,返回子类的class,methodClassOld是子类class,methodClass是父类class,所以条件不成立,在else中又将subscriberClassByMethodKey中对应的Value还原了回去(又是子类的Class),并返回false;这里就得出一个结论:

父类子类中都有的同名同参订阅方法,只执行子类的,父类忽略

但是没看到有用到那个我们遗留问题的用意啊!

转过来一想,是不是我太刻薄了…

或许正常逻辑下订阅者以及往上追溯父类出现同参就会只有一种情况:父类和子类中都有该参数类型的方法,但方法名不同;其他的情况就不存在了;因为一个订阅者中没必要出现两个同参订阅方法;

而作者这么做的用意,就是大部分都是使用

Object existing = anyMethodByEventType.put(eventType, method);
            if (existing == null) {
                return true;
            }

就足够了;而父类中出现同参不同的方法才用到else中的检查签名的判断,或者再往上父类的父类…,这样一般不会出现,因为完全可以在父类中定义一个方法,子类订阅方法响应的时候,调用这个覆写方法(覆写方法里面再调用super.父类方法)就行了;

接着往下走:

return getMethodsAndRelease(findState);
private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {
        List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods);
        findState.recycle();
        synchronized (FIND_STATE_POOL) {
            for (int i = 0; i < POOL_SIZE; i++) {
                if (FIND_STATE_POOL[i] == null) {
                    FIND_STATE_POOL[i] = findState;
                    break;
                }
            }
        }
        return subscriberMethods;
    }

把寻找状态对象中收集的订阅方法返回;同时释放寻找状态中的值;同时将寻找状态对象扔到状态池中,免于对象创建的内存开销;

绕了好大一圈,终于开始循环订阅了:

synchronized (this) {
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);
            }
        }
// Must be called in synchronized block
    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        Class<?> eventType = subscriberMethod.eventType;
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        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);
            }
        }

        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);

        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<Class>).
                Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
                for (Map.Entry<Class<?>, 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);
            }
        }
    }

先获取订阅方法的参数类型;然后将订阅者和订阅方法包装成Subscription(把他叫订阅者方法好不好);
subscriptionsByEventType是一个Map,里面用参数类型做key,同一参数类型的订阅者方法装载的集合作为Value(这个不只是针对当前订阅者,而是针对整个APP的);
拿到同一参数类型的订阅者方法的List集合后,检查如果已经订阅过了,则抛出异常提醒;没订阅就按照优先级添加到集合中;
typesBySubscriber是一个Map(好多Map啊),用订阅者作为key,订阅者中的所有参数类型装载的List作为Value,把参数类型添加进去;
这样注册就结束了;

当然后面还有一段代码:

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<Class>).
                Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
                for (Map.Entry<Class<?>, 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);
            }
        }

这段代码就不详细解释了,他是属于黏性事件,发送的黏性事件随着订阅后就响应,所以紧跟在订阅后面,这里我们分析的是普通事件的register,就先不说这个了;

还有对于那个遗留问题(双检查事件),我还是有点不甘心,谁能睡服我一下?

遗留问题:
双检查真的有作用吗?


猜你喜欢

转载自blog.csdn.net/xiey94/article/details/82936556