EventBus

Source code version:

  • EventBus: 3.3.1

Navigation:

  • EventBus library -EventBus Library
  • EventBus library -EventBus Library
  • See more articles here: home page

The post EventBus

EventBus –> post()

public void post(Object event) {
    / / 1, to get the current thread PostingThreadState, currentPostingThreadState is a ThreadLocal object, ensure that each thread has a PostingThreadState object, guarantee the thread safety.
    PostingThreadState postingState = currentPostingThreadState.get();
    // get the event queue of the current thread
    List<Object> eventQueue = postingState.eventQueue;
    // The event to be sent is added to the event queue of the current thread
    eventQueue.add(event);

    // Check if the event is being sent. If the event is being sent, it will not be executed because the event has been added to the eventQueue, and it will continue to follow the logic of the while loop.
    if(! postingState.isPosting) {If the event is not sent, initialize PostingThreadState and traverse the event queue.
        // check whether the current thread is the main thread
        postingState.isMainThread = isMainThread();
        // 7. Indicate that the current thread is sending
        postingState.isPosting = true;
        If the current thread is canceled, an exception is thrown.
        if (postingState.canceled) {
            throw new EventBusException("Internal error. Abort state was not reset");
        }
        try {eventQueue.remove(0)
            // if there is an event, send it.
            while(! eventQueue.isEmpty()) {Eventqueue.remove (0) indicates that posted events are sent in sequence.
                postSingleEvent(eventQueue.remove(0), postingState); }}finally {
            // All events of the current thread are completed.
            postingState.isPosting = false;
            postingState.isMainThread = false; }}}Copy the code

The POST () method, for sending events, is compatible with multiple threads sending multiple events (described below). It using ThreadLocal object, ensure that each thread has a PostingThreadState object, to ensure the safety of the thread it through the postingState. IsMainThread recorded the sending thread is the main thread, and then it will, in turn, to send.

Description:

  1. postingState.isMainThreadAnd record theSending threadWhether it isThe main thread.
  2. steps10.eventQueue.remove(0),post()The events that come in willIn turn to send.

Let’s see currentPostingThreadState attributes and PostingThreadState class.

private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
    @Override
    protected PostingThreadState initialValue(a) {
        return newPostingThreadState(); }};final static class PostingThreadState {
    final List<Object> eventQueue = new ArrayList<>(); // The event queue of the sending thread
    boolean isPosting; // Whether the sending thread is sending
    boolean isMainThread; // Whether the sending thread is in the main thread
    Subscription subscription; // Subscription that the sending thread is publishing
    Object event; // Send the Event that the thread is publishing
    boolean canceled; // Whether the sending thread has been canceled
}
Copy the code

The PostingThreadState class is the state class of the sending thread. CurrentPostingThreadState is a ThreadLocal object, ensure that each thread has a PostingThreadState object, guarantee the thread safety.

EventBus –> postSingleEvent

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
    // get the Class of the sent eventClass<? > eventClass = event.getClass();// check whether Subscription is found
    boolean subscriptionFound = false;
    if (eventInheritance) { // eventInheritance: Whether the event has inheritance, the default is true.
        // if the Event is inherited, send all the parent classes of the Event and all the parent interfaces.
        // Get [all parent classes and all parent interfaces that send Event].List<Class<? >> eventTypes = lookupAllEventTypes(eventClass);// send a single event through the collection.
        int countTypes = eventTypes.size();
        for (int h = 0; h < countTypes; h++) { Class<? > clazz = eventTypes.get(h);// 16, send a single event and check if Subscription is foundsubscriptionFound |= postSingleEventForEventType(event, postingState, clazz); }}else {
        // if the Event is not inherited, send the Event for [send Event].
        subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
    }
    if(! subscriptionFound) {// if no Log is found, determine whether to print the Log or send NoSubscriberEvent.
        if (logNoSubscriberMessages) {
            If there is no subscriber, abnormal information is printed.
            logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
        }
        if(sendNoSubscriberEvent && eventClass ! = NoSubscriberEvent.class && eventClass ! = SubscriberExceptionEvent.class) {// 20. Send the NoSubscriberEvent event when there are no subscribers.
            post(new NoSubscriberEvent(this, event)); }}}Copy the code

The postSingleEvent() method is compatible with sending multiple events in a single thread for events. If the Event has inheritance, the Event of [send all parent classes of Event and all parent interfaces] (this is multi-event) is sent; otherwise, the Event of [send Event] is sent, and it determines whether to print Log or send NoSubscriberEvent if it is not found.

Let’s look at the lookupAllEventTypes method again. EventBus –> lookupAllEventTypes

