EventBus 源码分析

作者: 桑小年 | 来源:发表于2019-03-07 17:47 被阅读13次

    这篇文章主要是根据我们平时的使用,一步一步的分析EventBus源码流程,因此分为三步:

    1、注册订阅者
    2、事件发布
    3、反注册订阅者

    1、register 注册订阅者

    在使用eventBus的时候,第一个步骤就是注册订阅者

      EventBus.getDefault().register(this);
    

    getDefault方法是一个单例模式的初始化方法,主要就是获取一个实例,源码如下:

       public static EventBus getDefault() {
            EventBus instance = defaultInstance;
            if (instance == null) {
                synchronized (EventBus.class) {
                    instance = EventBus.defaultInstance;
                    if (instance == null) {
                        instance = EventBus.defaultInstance = new EventBus();
                    }
                }
            }
            return instance;
        }
    

    可以很清楚的看到,这里只是一个DoubleCheck的单例模式,接下来直接看一下构造方法:

      /**
         * Creates a new EventBus instance; each instance is a separate scope in which events are delivered. To use a
         * central bus, consider {@link #getDefault()}.
         */
        public EventBus() {
            this(DEFAULT_BUILDER);
        }
    
        EventBus(EventBusBuilder builder) {
            logger = builder.getLogger();
            subscriptionsByEventType = new HashMap<>();
            typesBySubscriber = new HashMap<>();
            stickyEvents = new ConcurrentHashMap<>();
            mainThreadSupport = builder.getMainThreadSupport();
            mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
            backgroundPoster = new BackgroundPoster(this);
            asyncPoster = new AsyncPoster(this);
            indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
          //
            subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
                    builder.strictMethodVerification, builder.ignoreGeneratedIndex);
            logSubscriberExceptions = builder.logSubscriberExceptions;
            logNoSubscriberMessages = builder.logNoSubscriberMessages;
            sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
            sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
            throwSubscriberException = builder.throwSubscriberException;
            eventInheritance = builder.eventInheritance;
            executorService = builder.executorService;
        }
    
    

    构造方法里面是通过一个Builder模式来对EventBus各项配置进行初始化
    在getDefault获取到实例之后,就会调用register方法进行注册,这时候进入register方法看一下注册过程:

       public void register(Object subscriber) {
            Class<?> subscriberClass = subscriber.getClass();
            //获取到订阅者中所有的订阅方法,并储存到list集合中
            List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
            synchronized (this) {
                for (SubscriberMethod subscriberMethod : subscriberMethods) {
                    subscribe(subscriber, subscriberMethod);
                }
            }
        }
    

    从源码里面看,注册过程只有两步:

    a、根据注册时传入的订阅者对象,找到所有的订阅方法;
    b、订阅所有的订阅方法

    a1)、
    首先看一下如何查找到订阅者中所有的订阅方法,即findSubscriberMethods()方法,subscriberMethodFinder是在EventBus构造方法中进行的初始化,进入findSubscriberMethods方法中进行查看:

            List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
            //METHOD_CACHE 一个map集合,以订阅者为key,以订阅方法的list集合为value进行缓存
            List<SubscriberMethod> subscriberMethods =  METHOD_CACHE.get(subscriberClass);
            //如果当前的对象已经被缓存,则直接获取返回
            if (subscriberMethods != null) {
                return subscriberMethods;
            }
         if (ignoreGeneratedIndex) {
               // 如果忽略索引,就根据反射来获取
                subscriberMethods = findUsingReflection(subscriberClass);
            } else {
                //否则使用索引
                subscriberMethods = findUsingInfo(subscriberClass);
            }
          //如果该订阅者中没有订阅方法,此处会抛出一个异常,提醒你该订阅者和他的父类里面都没有订阅方法(public 修饰并且@Subscribe 进行注解)
            if (subscriberMethods.isEmpty()) {
                throw new EventBusException("Subscriber " + subscriberClass
                        + " and its super classes have no public methods with the @Subscribe annotation");
            } else {
              //如果有订阅方法,则把该订阅者和订阅方法进行缓存,并返回订阅方法的list集合
                METHOD_CACHE.put(subscriberClass, subscriberMethods);
                return subscriberMethods;
            }
        }
    

    a1.1)、
    findSubscriberMethods 里面的逻辑已经很清晰了,现在看一下 findUsingInfo()和findUsingReflection(),一般使用的时候,并没有自定义进行配置,所以一般都是使用索引进行查找

    a1.1.1)
    这里就先分析一下findUsingInfo()方法,反射的稍后再看:

    //忽略索引时,查找方法
     private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
            // 创建并初始化FindState对象 FindState是订阅者的一个辅助类,用于获取到订阅方法
            FindState findState = prepareFindState();
            //关联订阅者
            findState.initForSubscriber(subscriberClass);
           //此处通过循环,获取到订阅者父类中的订阅方法
            while (findState.clazz != null) {
                //获取到订阅者的信息
                findState.subscriberInfo = getSubscriberInfo(findState);
                if (findState.subscriberInfo != null) {
                    //如果可以找到订阅者信息,将订阅方法缓存到 subscriberMethods 中
                    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类是一个辅助类,用来辅助订阅者获取订阅方法,代码比较简单,篇幅原因此处就不在进行源码解析,只要清楚这个类主要用途即可
    prepareFindState() 方法则是用来创建并初始化FindState的方法,同时坐着也通过一个缓存池进行了优化
    从上面源码中可以看到,在可以直接查找到订阅信息的情况比较简单,直接将查找到的信息进行缓存即可,下面分析一下找不到订阅信息的时候,通过反射方法进行查找:

    //通过反射方式,获取到订阅者中所有的订阅方法
        private void findUsingReflectionInSingleClass(FindState findState) {
            // 1 获取订阅者内所有方法
            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;
            }
            // 2 遍历所有方法,找到所有的订阅方法,并将所有的订阅方法进行缓存到 findState 中
            for (Method method : methods) {
                int modifiers = method.getModifiers();
                //如果是非静态、非抽象、public 修饰的
                if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
                    Class<?>[] parameterTypes = method.getParameterTypes();
                    //只有一个参数
                    if (parameterTypes.length == 1) {
                        Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                        // Subscribe 进行注解
                        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");
                }
            }
        }
    
    

    认真看一下每一个步骤,可以发现其实就是通过反射查找到符合条件的订阅方法,然后将订阅方法缓存到findState中,同时对注解的订阅方法进行校验,给出提示,在查找到所有的订阅方法之后,代码执行到了,a1.1.1) findUsingReflectionInSingleClass 执行完毕,然后 findUsingInfo()在循环查找并缓存订阅者中所有的订阅方法,至此, 在a1.1) findUsingInfo 方法也执行完毕,成功找到了所有的订阅方法,a1)、中忽略索引的情况已经执行完毕
    查找完成之后,我们在回过头看一下a1)、findSubscriberMethods 方法中不忽略 索引的方法 findUsingReflection()

     FindState findState = prepareFindState();
            findState.initForSubscriber(subscriberClass);
            while (findState.clazz != null) {
                findUsingReflectionInSingleClass(findState);
                findState.moveToSuperclass();
            }
            return getMethodsAndRelease(findState);
    

    这段代码就非常简单了,上面已经分析过了,直接使用反射查找到所有的方法,并进行缓存;
    在查找到所有的订阅方法之后,将所有的方法进行缓存到 METHOD_CACHE ,至此,注册第一步就完成了;
    无论使用哪一种方法,到这里都已经找到所有的订阅方法了,在再次返回到 register()方法中:

      synchronized (this) {
                for (SubscriberMethod subscriberMethod : subscriberMethods) {
                    subscribe(subscriber, subscriberMethod);
                }
            }
    

    这段代码通过循环,将所有的订阅方法和订阅者关联起来,看一下subscribe 方法

      private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
            Class<?> eventType = subscriberMethod.eventType;
            //创建订阅者和订阅方法的封装对象
            Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
            //由于所有的订阅方法都只有一个参数,并且subscriptionsByEventType 是以 订阅方法的参数的class为key,Subscription集合为value的
    //      //的map集合,所以,根据eventType 可以找到所有的含有此参数的订阅方法所在的订阅者
            // 根据订阅方法的参数类型,查找到所有包含有该Event 的订阅者,Event 即为订阅方法的参数
            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;
                }
            }
    
            //根据订阅者,查找订阅者里面所有的Event 类型,并储存
            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);
                }
            }
        }
    

    至此,订阅者和订阅事件进行了关联,并且进行了缓存,register()完成

    2、事件发布

    事件发布一般通过以下两种方法进行发布,postSticky 为发送粘性事件

     EventBus.getDefault().post(new Event("我是测试event数据"));
     EventBus.getDefault().postSticky(new Event("我是测试event数据"));
    

    首先先分析一下post 事件

      public void post(Object event) {
            //获取到ThreadLocal中储存的数据,并将当前post的数据添加到队列中
            PostingThreadState postingState = currentPostingThreadState.get();
            List<Object> eventQueue = postingState.eventQueue;
            eventQueue.add(event);
            //如果当前没有在post 事件
            if (!postingState.isPosting) {
                postingState.isMainThread = isMainThread();
                postingState.isPosting = true;
                if (postingState.canceled) {
                    throw new EventBusException("Internal error. Abort state was not reset");
                }
                //通过循环取出队列中所有数据,并进行订阅
                try {
                    while (!eventQueue.isEmpty()) {
                        postSingleEvent(eventQueue.remove(0), postingState);
                    }
                } finally {
                    postingState.isPosting = false;
                    postingState.isMainThread = false;
                }
            }
        }
    
     private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
            Class<?> eventClass = event.getClass();
            boolean subscriptionFound = false;
            //此处仅判断是否允许继承,如果允许就会执行父类中的订阅方法,不作为重点关注内容
            if (eventInheritance) {
                List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
                int countTypes = eventTypes.size();
                for (int h = 0; h < countTypes; h++) {
                    Class<?> clazz = eventTypes.get(h);
                    subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
                }
            } else {
                subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
            }
            if (!subscriptionFound) {
                if (logNoSubscriberMessages) {
                    logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
                }
                if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                        eventClass != SubscriberExceptionEvent.class) {
                    post(new NoSubscriberEvent(this, event));
                }
            }
        }
    
        private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
            CopyOnWriteArrayList<Subscription> subscriptions;
            //subscriptionsByEventType,key 为订阅方法的参数类型,value为订阅者和订阅方法封装的list集合
            //根据register()的源码,已经知道在注册的时候,会将订阅者和订阅方法缓存到subscriptionsByEventType里面
            synchronized (this) {
                subscriptions = subscriptionsByEventType.get(eventClass);
            }
            //如果post的Event有订阅者接收,循环进行订阅
            if (subscriptions != null && !subscriptions.isEmpty()) {
                for (Subscription subscription : subscriptions) {
                    postingState.event = event;
                    postingState.subscription = subscription;
                    boolean aborted = false;
                    try {
                        postToSubscription(subscription, event, postingState.isMainThread);
                        aborted = postingState.canceled;
                    } finally {
                        postingState.event = null;
                        postingState.subscription = null;
                        postingState.canceled = false;
                    }
                    if (aborted) {
                        break;
                    }
                }
                return true;
            }
            return false;
        }
    
      // 订阅方法的执行方法,会根据threadMode将订阅方法在不同的线程进行执行
        private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
            switch (subscription.subscriberMethod.threadMode) {
                case POSTING:
                    invokeSubscriber(subscription, event);
                    break;
                case MAIN:
                    if (isMainThread) {
                        invokeSubscriber(subscription, event);
                    } else {
                        mainThreadPoster.enqueue(subscription, event);
                    }
                    break;
                case MAIN_ORDERED:
                    if (mainThreadPoster != null) {
                        mainThreadPoster.enqueue(subscription, event);
                    } else {
                        // temporary: technically not correct as poster not decoupled from subscriber
                        invokeSubscriber(subscription, event);
                    }
                    break;
                case BACKGROUND:
                    if (isMainThread) {
                        backgroundPoster.enqueue(subscription, event);
                    } else {
                        invokeSubscriber(subscription, event);
                    }
                    break;
                case ASYNC:
                    asyncPoster.enqueue(subscription, event);
                    break;
                default:
                    throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
            }
        }
    

    post方法本身并不复杂,重点关注一下postToSubscription()方法,在此方法中,根据不同的线程进行处理,执行订阅方法,到这里,事件发布就完成了,订阅方法也已经被执行了,完成了事件的分发

    3、反注册订阅者

    反注册就非常简单了,仅仅是将缓存的订阅者及其订阅方法移除即可

        public synchronized void unregister(Object subscriber) {
            List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
            if (subscribedTypes != null) {
                for (Class<?> eventType : subscribedTypes) {
                    unsubscribeByEventType(subscriber, eventType);
                }
                typesBySubscriber.remove(subscriber);
            } else {
                logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
            }
        }
    private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
            List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
            if (subscriptions != null) {
                int size = subscriptions.size();
                for (int i = 0; i < size; i++) {
                    Subscription subscription = subscriptions.get(i);
                    if (subscription.subscriber == subscriber) {
                        subscription.active = false;
                        subscriptions.remove(i);
                        i--;
                        size--;
                    }
                }
            }
        }
    

    从代码中可以看出,反注册的时候将注册时候缓存在 typesBySubscriber 和subscriptionsByEventType 中的数据移除,完成了反注册

    相关文章

      网友评论

        本文标题:EventBus 源码分析

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