[EventBus] source code analysis of EventBus (detailed process of registering subscribers and registering subscription methods)

preface

In the last blog [EventBus] source code analysis of EventBus (registered subscriber | subscription method | search subscription method) The first step of registering a subscriber, searching for a subscriber, is introduced;

First, get the current list < subscribermethod > subscribermethods subscription method collection, which is from the cached map < class <? > of the SubscriberMethodFinder, List<SubscriberMethod>> METHOD_ Cache acquisition, which is generated when the subscription method is obtained for the first time, and then obtained directly from the cache;

METHOD_CACHE cache generation strategy: if the subscription method is obtained for the first time and the cache is empty, obtain all qualified subscription methods of the subscription class through reflection, encapsulate the subscription method into the SubscriberMethod, and then add it to the findState.subscriberMethods collection;





1, EventBus registered subscriber


After obtaining the subscription method set, start traversing the subscription method set, call the subscribe method and register the subscriber;

public class EventBus {
    /**
     * Registers the given subscriber to receive events. Once the subscriber is no longer interested in receiving events, it must call {@link#unregister (Object)}.
     * <p/>
     * Subscribers have event handling methods that must be annotated by {@ link Subscribe}.
     * {@link Subscribe}Comments also allow configurations like {@ link ThreadMode} and priority.
     */
    public void register(Object subscriber) {
        Class<?> subscriberClass = subscriber.getClass();
        // 1. Get the subscriber collection and find the qualified subscription method collection in the current subscription class 
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        // 2. Traverse the subscriber set, subscribe to events, and save data. These data are some mapping relationships 
        synchronized (this) {
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);
            }
        }
    }
}




2, Specific process of registering subscription method


Get the subscription method parameter type, which can be any type, custom MessageEvent message type;

subscribe(Object subscriber, SubscriberMethod subscriberMethod)

Encapsulate the Subscription object, which encapsulates a subscriber object and a Subscription method;

Subscription newSubscription = new Subscription(subscriber, subscriberMethod);

From map < class <? >, Copyonwritearraylist < Subscription > > subscriptionsbyeventtype member variable to obtain the Subscription set. The purpose of this HashMap is to save all Subscription classes and Subscription methods corresponding to the current eventType, so that after the message center obtains the corresponding type of messages, it can smoothly pass them to the corresponding Subscription methods;

  • Key is an event type object;
  • Value is the Subscription collection, which encapsulates a subscriber object and a Subscription method

CopyOnWriteArrayList is a thread safe collection. When writing data, a copy will be copied, which will not affect the operation of other threads to read the collection

CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);

If the traversal data is not saved in the above set, save the subscription method information to the above set;

        if (subscriptions == null) {
            subscriptions = new CopyOnWriteArrayList<>();
            // Save key value pairs
            subscriptionsByEventType.put(eventType, subscriptions);
        } else {
            if (subscriptions.contains(newSubscription)) {
                throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
                        + eventType);
            }
        }

If the priority annotation attribute is set for the subscription method, the priority of the subscription method is processed and rearranged;

        int size = subscriptions.size();
        for (int i = 0; i <= size; i++) {
        	// Process subscription method priority and rearrange it  
            if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
                subscriptions.add(i, newSubscription);
                break;
            }
        }

To map < object, list < class <? > > The typesbysubscriber member variable adds a subscriber object subscription method parameter type collection key value pair. The function of the member variable is to find relevant subscription methods when canceling registration;

		// Map<Object, List<Class<?>>> typesBySubscriber
		//		Key: Subscriber object
		//		Value: collection of subscription method parameter types 
		//		This method is required when unregistering 
        List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
        if (subscribedEvents == null) {
            subscribedEvents = new ArrayList<>();
            typesBySubscriber.put(subscriber, subscribedEvents);
        }
        subscribedEvents.add(eventType);

Registration subscription method code example:

/**
 * EventBus It is the central publish / subscribe event system for Java and Android.
 * Events are published ({@link#post (Object)}) to the bus, which passes them to subscribers with matching handlers
 * Method of event type.
 * To receive events, subscribers must register themselves with the bus using {@link#register (Object)}.
 * Once registered, the subscriber will receive events until {@link#unregister (Object)} is called.
 * The event handling method must be annotated by {@ link Subscribe}. It must be public and does not return any content (void),
 * And there is only one parameter (event).
 */
public class EventBus {
    // This method must be invoked in the synchronous code block. 
    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
    	// Get the subscription method parameter type, which can be any type, custom MessageEvent message type
        Class<?> eventType = subscriberMethod.eventType;
        // Subscription encapsulates a subscriber object and a subscription method 
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
        
        // Map<Class<?>,  Copyonwritearraylist < subscription > > subscriptionsbyeventtype member variable 
        //		Key is an event type object 
        //		Value is the Subscription collection, which encapsulates a subscriber object and a Subscription method 
        //		The purpose of this HashMap is to save all subscription classes and subscription methods corresponding to the current eventType 
        //		So that after the message center obtains the corresponding type of message, it can smoothly deliver it to the corresponding subscription method 
        // CopyOnWriteArrayList is a thread safe collection. When writing data, a copy will be copied, which will not affect the operation of other threads to read the collection
        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        
        // If the traversal data is not saved in the above collection, the subscription method information is saved in the above collection 
        if (subscriptions == null) {
            subscriptions = new CopyOnWriteArrayList<>();
            // Save key value pairs
            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++) {
        	// Process subscription method priority and rearrange it  
            if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
                subscriptions.add(i, newSubscription);
                break;
            }
        }

		// Map<Object, List<Class<?>>> typesBySubscriber
		//		Key: Subscriber object
		//		Value: collection of subscription method parameter types 
		//		This method is required when unregistering 
        List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
        if (subscribedEvents == null) {
            subscribedEvents = new ArrayList<>();
            typesBySubscriber.put(subscriber, subscribedEvents);
        }
        subscribedEvents.add(eventType);

        // Processing methods and related business logic
        if (subscriberMethod.sticky) {
            if (eventInheritance) {
                // Existing sticky events for all subclasses of eventType must be considered.
                // Note: iterating over all events may be inefficient because there are many sticky events,
                // Therefore, the data structure should be changed to allow more efficient lookup
                // For example, store additional mappings for subclasses of a superclass: 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);
            }
        }
    }
}




3, Subscription class


The Subscription class encapsulates a subscriber object and a Subscription method

final class Subscription {
    final Object subscriber;
    final SubscriberMethod subscriberMethod;
}

Tags: Java Android eventbus

Posted by ThermalSloth on Fri, 24 Sep 2021 05:05:14 +0530