private staticList<Class<? >> lookupAllEventTypes(Class<? > eventClass) {// Use synchronization and lock uniqueness to ensure orderly thread execution.
    synchronized (eventTypesCache) {
        // Get the maintained collection of all events (parent class, parent interface) of the current Event from the cache.List<Class<? >> eventTypes = eventTypesCache.get(eventClass);if (eventTypes == null) {
            // If the collection is empty, it is acquired for the first time, then the collection is created, maintained and added to the cache.
            eventTypes = new ArrayList<>();
            // Clazz defaults to eventClassClass<? > clazz = eventClass;// Iterate over the current clazz, continuing as long as it is not empty.
            while(clazz ! =null) {
                // Add the current clazz and add eventClass for the first time.
                eventTypes.add(clazz);
                // Add all interface classes of the current clazz
                addInterfaces(eventTypes, clazz.getInterfaces());
                // Get the parent of the current clazz class to continue adding.
                clazz = clazz.getSuperclass();
            }
            // Add the maintained collection to the cache.
            eventTypesCache.put(eventClass, eventTypes);
        }
        // Finally return this maintained collection of all events (parent class, parent interface).
        returneventTypes; }}// Loop recursively adds all interface classes of the current class to eventTypes.
// Example: The current class implements interface A and interface B, A implements interface C and D, B implements interface E, C, D, and E does not implement any interface, and finally adds all interfaces A, B, C, D, and E to eventTypes.
static void addInterfaces(List
       
        > eventTypes, Class
        [] interfaces)
       > {
    // Iterate over all interfaces
    for(Class<? > interfaceClass : interfaces) {if(! eventTypes.contains(interfaceClass)) {// If this interface is not included, add it.
            eventTypes.add(interfaceClass);
            // Continue to recursively add all the interfaces of the parent interface, using recursion to complete all its parent interfaces.addInterfaces(eventTypes, interfaceClass.getInterfaces()); }}}Copy the code

LookupAllEventTypes () method to get all the parent classes that send events and all the parent interfaces.

EventBus –> postSingleEventForEventType

private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class
        eventClass) {
    CopyOnWriteArrayList<Subscription> subscriptions;
    synchronized (this) {
        Get all subscribers to the HashMap based on the event, executed in a synchronized method to ensure that the HashMap get method is thread safe.
        subscriptions = subscriptionsByEventType.get(eventClass);
    }
    if(subscriptions ! =null && !subscriptions.isEmpty()) {
        // if there are subscribers, traverse the collection to send, return true (indicating that the subscriber was found).
        // 23, traverse all subscribers of this type to send
        for (Subscription subscription : subscriptions) {
            Record the Event that the sending thread is publishing
            postingState.event = event;
            // record the Subscription that the sending thread is publishing
            postingState.subscription = subscription;
            // 26
            boolean aborted;
            try {
                / / 27, the events sent to the subscriber, postingState. IsMainThread post method for record.
                postToSubscription(subscription, event, postingState.isMainThread);
                // 28. Whether the event is cancelled
                aborted = postingState.canceled;
            } finally {
                // Reset postingState
                postingState.event = null;
                postingState.subscription = null;
                postingState.canceled = false;
            }
            if (aborted) {
                // 30. If the event is cancelled, all releases of this type are cancelled.
                break; }}return true;
    }
    // 31, no subscribers, do not send, return false (not found).
    return false;
}
Copy the code

Send postSingleEventForEventType () method, for the event, compatible with the multithreading single-shot send single event. Get all subscriptions (holding subscriber objects, subscribing method objects) that subscribe to it based on the event, then iterate and send them in turn.

EventBus –> postToSubscription

private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
    ThreadMode (ThreadMode)
    switch (subscription.subscriberMethod.threadMode) {
        case POSTING:
            // 33. Publish thread (default)
            // call reflection to notify the event directly
            invokeSubscriber(subscription, event);
            break;
        case MAIN:
            //
            if (isMainThread) {
                On Android and is the main thread, or not on Android, call reflection direct notification.
                invokeSubscriber(subscription, event);
            } else {
                On Android and not the main thread, add the event to the main thread queue and call it on the main thread.
                mainThreadPoster.enqueue(subscription, event);
            }
            break;
        case MAIN_ORDERED:
            // the main thread is in order.
            if(mainThreadPoster ! =null) {
                On Android, always add events to the main thread queue and call them on the main thread.
                mainThreadPoster.enqueue(subscription, event);
            } else {
                // 40, not on Android, directly through reflection call.
                invokeSubscriber(subscription, event);
            }
            break;
        case BACKGROUND:
            // 41, background thread
            if (isMainThread) {
                If the event is on Android and is the main thread, or if it is not on Android, it is added to the background thread queue and called in the background thread.
                backgroundPoster.enqueue(subscription, event);
            } else {
                // 43, on Android and not the main thread, directly through reflection call.
                invokeSubscriber(subscription, event);
            }
            break;
        case ASYNC:
            // async thread
            // add the event to the asynchronous thread queue and invoke it in the asynchronous thread.
            asyncPoster.enqueue(subscription, event);
            break;
        default:
            throw new IllegalStateException("Unknown thread mode: "+ subscription.subscriberMethod.threadMode); }}Copy the code

The postToSubscription() method, for notification of events, is compatible with multiple threads sending single events. The subscription method is called according to the ThreadMode of the subscription method, and if thread switching is required, the call is made by switching threads. Otherwise, call directly.

Description:

  1. mainThreadPoster(i.e.HandlerPoster),backgroundPoster,asyncPosterThe threePoster, will eventually be calledinvokeSubscriber()Methods for notification are described in detail below –Poster.
  2. EventBusIf yesViscosity methodandHas been sentThis event is also calledpostToSubscription()Method to notify.

EventBus –> invokeSubscriber

void invokeSubscriber(Subscription subscription, Object event) {
    try {
        Reflection calls the subscription method of the subscriber object, passing in the event parameter.
        subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
    } catch (InvocationTargetException e) {
        // call subscription method exception
        handleSubscriberException(subscription, event, e.getCause());
    } catch (IllegalAccessException e) {
        throw new IllegalStateException("Unexpected exception", e); }}private void handleSubscriberException(Subscription subscription, Object event, Throwable cause) {
    if (event instanceof SubscriberExceptionEvent) {
        // SubscriberExceptionEvent event that determines whether to print the Log when the subscription method is called abnormally (no event is sent to prevent recursion).
        if (logSubscriberExceptions) {
            // Print exception information when calling the subscription method.
            // Don't send another SubscriberExceptionEvent to avoid infinite event recursion, just log
            logger.log(Level.SEVERE, "SubscriberExceptionEvent subscriber " + subscription.subscriber.getClass()
                    + " threw an exception", cause);
            SubscriberExceptionEvent exEvent = (SubscriberExceptionEvent) event;
            logger.log(Level.SEVERE, "Initial event " + exEvent.causingEvent + " caused exception in "+ exEvent.causingSubscriber, exEvent.throwable); }}else {
        // A common event that determines whether an exception is thrown, a Log is printed, and a SubscriberExceptionEvent is sent when a subscription method is called with an exception.
        if (throwSubscriberException) {
            // Throw a SubscriberException exception when a subscription method exception is invoked.
            throw new EventBusException("Invoking subscriber failed", cause);
        }
        if (logSubscriberExceptions) {
            // Print exception information when calling the subscription method.
            logger.log(Level.SEVERE, "Could not dispatch event: " + event.getClass() + " to subscribing class "
                    + subscription.subscriber.getClass(), cause);
        }
        if (sendSubscriberExceptionEvent) {
            // Send the SubscriberExceptionEvent event when calling the subscription method is abnormal.
            SubscriberExceptionEvent exEvent = new SubscriberExceptionEvent(this, cause, event, subscription.subscriber); post(exEvent); }}}Copy the code

The invokeSubscriber() method calls the subscription method of the subscriber object for reflection, passing in the event argument. HandleSubscriberException () method, call the subscribe method for handling exceptions, judge whether an exception is thrown, print the Log and send SubscriberExceptionEvent.

Threading model

POSTING (POSTING threads)

Events are subscribed to and published on the same thread (step 34). This is the default setting. Event passing means minimal overhead because it completely avoids thread switching. Therefore, this is the recommended pattern for simple tasks that are known to be completed in a very short time and do not require a main thread. Event handlers using this pattern must return quickly to avoid blocking the publishing thread that might be the main thread.

Description:

  1. Event publishing (which may block the Android main thread)
  • Events are received and published on the same thread, and since no thread pool is used, publishing must block the thread.
  1. Event reception (which may block the Android main thread)
  • Events are received and published on the same thread. Since receiving may block the main thread, it is not recommended to perform time-consuming operations in receiving.

MAIN

On Android, the subscriber will be called in the Android main thread (UI thread). If the publication thread is the main thread, the subscription method is called directly, blocking the publication thread (Step 36). Otherwise, the event is queued for delivery (non-blocking) (Step 37). Subscribers using this pattern must return quickly to avoid blocking the main thread. If not on Android, the behavior is the same as POSTING (Step 36).

Description:

  1. Event publishing (must block the Android main thread)
  • inAndroidAnd isThe main threadOr,Not inAndroidOn, the behavior andPOSTINGThe same (Step 36), so releaseA certainwillBlocking threads.
  • inAndroidAnd on thenotThe main thread, inserts the event toMain thread queue(Steps to 37), as a result of usingThe thread pool, so publishA certainDon’tBlocking threads.
  1. Event receiving (must block the Android main thread)
  • Events are received in the main thread, so receiving must block the main thread, so time-consuming operations cannot be performed in the receive.
  1. Order of events (which may not be ordered)
  • inAndroidGo, if firstThe main threadRelease (Waiting in line, may wait a long time before it executes itself), afterThe main threadRelease (Direct call), so inAndroidEvents on itmayisNot ordered.
  • In theAndroidOn, as isDirect call, so the eventA certainisThe orderly.

MAIN_ORDERED

On Android, the subscriber will be called in the Android main thread (UI thread). Unlike MAIN, events are always queued for delivery (step 39). This ensures that the POST call is non-blocking.

Description:

  1. Event publishing (must not block the Android main thread)
  • inAndroidOn, always willThe eventAdded to theMain thread queueIn the main thread call, due to useHandlerPublish, therefore publishA certainDon’tBlocking threads.
  • Not inAndroidOn, the behavior andPOSTINGSame, so publishA certainwillBlocking threads.
  1. Event receiving (must block the Android main thread)
  • The eventreceiveinThe main thread, so receiveA certainwillBlocking main thread, soreceiveIn theCan’tperformTime consumingOperation (withMAIN).
  1. Order of events (must be ordered)
  • inAndroidOn,alwaysAdded to theMain thread queue, so the eventA certainisThe orderly(Steps to 39).
  • In theAndroidOn, as isDirect call, so the eventA certainisThe orderly(Step 40).

BACKGROUND

On Android, the subscriber is called in a background thread. If the publishing thread is not the main thread, the subscription method is called directly in the publishing thread (step 43). If the publishing thread is the main thread, EventBus uses a single background thread that delivers all its events sequentially (Step 42). Subscribers using this pattern should try to return quickly to avoid blocking background threads. If not on Android, always use background threads (Step 42).

Description:

  1. Event publishing (must not block the Android main thread)
  • inAndroidAnd isThe main threadOr,Not inAndroidTo insert the event toBackground thread queue(Steps to 42), as a result of usingThe thread pool, so publishA certainDon’tBlocking threads.
  • inAndroidAnd on thenotThe main thread, then the behavior andPOSTINGThe same (Step 43), so releaseA certainwillBlocking threads.
  1. Event reception (which may block thread pool threads)
  • inAndroidAnd isThe main thread, the use ofbackgroundPosterExecution (Steps to 42), as a result ofbackgroundPosterWhat ifIn execution,No more thread creationExecute, but useThe last threadA block of execution (see details below –PosterbackgroundPoster), so receivemaywillBlocking background thread, soreceiveIn theIs not recommendedperformTime consumingOperation.

ASYNC

The subscriber will be invoked in a separate thread. This is always independent of the publishing thread and the main thread. Publishing events using this pattern never waits for a subscription method. Use this pattern if the execution of the subscription method is likely to take some time (for network access, for example). Avoid triggering a large number of long-running asynchronous subscription methods simultaneously to limit the number of concurrent threads. EventBus uses thread pools to efficiently reuse threads that have completed asynchronous subscriber notifications.

Description:

  1. Event publishing (must not block the Android main thread)
  • The subscriberWill be inA separatethethreadIn the call (Step 45), as a result of usingThe thread pool, so publishA certainDon’tBlocking threads.
  1. Event reception (does not block the thread pool thread)
  • The subscriberWill be inA separatethethreadCall, useasyncPosterExecution (Step 45), as a result ofasyncPosterIn the implementationEvery timeAll inThe thread poolUse insideThe new threadExecute (see details below –PosterasyncPoster), soreceiveIn thecanperformTime consumingOperation.

summary

  1. EventBusthepost()To obtainThe current threadtheThe event queue, the event to be sentjoinTo it.
  2. Iterate through the event queue, sending events as long as there are any.
  3. Determine whether the event existsinheritanceIf so, based onThe event type, find the currentEventtheAll the parent classAs well asAll parent interfacestheclassThe collection,traverseThis set, sent in turnA singleEvents; Otherwise, only sendThe currentEventaEvents.
  4. According to theThe eventTo obtainAll the people who subscribe to itSubscriptionThe collection,traverseCollection to send events toThe subscribertheThe subscribe method.
  5. When it is sent to the subscriber, the subscription method is called according to the thread model of the subscription method. If thread switching is needed, the subscription method is called by switching threads. Otherwise, call directly.

The EventBus postSticky

EventBus –> postSticky()

public void postSticky(Object event) {
    synchronized (stickyEvents) {
        // add the event to the sticky event set
        stickyEvents.put(event.getClass(), event);
    }
    // 2. Send events
    post(event);
}
Copy the code

Description:

  1. EventBusthepost()Will only be sent toHas been registeredtheThe subscriber.
  2. EventBusthepostSticky()Will be sent toHas been registeredtheThe subscriber,And then you have to registertheThe subscribertheSticky subscription method.

summary

  1. EventBusthepostSticky()That will beThe eventTo join thestickyEventsIn the set, when there areThe newThe subscriberregisteredIf, afterThe subscriberSubscribe to theThe eventtheSticky subscription methodAnd it willsendtoThis method.
  2. Events are sent to subscribers who have registered.

EventBus logout

EventBus –> unregister()

public synchronized void unregister(Object subscriber) {
    // get all events subscribed to by the subscriberList<Class<? >> subscribedTypes = typesBySubscriber.get(subscriber);if(subscribedTypes ! =null) {
        // 2, iterate through all the set of events subscribed by this subscriber
        for(Class<? > eventType : subscribedTypes) {// remove the Subscription from the Subscription collection for this event by eventType.
            unsubscribeByEventType(subscriber, eventType);
        }
        // 4. Remove the subscriber from typesBySubscriber
        typesBySubscriber.remove(subscriber);
    } else {
        // 5, not registered, call logout, Log print warning.
        logger.log(Level.WARNING, "Subscriber to unregister was not registered before: "+ subscriber.getClass()); }}Copy the code

