Android EventBus source code analysis

Keywords: Android Java Attribute network

Preface

In the last article, I gave a brief account of the usage of EventBus, and then I went to study the source code of EventBus 3.0. I also referred to some big blogs on the Internet for further understanding. I wrote this article to give myself a good summary and review of EventBus. If there is any incorrect place, please leave a message.

1, About EventBus

Before the emergence of EventBus, developers at that time generally used the broadcast among Android's four major components for message passing. Why do we use the event bus mechanism to replace the broadcast? Mainly because:

Broadcast: time consuming, easy to capture (unsafe).
Event bus: it is more resource-saving and efficient, and can transfer information to various objects other than the native.

Event is a general term, which refers to a conceptual thing (not necessarily an event). By consulting official documents, there is no requirement for the event naming format. You can define an object as an event, or send basic data types such as int, String, etc. as an event. The subsequent source code can also be improved to prove that (there is no requirement for method naming, just add @ Subscribe annotation! At the same time, there is no requirement for event naming).

As a message bus, EventBus mainly consists of three parts:
Event: can be any type of object. The event is passed through the event's publisher.
Event Subscriber: receives specific events.
Event Publisher: used to notify subscribers of an event. You can send events anywhere in any thread.

Figure 1 above explains the general workflow of the entire EventBus: the Publisher of the Event sends the Event through the post() method. The Event bus processes internally and finds the Event Subscriber who has subscribed to the Event. Then the Subscriber of the Event receives the Event through the onEvent() method for relevant processing.

2, Source code analysis of EventBus

1. Subscriber: EventBus.getDefault().register(this);

First, we start to analyze the method getDefault() to get the EventBus instance:

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

In getDefault(), the singleton mode of double check and lock is used to create the EventBus instance.
Next, we see what is done in the default construction method of EventBus:

private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();

public EventBus() {
    this(DEFAULT_BUILDER);
}

In the default construction method of EventBus, another parameter construction method is called, and a default builder object of type EventBusBuilder is passed in. The EventBusBuilder here is obviously a builder of EventBus, so that EventBus can add custom parameters and install a custom default EventBus instance.
Let's look at the construction method of EventBusBuilder again:

public class EventBusBuilder {

    ...

    EventBusBuilder() {
    }
    
    ...
    
}

Nothing has been done in the construction method of EventBusBuilder. Let's continue to check the parameter construction method of EventBus:

private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
private final Map<Object, List<Class<?>>> typesBySubscriber;
private final Map<Class<?>, Object> stickyEvents;

EventBus(EventBusBuilder builder) {
    ...
    
   //Map < Subscription Event. The subscriber set > key of the Subscription Event is the class type of the Event (that is, the entity type in the definition Subscription method), and the value is the element Subscription linked list
   //Subscription class: two fields in the concern class, one is the subscriber of Object type, which is the registered Object (often Activity in Android) 
   //The other is the SubscriberMethod type. The details are as follows:
   //SubscriberMethod: type of SubscriberMethod (subscription method). One of the fields in the concern class, eventType, is of class <? > type, which represents the class type of Event.
    subscriptionsByEventType = new HashMap<>();
    
    // Map < subscriber, subscription event set > (for example, all corresponding subscription methods in the Activity correspond to the Activity)
    typesBySubscriber = new HashMap<>();
    
    // Map < subscription event class type, subscription event instance object >. A word specially used for sticky event processing to determine whether an object has been registered
    stickyEvents = new ConcurrentHashMap<>();
    
    //mainThreadPoster: the main thread event sender, through its mainThreadPoster.enqueue(subscription, event)
    //Method can queue subscription information and corresponding events,
	//Then send a message through the handler, and execute the method in the handleMessage of the handler. 
    mainThreadSupport = builder.getMainThreadSupport();
    mainThreadPoster = mainThreadSupport != null ? 
    mainThreadSupport.createPoster(this) : null;
    //Backgroundpostmaster: the background event sender, through its enqueue(), adds the method to a queue in the background,
	//Finally, execute through the thread pool. Note that it adds the synchronized keyword to the execute() method of the Executor 
	//The control flag is set up to ensure that only one task can be executed by the thread pool at any time.
    backgroundPoster = new BackgroundPoster(this);
    //asyncPoster: the implementation logic is similar to backgroundPoster, and different from the guarantee of backgroundPoster
	//At any time, only one task can be executed by the thread pool. asyncPoster runs asynchronously and can receive multiple tasks at the same time
    asyncPoster = new AsyncPoster(this);
    
    ...
    
    
    subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
            builder.strictMethodVerification, builder.ignoreGeneratedIndex);
   
    // Get some column subscription information from builder to assign
    ...
   
    // A default thread pool object is taken out from the builder. It is created by the newCachedThreadPool() method of Executors. It is a thread pool with use, without create and without the maximum number.
    executorService = builder.executorService;
}

