preface

After reading a lot of articles analyzing EventBus, I always felt confused. This time, I took the time to thoroughly organize EventBus and found that the core of EventBus is actually three images, which involve three HashMap tables. If you understand these three images, EventBus will understand them.

1. The first graph (Subscribers and subscription events)

Take a look at the code for registering and unregistering EventBus in your activity.

	onStart{
	   EventBus.getDefault().register(this);
	}

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onEvent1(Event1 event) {}@Subscribe(threadMode = ThreadMode.MAIN)
    public void onEvent2(Event2 event) {
     }
	
	onStop{
	   EventBus.getDefault().register(this);
	}

Copy the code

Look at the code above, the registered listener is the activity, called subscriber, in the activity listen for Event1 and Event2, now execute a code in another position:

EventBus.getDefault().post(new Event1());
Copy the code

At this point, onEvent1 in the activity receives the event. Here’s the first one:

private finalMap<Object, List<Class<? >>> typesBySubscriber;Copy the code

EventBus uses reflection to iterate through the object’s methods while registering, adding legitimate methods with the @SUBSCRIBE tag to typesBySubscriber. TypesBySubscriber is in the form of a HashMap, and the key is the registered object itself. Since there may be multiple listening events in a registered object, the value is an Event stored in a list.

What do we do in the register method

 public void register(Object subscriber) { Class<? > subscriberClass = subscriber.getClass(); List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);synchronized (this) {
            for(SubscriberMethod subscriberMethod : subscriberMethods) { subscribe(subscriber, subscriberMethod); }}}// Must be called in synchronized block
    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) { List<Class<? >> subscribedEvents = typesBySubscriber.get(subscriber);if (subscribedEvents == null) {
            subscribedEvents = newArrayList<>(); typesBySubscriber.put(subscriber, subscribedEvents); }}Copy the code

The code above does two things: 1. It iterates through the methods of the registered object by reflection, gets the @SUBSCRIBE tag and puts it in a list. Finally, the registered object is given the key, and the @SUBSCRIBE list of methods is placed as a value in a HashMap, as shown in the figure above.

thinking

1. Why register listener as key and listener event list as value in HashMap? A subscriber can subscribe to multiple events. If you remove a subscriber’s listening event, you should remove all of the events in the event. That is to say, in the process of de-registration, all events will be found through Subsribe for de-registration.

2. Second graph (Subscription events and subscribers)

EventBus.getDefault().post(new Event1());
Copy the code

All listeners registered to listen on Event1 will receive a callback

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onEvent(Event event) {}Copy the code

The subsriber is the subscriber and is executed in the onstart of the activity for example

. EventBus.getDefault().register(this);
Copy the code

So subsribe is an activity.

Think about why you need to save the relationship table between events and subsribe.

This is because an Event may be subscribed by more than one subsribe, so when executing POST (Event), all subscribed events will be found and the Event method will be called. Let’s look at the post method:


    private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class
        eventClass) {
        CopyOnWriteArrayList<Subscription> subscriptions;
        synchronized (this) {
            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; }}Copy the code

Post and postSticky mainly will be called to the above methods, the above method subscriptionsByEventType. Get (eventClass) is through the type of event to the table above to find the corresponding notification subscriptions.

The third picture

Consider a question before looking at the third picture, how is postSticky actually implemented? Why can I still listen to event events when postSticky is executed first and register is executed later? First look at the postSticky code:

 public void postSticky(Object event) {
        synchronized (stickyEvents) {
            stickyEvents.put(event.getClass(), event);
        }
        // Should be posted after it is putted, in case the subscriber wants to remove immediately
        post(event);
    }
Copy the code

PostSticky saves event. Getclass and event, and then we subscribe:

       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);
            
Copy the code

StickyEvent is found in the stickEvents table. If the registered event is the same as stickyEvent, the postToSubscription method is executed. That is, call the registered method execution.

conclusion

1, to understand the EventBus to burst from the register and unRegister, post, postSticky method. Understand that a register is essentially finding each method with subscriber in a subscription object (such as an activity), and those are the methods that get called. Subscription objects (such as activities) are holders of a set of Event methods.

2. The sticky method can receive the previous stickyEvent method because the hashMap of stickyEvent is maintained in EventBus. When the subsribe is registered it’s going to go through if it’s registered and it’s going to listen on stickyEvent and if it’s registered it’s going to do a callback.

EventBus shortcomings

1. There are many event classes defined during use. 2. 3, you need to register and de-register, if you forget to de-register will lead to memory leaks