Unregister () method for unregistering subscribers. Remove itself from typesBySubscriber and remove multiple event types within subscriber from subscriptionsByEventType.

EventBus –> unsubscribeByEventType()

private void unsubscribeByEventType(Object subscriber, Class
        eventType) {
    // get all subscribers of the event
    List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
    if(subscriptions ! =null) {
        int size = subscriptions.size();
        // 7, walk through the collection above, find the Subscription for this subscriber, and remove it from the collection.
        for (int i = 0; i < size; i++) {
            Subscription subscription = subscriptions.get(i);
            if (subscription.subscriber == subscriber) {
                // find, remove from set.
                subscription.active = false; subscriptions.remove(i); i--; size--; }}}}Copy the code

The unsubscribeByEventType() method removes a single event type in subscriber from subscriptionsByEventType.

Description:

  1. canFirst traversalsubscriptionsByEventTypeAnd thenTo traverse thesubscriptionsByEventTypethevalueAnd the lastmatchingsubscriberIt can be found in this wayTo remove thetheSubscription, butThis kind ofTo implement thanThe aboveimplementationLow efficiency.

summary

  1. EventBustheThe cancellationTo obtainThe subscriberSubscribe to theAll events.traverseAll events passThe eventfromsubscriptionsByEventTypeGets theThe eventtheallSubscriptionTo traverse theSubscriptionSet, findThe subscribertheSubscriptionAnd then fromThis collectionIn theremove.
  2. Will thisThe subscriberfromtypesBySubscriberIn theremove.