Let's look at the code executed in the specific register().

  public void register(Object subscriber) { 
         //Subscriber type 
         Class<?> subscriberClass = subscriber.getClass(); 
         //Judge whether the class is anonymous. If it is anonymous, use reflection 
         boolean forceReflection = subscriberClass.isAnonymousClass(); 
         //Get all the subscriber's response function information (i.e. methods like onNewsEvent() above) 
         List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(sub scriberClass, forceReflection); 
        //Loop each event response function, execute the subscribe() method, and update subscription related information 
        for (SubscriberMethod subscriberMethod : subscriberMetho ds) { subscribe(subscriber, subscriberMethod); 
        } 
  }

Next, we look at the findSubscriberMethods() method of the SubscriberMethodFinder:

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
    // Method? Cache: Map < Class <? > and list < subscribermethod > > types. The key is the Class of the registered Class (for example, Activity),
    //The value is the method list of all EventBus callbacks in this class (that is, methods marked with @Subscribe).
    List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
    if (subscriberMethods != null) {
        return subscriberMethods;
    }

    //Ignore generated index to determine whether to use generated APT code to optimize the process of finding received events. If it is enabled,
	//Then you can quickly get the information about the event receiving method through subscriberInfoIndexes.
	//So if you don't access the APT of EventBus in the project,
	//You can set ignore generated index to false to improve performance
    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 {
    	//Put in cache
        METHOD_CACHE.put(subscriberClass, subscriberMethods);
        return subscriberMethods;
    }
}

Here, if ignoreGeneratedIndex is true, look at findUsingReflection() method

private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
    FindState findState = prepareFindState();
    findState.initForSubscriber(subscriberClass);
    while (findState.clazz != null) {
    	// Get the method decorated by @ Subscribe through pure reflection
        findUsingReflectionInSingleClass(findState);
        // Assign the parent class of the current class to findState.clazz 
        findState.moveToSuperclass();
    }
    // Reset FindState for next recycling
    return getMethodsAndRelease(findState);
} 
//After initializing the FindState object, a while In the loop, the current class and its parent class are obtained by unreflection(Notice that Java If the current class implements an interface, even if the interface's methods are @Subscribe Decorated, the method in the current class also does not contain the annotation attribute, so if a method is used in the interface @Subscribe It's useless to decorate and then let the class implement this interface)The subscription method is added to the list, and finally the list is returned and reset FindState The object is convenient for next reuse.```

//Here ignore generated index defaults tofalse,So it will executefindUsingInfo()Method

