EventBus source code analysis

  The following code analysis is based on eventbus:3.1.1 .
  EventBus is used for communication between Android modules. Let's see how it is implemented.
  Starting from the entrance, register EventBus first, assuming that it is registered in the onCreate() method of Activity (the Activities below are all components that register EventBus).

EventBus.getDefault().register(this);

  The getDefault() method of EventBus returns a singleton EventBus object:

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

  Next is registration:

public void register(Object subscriber) {
        Class<?> subscriberClass = subscriber.getClass();
        //注释1:利用反射获取当前类添加了@subscribe注解的方法。
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        synchronized (this) {
            //遍历所有的方法,逐一绑定。
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                //注释2:将方法和订阅者以键值对的形式储存起来。
                subscribe(subscriber, subscriberMethod);
            }
        }
    }

  The findSubscribeMethods method will take out all the methods annotated with @Subscribe in the Activity, wrap it into a SubscriberMethod object, and return it in the collection.
  After getting all the methods to be registered, the real registration takes place. In fact, it is to store Activity and annotated methods by category, so that they can be used directly when sending messages.

// Must be called in synchronized block
    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        //看过findSubscriberMethods方法就知道,注解方法的参数有且只能有一个,eventType就是这个参数的类型。
        Class<?> eventType = subscriberMethod.eventType;
        //将Activity和方法包装起来。
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
        //尝试获取按参数类别分类的集合
        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        if (subscriptions == null) {
            subscriptions = new CopyOnWriteArrayList<>();
            //如果没有这样的集合就新建一个,键是参数的类型,值是一个集合,里面的所有Activity的订阅方法都是对应的参数类型。
            subscriptionsByEventType.put(eventType, subscriptions);
        } else {
            if (subscriptions.contains(newSubscription)) {
                throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
                        + eventType);
            }
        }

        int size = subscriptions.size();
        //在考虑优先级的情况下把Activity和方法的包装对象放到集合中。
        for (int i = 0; i <= size; i++) {
            if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
                subscriptions.add(i, newSubscription);
                break;
            }
        }

        //按Activity来存储参数类型
        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);
            }
        }
    }

  Registering EventBus probably does the above things. To sum it up:

1.先拿出订阅者的所有带有@Subscribe注解的方法。
2.按方法参数的类型把订阅者和方法作为一个整体存储起来。
3.按订阅者保存其内所有方法的参数类型。

  Analysis of the EventBus message mechanism here can also be half guessed. When we post a variable, EventBus will send this variable to all registered methods with the same parameter type according to the type of the variable. The calling method should be reflection.
  The following analysis of the post method can verify my conjecture:

public void post(Object event) {
        //currentPostingThreadState是一个ThreadLocal。
        PostingThreadState postingState = currentPostingThreadState.get();
        List<Object> eventQueue = postingState.eventQueue;
        eventQueue.add(event);//将事件添加到消息队列。

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

  Let's take a look at postingState first. It can be found that it is obtained from ThreadLocal.
  Students who understand ThreadLocal must be able to think of Handler. The Looper in the Handler mechanism is also stored in ThreadLocal. This is because ThreadLocal can ensure that each thread's data is independent. If you are wondering, please read my blog .
  Since currentPostingThreadState overrides ThreadLocal's initialValue() method, the postingState here will not get a null value. The message queue (eventQueue) is also initialized with the initialization of postingState.
  After the above analysis, it can be concluded that:

1.调用EventBus的post方法会把消息放到消息队列中。
2.消息队列是线程独立的,每个线程有自己的消息队列(类似Handler中的MessageQueue)。
3.每次调用post方法会把当前线程消息队列中的消息全部发送出去。

  Next, let's take a look at how the message is sent:

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
        Class<?> eventClass = event.getClass();
        boolean subscriptionFound = false;
        if (eventInheritance) {//默认false
            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));
            }
        }
    }

  In fact, the postSingleEventForEventType() method is called:

private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
        CopyOnWriteArrayList<Subscription> subscriptions;
        synchronized (this) {
            //从按参数类型分类的集合中拿出对应的Activity集合。
            subscriptions = subscriptionsByEventType.get(eventClass);
        }
        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;//回去抛异常。
    }

  Here, take out the target Activity set according to the parameter type, and then really use the method corresponding to reflection involk.

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

  Here, the parameters of the annotation will be specifically judged. If the child thread is required to call, the thread will be taken out from the thread pool to call the method. If it is called on the main thread, the current thread will be judged. If it is the main thread, it will be called directly, otherwise, the handler will be used. Return to the main thread and call again.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324469750&siteId=291194637