Poster

Poster

public interface Poster {

    void enqueue(Subscription subscription, Object event);
}
Copy the code

Poster interface, which has an enqueue method that enqueues subscription and Event. It has three implementation classes, HandlerPoster, BackgroundPoster, and AsyncPoster. Let’s take a look at each class.

mainThreadPoster

HandlerPoster

public class HandlerPoster extends Handler implements Poster {

    private final PendingPostQueue queue;
    private final int maxMillisInsideHandleMessage;
    private final EventBus eventBus;
    private boolean handlerActive;

    public HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) {
        // This Handler object is the Handler for the main thread
        super(looper);
        this.eventBus = eventBus;
        this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage;
        // Create an event queue
        queue = new PendingPostQueue();
    }

    public void enqueue(Subscription subscription, Object event) {
        // Encapsulate a PendingPost object based on the arguments passed in
        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
        synchronized (this) {
            // Add PendingPost to the queue
            // Send a message, execute a message, and send it again. If the message is already being executed, the message is executed with the last message
            queue.enqueue(pendingPost);
            if(! handlerActive) { handlerActive =true;
                // Call Handler's sendMessage, send the event back to the main thread, and finally call the following handleMessage method
                if(! sendMessage(obtainMessage())) {throw new EventBusException("Could not send handler message"); }}}}@Override
    public void handleMessage(Message msg) {
        boolean rescheduled = false;
        try {
            long started = SystemClock.uptimeMillis();
            // Infinite loop
            while (true) {
                PendingPost pendingPost = queue.poll(); // Get and remove the first one
                // Double check to make sure pendingPost is not null, if it is, jump out of the loop
                if (pendingPost == null) {
                    synchronized (this) {
                        // Check again, this time in synchronized
                        pendingPost = queue.poll();
                        if (pendingPost == null) {
                            // Loop until queue ends
                            handlerActive = false;
                            return; }}}// Call the subscriber's subscription method with the reflected method.
                eventBus.invokeSubscriber(pendingPost);
                long timeInMethod = SystemClock.uptimeMillis() - started;
                if (timeInMethod >= maxMillisInsideHandleMessage) {
                    if(! sendMessage(obtainMessage())) {throw new EventBusException("Could not send handler message");
                    }
                    rescheduled = true;
                    return; }}}finally{ handlerActive = rescheduled; }}}Copy the code