```java
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
    // Note 1
    FindState findState = prepareFindState();
    findState.initForSubscriber(subscriberClass);
    while (findState.clazz != null) {
    	// Note 2
        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 {
             // Note 3
             findUsingReflectionInSingleClass(findState);
        }
        findState.moveToSuperclass();
    }
    // Reset FindState for next recycling
    return getMethodsAndRelease(findState);
}

Note 1: prepareFindState() method in SubscriberMethodFinder:

//The available FindState will be taken out from the find state pool (where the pool size is 4),
//If not, create a new FindState object directly through the code.
//Because FindState is frequently used in the registration process and creation consumes resources, create a FindState pool to reuse the FindState object
private static final FindState[] FIND_STATE_POOL = new FindState[POOL_SIZE];
private FindState prepareFindState() {
    synchronized(FIND_STATE_POOL) {
        for (int i = 0; i < POOL_SIZE; i++) {
            FindState state = FIND_STATE_POOL[i];
            if (state != null) {
                FIND_STATE_POOL[i] = null;
                return state;
            }
        }
    }
    return new FindState();
}

Analyze the FindState class:

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

It is the internal class of the SubscriberMethodFinder, which mainly does the initialization and recycling of objects.

Let's go back to note 2 of SubscriberMethodFinder: getSubscriberInfo():

private SubscriberInfo getSubscriberInfo(FindState findState) {
    if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) {
        SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();
        if (findState.clazz == superclassInfo.getSubscriberClass()) {
            return superclassInfo;
        }
    }
    if (subscriberInfoIndexes != null) {
        for (SubscriberInfoIndex index: subscriberInfoIndexes) {
            SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
            if (info != null) {
                return info;
            }
        }
    }
    return null;
}

Because findState.subscriberInfo and subscriberInfoIndexes are empty during initialization, null is returned directly
Next, we look at the findusingreflectioningsingleclass() method at annotation 3:

private void findUsingReflectionInSingleClass(FindState findState) {
    Method[] methods;
    try {
        // Returns the current class's own method and the explicitly overloaded parent class method
        methods = findState.clazz.getDeclaredMethods();
    } catch (Throwable th) {
        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) {
                    // A key
                    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");
        }
    }
}

The logic of this method is:
Get all the declaration methods in the subscriber class by reflection, and then find @ Subscribe as the annotation method in these methods for processing. First, go through a round of checks to see if findState.subscriberMethods exists. If so, encapsulate the method name, threadMode, priority as the sticky method object and add it to the subscription Bermethods list.
In fact, it needs to be filtered. Before explaining the source code of checkAdd(), please consider the following questions:
For the same Event, the current class has used multiple methods to subscribe to the object multiple times. If the Event is launched, how will the current class call these methods?
For the same Event, the parent class subscribes to the object once, and the child class overrides the subscription method. If the Event is launched, how will these methods be handled in the parent class and the child class?
To solve these problems, you need to look at the underlying implementation of checkAdd():

boolean checkAdd(Method method, Class<?> eventType) {
    Object existing = anyMethodByEventType.put(eventType, method);
    if (existing == null) {
        return true;
    } else {
        return checkAddWithMethodSignature(method, eventType);
    }
}

You can see that anyMethodByEventType uses the Class of Event as the key, which means that a Class can only subscribe to the same Event once. In fact, we need to continue to see the checkAddWithMethodSignature(), whose source code is simplified as follows:

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);
    if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) {
        return true;
    } else {
        subscriberClassByMethodKey.put(methodKey, methodClassOld);
        return false;
    }
}

You can see that the subscriberClassByMethodKey uses the method name + '>' + Event type as the key, which means that for the same class, the subscriberClassByMethodKey must not duplicate the key (after all, a class cannot have the same method name and the same method parameter and number), so it will eventually return true. This means that if a class uses multiple methods to register the same Event object, when the Event object is emitted, all methods will be called back.
But when the parent Class performs the above operations, if the child Class has a "display" subscription method to implement the parent Class, then the subscriberClassByMethodKey.put(methodKey, methodClass) return value will not be empty, and it is the Class of the subclass. At this time, the upper branch of if will determine whether the subclass Class is isAssignableFrom parent Class, which will definitely be false, which will go to the lower branch of if and return false. This means that when the subclass "displays" the subscription method of the parent Class, if the specified Event is launched at this time, the subscription method of the parent Class will not execute, but only the subscription method of the subclass.

After returning the subscriberMethods, the register method will finally call the subscribe method:

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
	//Get event type of subscription 
    Class<?> eventType = subscriberMethod.eventType;
    Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
    //Get the subscriber collection that subscribes to this event to find a CopyOnWriteArrayList in subscriptionsByEventType,
    //If not, create a new CopyOnWriteArrayList
    CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
     //Package subscribers subscribed through register() as Subscription object Subscription newSubscription 
    //= new Subscription(subscrib er, subscriberMethod); 
    //The subscriber set is empty, create a new set and add new subscription
    if (subscriptions == null) {
        subscriptions = new CopyOnWriteArrayList <> ();
        subscriptionsByEventType.put(eventType, subscriptions);
    } else {
        if (subscriptions.contains(newSubscription)) {//The subscriber already exists in the collection. An exception is thrown. Cannot repeat subscription 
            throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event " + eventType);
        }
    }
    int size = subscriptions.size();
    //Add a newSubscription object. It is a Subscription class, which contains information such as subscriber and subscriber method,
	//And here is a priority judgment, indicating that it is added according to the priority. The higher the priority, the higher the priority is, the higher the priority is
    for (int i = 0; i <= size; i++) {
        if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
        // Put in the List according to the priority size
            subscriptions.add(i, newSubscription);
            break;
        }
    }
    //The typesBySubscriber is added, which is mainly used in the isRegister() method of EventBus to determine whether the Subscriber object has been registered
    List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
    if (subscribedEvents == null) {
        subscribedEvents = new ArrayList<>();
        typesBySubscriber.put(subscriber, subscribedEvents);
    }
    subscribedEvents.add(eventType);
    //It will determine whether it is a sticky event. If it is a sticky event, the checkPostStickyEventToSubscription() method is called
    if (subscriberMethod.sticky) {
    	//Parent event in response to subscription event 
        if (eventInheritance) {
            Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
            //Loop to get each stickyEvent event 
            for (Map.Entry<Class<?>, Object> entry : entries) {
                Class<?> candidateEventType = entry.getKey();
                //Is the parent of this class 
                if (eventType.isAssignableFrom(candidateEventType)) {
                //The latest event of this event type is sent to the current subscriber.
                    Object stickyEvent = entry.getValue();
                    checkPostStickyEventToSubscription(newSubscription, stickyEvent);
                }
            }
        } else {
            Object stickyEvent = stickyEvents.get(eventType);
            checkPostStickyEventToSubscription(newSubscription, stickyEvent);
        }
    }
}

Let's look at the checkPostStickyEventToSubscription() method:

private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
    if (stickyEvent != null) {
        postToSubscription(newSubscription, stickyEvent, isMainThread());
    }
}

You can see that the postToSubscription() method is finally called to send sticky events. For the handling of sticky events, we will analyze them at last, and then we will see how the events are posted.

2. Publisher: EventBus.getDefault().post(new Event());

First, let's look at the post() method:

public void post(Object event) {
    //
    PostingThreadState postingState = currentPostingThreadState.get();
    List <Object> eventQueue = postingState.eventQueue;
    eventQueue.add(event);

    //Determine whether the newly added event is in distribution
    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;
        }
    }
}

currentPostingThreadState is a ThreadLocal class, through which the PostingThreadState object is obtained, and then the event list is obtained according to the object (do you think of the message mechanism in Android?) , and insert the incoming event into the list. In order to control that the event out of the queue will not be called multiple times, the PostingThreadState object has an isPosting to mark whether the current linked list has started callback operation. From the source code, we can see that each time an event is distributed, the event will also be remove d from the linked list. You can see the source code of currentPostingThreadState as follows:

private final ThreadLocal <PostingThreadState> currentPostingThreadState = new ThreadLocal <PostingThreadState> () {
@Override
protected PostingThreadState initialValue() {
    return new PostingThreadState();
}
};

final static class PostingThreadState {
    final List <Object> eventQueue = new ArrayList<>();
    boolean isPosting;
    boolean isMainThread;
    Subscription subscription;
    Object event;
    boolean canceled;
}

Next, go to the postSingleEvent() method

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
	//Type of distribution event
    Class<?> eventClass = event.getClass();
    boolean subscriptionFound = false;
    //The default value is true. If it is set to true, it will judge whether to launch the parent event when launching the event. If it is set to false, it can improve some performance.
    if (eventInheritance) {
        //Its function is to take out the class list of Event and its parent class and interface. Of course, repeated fetching will affect performance,
        //So it also makes a cache of eventtypesacache, so it doesn't need to call getSuperclass() method repeatedly.
        List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
        int countTypes = eventTypes.size();
        for (int h = 0; h < countTypes; h++) {
            Class<?> clazz = eventTypes.get(h);
            subscriptionFound |=
            //Publish each event to each subscriber
            postSingleEventForEventType(event, postingState, clazz);
        }
    } else {
        subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
    }
    if (!subscriptionFound) {
        ...
    }
}

Then we enter the postSingleEventForEventType() method:

private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class <?> eventClass) {
    CopyOnWriteArrayList <Subscription> subscriptions;
    synchronized(this) {
     // Gets the subscriber information set corresponding to the subscription event class type. (set constructed when register function is used) 
        subscriptions = subscriptionsByEventType.get(eventClass);
    }
    if (subscriptions != null && !subscriptions.isEmpty()) {
        for (Subscription subscription: subscriptions) {
            postingState.event = event;
            postingState.subscription = subscription;
            boolean aborted = false;
            try {
            // Publish subscription events to subscription functions
                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, we extract the corresponding subscriptions object directly from the subscriptionsByEventType according to the Event type, and finally call the postToSubscription() method.

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 {
                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("Unknow thread mode: " + subscription.subscriberMethod.threadMode);
    }
}

In fact, there are only two methods. One is invokeSubscriber, which means calling the method immediately. The other is xxxPoster.enqueue(), which means using other threads to execute the method.
The following are the threadMode types to avoid time-consuming operations and which threads to block during time-consuming operations:

  • POSTING: the receiving event method should execute the thread where the transmitting event method is located (since the transmitting event method thread may be the main thread, this means that the receiving method cannot perform time-consuming operations, otherwise it will block the main thread)
  • MAIN: in Android, the receive event method should be executed in the MAIN thread, otherwise (in Java projects) it is equivalent to POSTING. If the sending event method is already in the MAIN thread, the receiving event method will be called immediately (which means that the receiving event method cannot perform time-consuming operations, otherwise it will block the MAIN thread; at the same time, because it is an immediate call, the sending event method will be blocked by the receiving event method at this time). Otherwise, it is equivalent to the MAIN "ordered
  • Main? Ordered: in Android, the receive event method will be thrown into the MessageQueue for execution (which means that the transmit event method will not be blocked), otherwise (in Java projects), it is equivalent to POSTING.
  • BACKGROUND:
    • In Android
      • If the launch event method is executed in the main thread, the receive event method should be executed in the sub thread, but the sub thread is a single sub thread maintained by EventBus, so in order to avoid affecting the execution of other receive event methods, the method should not be too time-consuming to avoid blocking the sub thread.
      • If the launch event method is executed in a sub thread, the receive event method should execute in the thread where the launch event method is located.
    • In Java projects, the receive event method always executes in a single child thread maintained by EventBus.
  • ASYNC: the receive method should execute on a different thread than the send event method. Often used for time-consuming operations, such as network access. Of course, try to avoid triggering this type of method in large numbers at the same time, although EventBus specifically created a thread pool for this purpose to manage and recycle these threads.

3. Unsubscribe: EventBus.getDefault().unregister(this);

  public synchronized void unregister(Object subscriber) { 
     // Gets the collection of all subscription event class types of the subscriber 
     List<Class<?>> subscribedTypes = typesBySubscriber.get(subsc riber); 
     if (subscribedTypes != null) { 
         for (Class<?> eventType : subscribedTypes) { 
         	//subscriptionsByEventType removes all subscription information of the subscriber
              unsubscribeByEventType(subscriber, eventType); 
         }
         // Delete the < subscriber object from typesBySubscriber, and subscribe to the event class type Collection > 
         typesBySubscriber.remove(subscriber); 
     } else { 
         Log.e("EventBus", "Subscriber to unregister was not regi stered before: "+ subscriber.getClass()); 
     } 
  }
  private void unsubscribeByEventType(Object subscriber, Class<?> eventType) { 
     // Gets the subscriber information set corresponding to the subscription event 
     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); 
              // Removes a specific subscriber from the subscriber collection 
              if (subscription.subscriber == subscriber) { 
                  subscription.active = false; 
                  subscriptions.remove(i); 
                  i --; 
                  size --; 
              } 
         } 
     } 
  }

4, Publish sticky moment: EventBus.getDefault.postSticky(new CollectEvent())

Ordinary events can only be received after they are registered and then sent, while sticky events can also be received after they are sent and then subscribed to. In addition, the sticky event will be saved in memory. Every time you enter the memory, you will search for the latest sticky event, unless you deregister it manually

public void postSticky(Object event) {
    synchronized (stickyEvents) {
        // Put the event in stickyEvents first
        stickyEvents.put(event.getClass(), event);
    }
    post(event);
}

You can see that the first step is to put the event into stickyEvents, and the second step is normal post(). In order to avoid conflicts caused by multithreaded operations postSticky(Object) and removetickyevent (class <? >), the synchronized keyword is added to the stickyEvents object,
When we analyzed the last part of register() method, the source code of sticky event is as follows:

if (subscriberMethod.sticky) {
    Object stickyEvent = stickyEvents.get(eventType);
    if (stickyEvent != null) {
        postToSubscription(newSubscription, stickyEvent, isMainThread());
    }
}

As you can see, here you will judge whether the current event is a sticky event. If so, take the event out of the sticky events and execute the postToSubscription() method.

OK, this is the end of the EventBus source code.

summary

The source code of EventBus is simple. I don't see it. Many of the design techniques are worth learning, such as the reuse pool mentioned earlier, and the synchronized keyword all over the source code of EventBus. The author is also the first time to write the source code analysis, but also learn from the articles of the big guys on the Internet.

Reference link:
Source code analysis of Android mainstream tripartite Library (IX. deep understanding of EventBus source code)
Talk about the source code and design of EventBus
EventBus 3.1.1 source code analysis

Published 2 original articles, praised 0 and visited 11
Private letter follow

Posted by carydean on Mon, 09 Mar 2020 01:59:50 -0700