EventBus源码浅析一

作者: GitCode8 | 来源:发表于2019-03-07 15:34 被阅读10次

EventBus使用很简单,了解它的源码的实现也是应该的,不但学习设计者的思想,也可以提高自己的能力。

注册

EventBus.getDefault().register(this);

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

可见,EventBus使用注解和反射机制。查看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;
        }
    }
  1. 通过METHOD_CACHE获取是否有缓存该类的所有方法,有缓存则直接返回一个List<SubscriberMethod>METHOD_CACHE是一个ConcurrentHashMap,以订阅类的Class为key,保存所有的订阅方法List<SubscriberMethod>为value。
  2. 通过跟踪代码,ignoreGeneratedIndex默认值为false,所有这里执行findUsingInfo方法。
 private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
       //finaState保留订阅类的相关信息
        FindState findState = prepareFindState();
        //findState的初始化
        findState.initForSubscriber(subscriberClass);
        //由于上一行的初始化,clazz!=null
        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类主要保留注册类的相关信息,在prepareFindStateinitForSubscriber,初始化后,findSate.clazz并不为空,findSate指向当前的订阅类的class。查看getSubscriberInfo
,此时返回值是null。在前面初始化中,findState.subscriberInfo的值是null,为此查看findUsingReflectionInSingleClass

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

  1. 这里通过反射机制查看订阅类是否有订阅方法。
  2. 符合订阅方法规则。
  • 订阅方法必须public修饰,不能是静态方法抽象方法。否则会抛出错误。
  • 参数只有一个的。
  • 使用Subscribe注解的
  1. 在符合第二个要求后,通过checkAdd方法检测订阅方法是否已存在。如果不存在则封装成SubscriberMethod保存在findState.subscriberMethods

在这里,我们查看一下FindState类的初始化和checkAdd方法。

        final List<SubscriberMethod> subscriberMethods = new ArrayList<>();
        final Map<Class, Object> anyMethodByEventType = new HashMap<>();
        final Map<String, Class> subscriberClassByMethodKey = new HashMap<>();
        final StringBuilder methodKeyBuilder = new StringBuilder(128);

        Class<?> subscriberClass;
        Class<?> clazz;
        boolean skipSuperClasses;
        SubscriberInfo subscriberInfo;

        void initForSubscriber(Class<?> subscriberClass) {
            this.subscriberClass = clazz = subscriberClass;
            skipSuperClasses = false;
            subscriberInfo = null;
        }

        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);
           //检测方法名是否相同
          //检测methodClassOld是否是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;
            }
        }
  • subscriberMethods用于保存所有订阅方法。anyMethodByEventType以事件类型EventType为key,保存方法MethodsubscriberClassByMethodKeymethod.getName()>eventType.getName()为key,保存methodClass
  • checkAdd的第一步是检查该事件类型的参数的方法是否已经存在,如果不存在,直接返回true添加,添加该订阅方法。第二步是,但存在该类型方法时,可能存在方法名不同或者方法名相同父类被子类重写。此时调用checkAddWithMethodSignature方法进行检查。
  • checkAddWithMethodSignature方法中,当methodClassOld==null,表示方法名不同,那么直接添加订阅方法。methodClassOld.isAssignableFrom(methodClass)表示methodClassOld是否methodClass的父类或者父接口。如果是的化,也直接添加。

这里的双重检查,判断该订阅方法是否已存在:

  1. 与已有的订阅方法参数类型是否相同,不相同,则直接添加。相同则下一步。
  2. 检测方法名是否相同,不相同则添加。相同则检查已有的订阅方法的类是否是订阅类的父类或父接口。
    主要是查找所有的订阅方法,并封装为SubscriberMethod

回到查到订阅方法的开始。


    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,以便下次查找订阅方法直接返回。

到这里整个查找订阅方法结束了

接着回到最开始的方法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);
            }
        }
    }

可见,通过subscribe方法将订阅者和其所有的订阅方法关联在一起。

 private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        Class<?> eventType = subscriberMethod.eventType;
        //Subscription保存着订阅者和订阅方法的信息和状态
        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);
            }
        }

        //根据事件的优先权,顺序添加到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;
            }
        }

        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,并以事件类型为key保存到subscriptionsByEventType中。
  • 如果subscriptionsByEventType已有订阅信息,会抛出异常。
  • 如果订阅方法是粘性的,则会收到该类型的最后一条粘性广播。

到这里,EventBus的注册过程基本就结束了。

整个流程总结:

1、 寻订阅类是否有订阅方法,是否符合要求
2、 将订阅方法相关信息封装成SubscriberMethod,并把所有订阅方法存在List和缓存到METHOD_CACHE中。
3 、将订阅者和订阅方法关联起来,封装成Subscription。并保存在subscriptionsByEventType
4、 如果是粘性事件,接收最后一条广播。

通过注册流程中,我们应该也能猜想EventBus的工作原理了。当我们post一个eventType时,去读取subscriptionsByEventType中该类型的相关订阅信息,然后达到调用我们订阅方法。也就是说明为什么EventBus是属于发布-订阅模式。

分析下来,涉及到了单例模式,建造者模式,观察者模式。

相关文章

网友评论

    本文标题:EventBus源码浅析一

    本文链接:https://www.haomeiwen.com/subject/rvfxpqtx.html