The HandlerPoster class inherits the Handler class and implements the Poster interface. The enQueue method adds PendingPost to the queue, sends an empty message, and finally walks to the handleMessage method, which decides to execute as long as the queue has data. Finally, reflection is used to call the subscriber’s subscription method.

backgroundPoster

BackgroundPoster

final class BackgroundPoster implements Runnable.Poster {

    private final PendingPostQueue queue;
    private final EventBus eventBus;

    private volatile boolean executorRunning;

    BackgroundPoster(EventBus eventBus) {
        this.eventBus = eventBus;
        // Initialize the queue
        queue = new PendingPostQueue();
    }

    public void enqueue(Subscription subscription, Object event) {
        // Encapsulate the PendingPost object
        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
        synchronized (this) {
            // Add the PendingPost object to the queue
            // Send a message, execute a message, and send it again. If the message is already being executed, the message is executed with the last message
            queue.enqueue(pendingPost);
            if(! executorRunning) { executorRunning =true;
                // The thread pool initialized earlier is used here
                eventBus.getExecutorService().execute(this); }}}// Thread pool execution callback
    @Override
    public void run(a) {
        // Implement the same HandlerPoster
        try {
            try {
                // Infinite loop
                while (true) {
                    PendingPost = PendingPost = PendingPost = PendingPost = PendingPost
                    PendingPost pendingPost = queue.poll(1000);
                    if (pendingPost == null) {
                        synchronized (this) {
                            // Check again, this time in synchronized
                            pendingPost = queue.poll();
                            if (pendingPost == null) {
                                // Loop until queue ends
                                executorRunning = false;
                                return; }}}// Call the subscriber's subscription method using reflectioneventBus.invokeSubscriber(pendingPost); }}catch (InterruptedException e) {
                eventBus.getLogger().log(Level.WARNING, Thread.currentThread().getName() + " was interruppted", e); }}finally {
            executorRunning = false; }}}Copy the code

The BackgroundPoster class implements the Runnable interface and implements the Poster interface. The enQueue method adds PendingPost to the queue, and then executes the Runnable using the thread pool. Finally, the run method decides, Execute as long as the queue has data, and finally use reflection to call the subscriber’s subscription method.

asyncPoster

AsyncPoster

class AsyncPoster implements Runnable.Poster {

    private final PendingPostQueue queue;
    private final EventBus eventBus;

    AsyncPoster(EventBus eventBus) {
        this.eventBus = eventBus;
        queue = new PendingPostQueue();
    }

    public void enqueue(Subscription subscription, Object event) {
        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
        // Send one, execute one
        queue.enqueue(pendingPost);
        eventBus.getExecutorService().execute(this);
    }

    @Override
    public void run(a) {
        PendingPost pendingPost = queue.poll();
        if(pendingPost == null) {
            throw new IllegalStateException("No pending post available"); } eventBus.invokeSubscriber(pendingPost); }}Copy the code

The AsyncPoster class implements the Runnable interface and the Poster interface. The enQueue method adds PendingPost to the queue, then executes the Runnable using the thread pool, and finally goes inside the Run method. Get the latest data from the queue, execute, and finally call the subscriber’s subscription method using reflection.

Description:

  1. BackgroundPosterClasses andAsyncPosterclassThreads executeThe difference between:BackgroundPosterCategories, and ifIn execution, the useLast threadExecute, otherwise useA new threadThe execution.AsyncPosterClass, always usedThe new threadThe execution.

summary

  1. PosterInterface, which has three implementation classes, forHandlerPoster,BackgroundPoster,AsyncPoster.
  2. And these classes,enqueueMethod will bePendingPost(holdsubscriptionObjects,eventObject) joins toqueueQueue up, and thenswitchtoSpecified threadExecute, use lastreflectioncallThe subscribertheThe subscribe method.

SubscriberMethodFinder

SubscriberMethodFinder

class SubscriberMethodFinder {
    private static final int BRIDGE = 0x40;
    private static final int SYNTHETIC = 0x1000;

    private static final int MODIFIERS_IGNORE = Modifier.ABSTRACT | Modifier.STATIC | BRIDGE | SYNTHETIC;
    private static finalMap<Class<? >, List<SubscriberMethod>> METHOD_CACHE =new ConcurrentHashMap<>();

    private List<SubscriberInfoIndex> subscriberInfoIndexes;
    private final boolean strictMethodVerification;
    private final boolean ignoreGeneratedIndex;

    private static final int POOL_SIZE = 4;
    private static final FindState[] FIND_STATE_POOL = new FindState[POOL_SIZE];

    SubscriberMethodFinder(List<SubscriberInfoIndex> subscriberInfoIndexes, boolean strictMethodVerification,
                           boolean ignoreGeneratedIndex) {
        this.subscriberInfoIndexes = subscriberInfoIndexes;
        this.strictMethodVerification = strictMethodVerification;
        this.ignoreGeneratedIndex = ignoreGeneratedIndex;
    }

    List<SubscriberMethod> findSubscriberMethods(Class
        subscriberClass) {
        // select * from the previous cache
        List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
        if(subscriberMethods ! =null) {
            // if the cache was cached before, return it directly
            return subscriberMethods;
        }

        if (ignoreGeneratedIndex) {
            // Index generated by annotation handler is ignored, so reflection is used directly. IgnoreGeneratedIndex defaults to false.
            subscriberMethods = findUsingReflection(subscriberClass);
        } else {
            // get all subscription methods
            subscriberMethods = findUsingInfo(subscriberClass);
        }
        if (subscriberMethods.isEmpty()) {
            throw new EventBusException("Subscriber " + subscriberClass
                    + " and its super classes have no public methods with the @Subscribe annotation");
        } else {
            // add to the cache set
            METHOD_CACHE.put(subscriberClass, subscriberMethods);
            returnsubscriberMethods; }}private List<SubscriberMethod> findUsingInfo(Class
        subscriberClass) {
        Get the FindState object from the array, if there is a direct return, if a new FindState object is not created.
        FindState findState = prepareFindState();
        // Initialize findState based on event subscribers
        findState.initForSubscriber(subscriberClass);
        while(findState.clazz ! =null) {
            // get the subscriberInfo and initialize it to null.
            findState.subscriberInfo = getSubscriberInfo(findState);
            if(findState.subscriberInfo ! =null) {
                // Through the annotation handler
                // Get all subscriber methods
                SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
                for (SubscriberMethod subscriberMethod : array) {
                    if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
                        / / by check, give findState. SubscriberMethods way to increase the subscriberfindState.subscriberMethods.add(subscriberMethod); }}}else {
                // Get Method from subscriber by reflection
                findUsingReflectionInSingleClass(findState);
            }
            // Move to the parent class for subsequent operations.
            findState.moveToSuperclass();
        }
        / / release
        return getMethodsAndRelease(findState);
    }

    // Get all subscriber methods from findState and release them
    private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {
        // Get a collection of all subscription methods for the subscriber
        List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods);
        // findState retrievals
        findState.recycle();
        // Restore the recovered findState to FIND_STATE_POOL.
        synchronized (FIND_STATE_POOL) {
            for (int i = 0; i < POOL_SIZE; i++) {
                if (FIND_STATE_POOL[i] == null) {
                    FIND_STATE_POOL[i] = findState;
                    break; }}}// return the collection
        return subscriberMethods;
    }

    // Get the FindState object from the cache and remove it from the cache if there is one; If not, create and return.
    private FindState prepareFindState(a) {
        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;
                    returnstate; }}}return new FindState();
    }

    // Get subscriber information
    private SubscriberInfo getSubscriberInfo(FindState findState) {
        // Process SubscriberInfoIndex to add a manually created AbstractSubscriberInfo (normally not manually).
        // -findState.subscriberInfo ! = null: The return value of getSubscriberInfo(findState) when the caller calls findState.subscriberInfo = getSubscriberInfo(findState) is not NULL. That is, subscriberInfoIndexes has a value.
        // -findState.subscriberInfo.getSuperSubscriberInfo() ! = null: indicates that AbstractSubscriberInfo is created manually, rather than the annotations handler generating the added SimpleSubscriberInfo.
        if(findState.subscriberInfo ! =null&& findState.subscriberInfo.getSuperSubscriberInfo() ! =null) {
            SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();
            if (findState.clazz == superclassInfo.getSubscriberClass()) {
                // If the Class to be obtained is equal to the parent Class, the parent Class is returned.
                returnsuperclassInfo; }}// Process the SubscriberInfoIndex added by addIndex().
        if(subscriberInfoIndexes ! =null) {
            for (SubscriberInfoIndex index : subscriberInfoIndexes) {
                SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
                if(info ! =null) {
                    returninfo; }}}return null;
    }

    // Get the subscriber method by reflection
    private List<SubscriberMethod> findUsingReflection(Class
        subscriberClass) {
        // Get FindState.
        FindState findState = prepareFindState();
        // Initialize FindState because it is reusable.
        findState.initForSubscriber(subscriberClass);
        while(findState.clazz ! =null) {
            / / reflection, if find it endures findState. SubscriberMethods.
            findUsingReflectionInSingleClass(findState);
            // Moving to the parent class changes findState.clazz
            findState.moveToSuperclass();
        }
        return getMethodsAndRelease(findState);
    }

    private void findUsingReflectionInSingleClass(FindState findState) {
        Method[] methods;
        try {
            // all declared methods in the subscriber are placed in an array.
            // getDeclaredMethods, which gets all the methods of the current class.
            // getMethods, which gets all public methods of the current class and all its parents.
            methods = findState.clazz.getDeclaredMethods();
            / / because getDeclaredMethods, get all the methods of the current class, so findState skipSuperClasses to false, for don't skip the parent class, which continue to find the parent class.
        } catch (Throwable th) {
            // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
            try {
                // get the public method declared in the subscriber and set the parent class to be skipped
                / / because getMethods, get the current class and all its parent class of all public methods, so findState skipSuperClasses is true, for skip the parent class, not to find the parent class.
                methods = findState.clazz.getMethods();
            } catch (LinkageError error) { // super class of NoClassDefFoundError to be a bit more broad...
                String msg = "Could not inspect methods of " + findState.clazz.getName();
                if (ignoreGeneratedIndex) {
                    // Ignore the index generated by the annotation generator, hint: consider using an EventBus annotation handler to avoid reflection.
                    msg += ". Please consider using EventBus annotation processor to avoid reflection.";
                } else {
                    // Ignore the index generated by the annotation generator, prompting: Please make this class visible to the EventBus annotation handler to avoid reflection.
                    msg += ". Please make this class visible to EventBus annotation processor to avoid reflection.";
                }
                throw new EventBusException(msg, error);
            }
            findState.skipSuperClasses = true;
        }
        // Iterate over these methods
        for (Method method : methods) {
            Get method modifiers :public, private, etc
            int modifiers = method.getModifiers();
            // The synthetic method is not static, static, bridge, synthetic.
            if((modifiers & Modifier.PUBLIC) ! =0 && (modifiers & MODIFIERS_IGNORE) == 0) {
                // 13, method parameter type arrayClass<? >[] parameterTypes = method.getParameterTypes();if (parameterTypes.length == 1) {
                    // Get method annotations
                    Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                    // 15, if there are annotations
                    if(subscribeAnnotation ! =null) { Class<? > eventType = parameterTypes[0];
                        // Add method and eventType to findState
                        if (findState.checkAdd(method, eventType)) {
                            // Get the threadMode object in the annotation
                            ThreadMode threadMode = subscribeAnnotation.threadMode();
                            // create a SubscriberMethod object and add it to findState
                            findState.subscriberMethods.add(newSubscriberMethod(method, eventType, threadMode, subscribeAnnotation.priority(), subscribeAnnotation.sticky())); }}}else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
                    // Not 1 argument, and is in strict mode, and Subscribe annotation on method, throw exception warning.
                    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)) {
                // The method decorator does not conform to the strict mode, and the method has a Subscribe annotation, then throws an exception warning.
                String methodName = method.getDeclaringClass().getName() + "." + method.getName();
                throw new EventBusException(methodName +
                        " is a illegal @Subscribe method: must be public, non-static, and non-abstract"); }}}}Copy the code

The SubscriberMethodFinder class findSubscriberMethods method finds all subscription methods for this subscriber. It is first fetched from the cache, and if it is in the cache, it is returned directly; If not in the cache, all subscription methods for this subscriber are retrieved via reflection or annotation handlers and returned in the collection.

conclusion

That’s the full EventBus source code! After the other tripartite library source code series, please timely attention. If you have any questions, see you in the comments!

Finally, I would like to recommend my website, devbolg.cn, the developer’s technical blog, which currently contains android related technologies, and will be open to all developers later. Welcome to experience!