Brief discussion on source code - EventBus(v3.2.0)

Posted by OhLordy on Thu, 11 Nov 2021 10:25:52 +0100

EventBus official website

usage method

Step 1: define events

Subscribed event class

public static class MessageEvent { /* Additional fields if needed */ }

Step 2: prepare subscribers

  1. The @ Subscribe annotation declaration is required for the subscription method. You can set the thread to handle the event, etc

    @Subscribe(threadMode = ThreadMode.MAIN)  
    public void onMessageEvent(MessageEvent event) {/* Do something */};
    
  2. Registration and de registration of subscribers, such as adding in Activity and Fragment

     @Override public void onStart() {
         super.onStart();
         EventBus.getDefault().register(this);
     }
    
     @Override public void onStop() {
         super.onStop();
         EventBus.getDefault().unregister(this);
     }
    

Step 3: send event

After the event is sent by post, the subscription method in step 2. Above can accept the event and call back for processing

 EventBus.getDefault().post(new MessageEvent());

summary

Create an EventBus instance through the singleton and the builder;

By adding a subscription method through @ Subscribe, you can set the priority of the subscription method, its processing thread, and whether it is sticky;

In the register registration method, all subscription methods in subscribers are obtained in two ways: ① collect the methods declared by @ Subscribe annotation through runtime reflection; ② Compile time index, that is, apt (process (set <? Extensions typeelement > annotations, roundenvironment Env)) method of abstractprocessor, writes the method declared by @ Subscribe annotation into the java file under build;

subscribe subscription method, which puts all subscription methods in the current subscriber into the subscribedEvents collection;

After the event event event is pushed by post, the matching object is found according to the stored subscription information collection, the invoke reflection is performed, and the subscription method subscribed to the event event event is called;

Source code day reading

1. Subscribe comment @ subscribe

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
    
    /*In which thread does the subscriber process*/
    //ThreadMode contains
    //1.POSTING - the event publisher and subscriber are in the same thread. There is no thread switching and the overhead is the smallest
    //2. The main subscription method is processed in the main thread
    //3.MAIN_ORDERED - the subscriber is processed in the main thread and processed in order according to the priority queue
    //4. Backgroup - the subscriber processes in the sub thread and processes it in order according to the priority queue
    //5.ASYNC - subscribers are processed in the thread pool, which is suitable for network access
    ThreadMode threadMode() default ThreadMode.POSTING;

    /*Is it a sticky event*/
    boolean sticky() default false;

    /*In the same thread, the subscriber with higher priority receives the event first*/
    int priority() default 0;
}

2. Create an EventBus instance EventBus.getDefault()

Using design patterns: singletons and builders

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

    //...

    EventBus(EventBusBuilder builder) {
        logger = builder.getLogger();
        subscriptionsByEventType = new HashMap<>();
        typesBySubscriber = new HashMap<>();
        stickyEvents = new ConcurrentHashMap<>();
        mainThreadSupport = builder.getMainThreadSupport();
        mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
        backgroundPoster = new BackgroundPoster(this);
        asyncPoster = new AsyncPoster(this);
        indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
        subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
                builder.strictMethodVerification, builder.ignoreGeneratedIndex);
        logSubscriberExceptions = builder.logSubscriberExceptions;
        logNoSubscriberMessages = builder.logNoSubscriberMessages;
        sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
        sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
        throwSubscriberException = builder.throwSubscriberException;
        eventInheritance = builder.eventInheritance;
        executorService = builder.executorService;
    }

   //...
}

3. Registration of eventbus. register(this)

Main variables and initialization reference parameters

//{msg1: [{firstActivity, onMsg1(msg1)}]}
//{msg2: [{firstActivity, onMsg2(msg2)}, {secondActivity, onMsg2(msg2)}]}
//{msg3: [{secondActivity, onMsg3(msg3)}]}
private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
//{firstActivity: [Msg1, Msg2]}
//{secondActivity: [Msg2, Msg3]}
private final Map<Object, List<Class<?>>> typesBySubscriber;


final List<SubscriberMethod> subscriberMethods = new ArrayList<>();
//{msg1: onMsg1(msg1)}
//{msg2: onMsg2(msg2)}
//{msg3: onMsg3(msg3)}
final Map<Class, Object> anyMethodByEventType = new HashMap<>();
//{onMsg1()>0001, firstActivity}
//{onMsg2()>0002, firstActivity}
//{onMsg2()>0002, secondActivity}
//{onMsg3()>0003, secondActivity}
final Map<String, Class> subscriberClassByMethodKey = new HashMap<>();

Registration method entry

/** Subscribers need to register to receive events and de register when they are no longer in use; The subscription method must be declared by the @ Subscribe annotation, and its thread and priority can be configured */
public void register(Object subscriber) {
    //Subscriber is a subscriber [such as firstActivity or secondActivity]
    Class<?> subscriberClass = subscriber.getClass();
    //Find out all subscription methods in the current subscriber
    List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
    synchronized (this) {
        //Traverse the subscription method in the current subscriber
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            //Start subscription
            subscribe(subscriber, subscriberMethod);
        }
    }
}

3.1 find all subscription methods in the subscriber findSubscriberMethods(subscriberClass)

//For example, find the onMsg1/onMsg2 subscription method in firstActivity
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
    //Fetch subscription method from cache [method_cache: concurrenthashmap < class <? >, list < subscribermethod > >]
    List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
    if (subscriberMethods != null) {
        return subscriberMethods;
    }

    //True = > get subscription method by runtime reflection
    //False = > generate subscription method index at compile time
    if (ignoreGeneratedIndex) {
        subscriberMethods = findUsingReflection(subscriberClass);
    } else {
        subscriberMethods = findUsingInfo(subscriberClass);
    }
    //If the subscription method is not defined in the current activity or fragment, an exception will be prompted
    if (subscriberMethods.isEmpty()) {
        throw new EventBusException("Subscriber " + subscriberClass + " and its super classes have no public methods with the @Subscribe annotation");
        //Cache all subscription methods within the current subscriber
    } else {
        METHOD_CACHE.put(subscriberClass, subscriberMethods);
        return subscriberMethods;
    }
}
3.1.1 mode I: Reflection
private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
    FindState findState = prepareFindState();
    findState.initForSubscriber(subscriberClass);
    while (findState.clazz != null) {
        //Find all subscription methods in subscribers through reflection
        findUsingReflectionInSingleClass(findState);
        findState.moveToSuperclass();
    }
    return getMethodsAndRelease(findState);
}
private void findUsingReflectionInSingleClass(FindState findState) {
    Method[] methods;
    try {
        // This is faster than getMethods, especially when subscribers are fat classes like Activities
        //Gets all methods in the subscriber class
        methods = findState.clazz.getDeclaredMethods();
    } catch (Throwable th) {
        // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
        try {
            //Gets all methods in the subscriber class and its 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) {
                msg += ". Please consider using EventBus annotation processor to avoid reflection.";
            } else {
                msg += ". Please make this class visible to EventBus annotation processor to avoid reflection.";
            }
            throw new EventBusException(msg, error);
        }
        //Parent class method = > has been obtained in getMethods() method, so skip parent class check is set here
        findState.skipSuperClasses = true;
    }

    //Traverse all methods
    for (Method method : methods) {
        //Filter out public modification methods [public xxMethod(...) {}]
        int modifiers = method.getModifiers();
        if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
            Class<?>[] parameterTypes = method.getParameterTypes();
            //Filter out methods with only one parameter [public xxMethod(Object obj) {}]
            if (parameterTypes.length == 1) {
                //Filter out the method declared by @ Subscribe annotation [@ Subscribe public xxMethod(Object obj) {}]
                Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                if (subscribeAnnotation != null) {
                    //eventType is the parameter type Object in the method [@ Subscribe public xxMethod(Object obj) {}]
                    //For example, MessageEvent in method [@ subscribe public void onmessageevent (MessageEvent) {}]
                    Class<?> eventType = parameterTypes[0];
                    //Has the current method and eventType pair been added to the subscriber and subscription method collection
                    if (findState.checkAdd(method, eventType)) {
                        //checkAdd returns true, that is, the method and eventType key value pairs are not in the subscriber methods [subscriber and subscription method collection]
                        //=>According to the @ Subscribe annotation attribute, create a SubscriberMethod instance and add it to subscriberMethods
                        ThreadMode threadMode = subscribeAnnotation.threadMode();
                        findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
                                subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
                    }
                }
                //Prompt: the method declared by the @ Subscribe annotation must have and can only have one parameter
            } 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);
            }
            //Prompt: the method declared by @ Subscribe annotation must be modified by public and cannot be modified by static and abstract
        } 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");
        }
    }
}
boolean checkAdd(Method method, Class<?> eventType) {
    // 2 level check: 1st level with event type only (fast), 2nd level with complete signature when required.
    // Usually a subscriber doesn't have methods listening to the same event type.
    /*
        About object obj = map. Put (key); = > If the corresponding value of key does not exist, null is returned; If the value corresponding to the key exists, the previously stored value is returned, as shown in the following example
        Map<String, String> map = new HashMap<>();
        String put1 = map.put("name", "Ashe");
        System.out.println("put1:" + put1);//put1:null
        String put2 = map.put("name", "Annie");
        System.out.println("put2:" + put2);//put2:Ashe
     */
    //If the subscriber has the following three subscription methods
    //@Subscribe(...) public void listen1(NewsMessage msg) {}
    //@Subscribe(...) public void listen2(NewsMessage msg) {}
    //@Subscribe(...) public void listen3(WeatherMessage msg) {}
    //The first time: put (newsmessage, listen1) = > existing = = null = > return true = > call the above findState.subscriberMethods.add method
    //The second time: put (newsmessage, listen2) = > existing = = listen1 = > else {...}
    //The third time: put (weathermessage, listen3) = > existing = = null = > return true = > call the above findState.subscriberMethods.add method
    Object existing = anyMethodByEventType.put(eventType, method);
    if (existing == null) {
        return true;
    } else {
        if (existing instanceof Method) {
            if (!checkAddWithMethodSignature((Method) existing, eventType)) {
                // Paranoia check
                throw new IllegalStateException();
            }
            // Put any non-Method object to "consume" the existing Method
            anyMethodByEventType.put(eventType, this);
        }
        return checkAddWithMethodSignature(method, eventType);
    }
}
private boolean checkAddWithMethodSignature(Method method, Class<?> eventType) {
    methodKeyBuilder.setLength(0);
    methodKeyBuilder.append(method.getName());
    methodKeyBuilder.append('>').append(eventType.getName());

    //Identity of the subscription method
    String methodKey = methodKeyBuilder.toString();
    //Subscriber class where the subscription method is located
    Class<?> methodClass = method.getDeclaringClass();
    //Add the method id and subscriber class to the subscriberClassByMethodKey collection
    Class<?> methodClassOld = subscriberClassByMethodKey.put(methodKey, methodClass);
    /*
        isAssignableFrom(clz)Judge whether it is the parent class of clz from the perspective of class inheritance
        instanceof clz Judge whether it is a subclass of clz from the perspective of instance inheritance
     */
    //The subscription method and subscriber class are added in the subscriberClassByMethodKey for the first time 𞓜 the subscription method and its parent class have been added to the subscriberClassByMethodKey
    //That is, the subscriberClassByMethodKey has not added the subscription method and the subclass information pair before
    if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) {
        // Only add if not already found in a sub class
        //Call the findState.subscriberMethods.add method above
        return true;
    } else {
        // Revert the put, old class is further down the class hierarchy
        subscriberClassByMethodKey.put(methodKey, methodClassOld);
        return false;
    }
}
3.1.2 mode II index

Usage reference = > https://greenrobot.org/eventbus/documentation/subscriber-index/

After configuring the build project, build - > generated - > AP_ generated_ Generate MyEventBusIndex class files in sources - > debug - > out - > com.example.myapp directory; You can see that all subscription methods are generated at compile time through apt's process method

package com.example.myapp;

import org.greenrobot.eventbus.meta.SimpleSubscriberInfo;
import org.greenrobot.eventbus.meta.SubscriberMethodInfo;
import org.greenrobot.eventbus.meta.SubscriberInfo;
import org.greenrobot.eventbus.meta.SubscriberInfoIndex;

import org.greenrobot.eventbus.ThreadMode;

import java.util.HashMap;
import java.util.Map;

/** This class is generated by EventBus, do not edit. */
public class MyEventBusIndex implements SubscriberInfoIndex {
    private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;

    static {
        SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();

        putIndex(new SimpleSubscriberInfo(cc.catface.eventbus.MainActivity.class, true, new SubscriberMethodInfo[] {
            new SubscriberMethodInfo("onName", cc.catface.eventbus.NameMsg.class, ThreadMode.MAIN_ORDERED, 7, true),
            new SubscriberMethodInfo("onAge", cc.catface.eventbus.AgeMsg.class),
            new SubscriberMethodInfo("onCommon", cc.catface.eventbus.CommonMsg.class),
            new SubscriberMethodInfo("onImpl", cc.catface.eventbus.MsgImpl.class),
            new SubscriberMethodInfo("onI", cc.catface.eventbus.IMsg.class),
        }));

    }

    private static void putIndex(SubscriberInfo info) {
        SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);
    }

    @Override
    public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {
        SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);
        if (info != null) {
            return info;
        } else {
            return null;
        }
    }
}

Collect the above subscription method information generated in build in EventBus

/*Find all subscription methods according to the subscriber class [for example, find onMsg1/onMsg2 according to firstActivity]*/
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
    FindState findState = prepareFindState();
    //Set the subscriberclass of findState = = clazz = firstactivity
    findState.initForSubscriber(subscriberClass);
    //Find firstActivity
    while (findState.clazz != null) {
        //
        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 {
            findUsingReflectionInSingleClass(findState);
        }
        //Find parent class
        findState.moveToSuperclass();
    }
    return getMethodsAndRelease(findState);
}
/*findState In clazz is firstActivity*/
private SubscriberInfo getSubscriberInfo(FindState findState) {
    //Find the subscription object in the parent class [such as baseActivity, the parent class of firstActivity]
    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) {
            //Call the getSubscriberInfo method of MyEventBusIndex to obtain the subscription method index generated at compile time
            SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
            if (info != null) {
                return info;
            }
        }
    }
    return null;
}

3.2 event subscribe (subscriber, subscriber method)

For example, add the onMsg1/onMsg2 subscription method in firstActivity to the subscribedEvents collection

// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
    //Subscription event type [e.g. Msg1]
    Class<?> eventType = subscriberMethod.eventType;
    //Create a subscription object [such as new Subscription(firstActivity, onMsg1(msg1))]
    Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
    //Subscription collection subscriptions subscribed to Msg1 events
    //It was empty at first
    CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
    if (subscriptions == null) {
        subscriptions = new CopyOnWriteArrayList<>();
        //Put Msg1 and the newly created subscriptions empty collection information pair into the map
        subscriptionsByEventType.put(eventType, subscriptions);
    } else {
        //There is already a current subscription object in the subscriptions collection = > the exception indicates that firstActivity has a subscription method listening to Msg1
        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++) {
        //Add subscription objects to subscriptions based on priority
        if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
            subscriptions.add(i, newSubscription);
            break;
        }
    }

    //Find all event types subscribed by subscribers [such as MSG1 and msg2 subscribed by firstActivity]
    List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
    //If the subscriber's subscription method type list is empty
    if (subscribedEvents == null) {
        //Add an empty collection of subscription event types to the subscriber
        subscribedEvents = new ArrayList<>();
        typesBySubscriber.put(subscriber, subscribedEvents);
    }
    //Add event type of subscription [e.g. Msg1]
    subscribedEvents.add(eventType);

    /**
     * Sticky event: you can receive an event by subscribing to it after it is sent
     */
    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 {
            //Get sticky events based on event type
            Object stickyEvent = stickyEvents.get(eventType);
            //Check and send sticky events
            checkPostStickyEventToSubscription(newSubscription, stickyEvent);
        }
    }
}

Unregister EventBus. unregister(this)

For example, unregister(firstActivity), according to firstActivity, get typesBySubscriber as [Msg1, Msg2], traverse and call unsubscribeByEventType(firstActivity, [Msg1, Msg2]) and call subscriptionsByEventType.get(Msg1, Msg2). The obtained subscriptions are [firstActivity] and [firstActivity, secondActivity], and then judge and remove them. The final subscriptions are [] and [] respectively [secondActivity]

/** Unregisters the given subscriber from all event classes. */
public synchronized void unregister(Object subscriber) {
    //subscribedTypes are all subscription event types in the subscriber [such as MSG1 and msg2 subscribed in firstActivity]
    //typesBySubscriber will be assigned after the subscriber calls the register(this) method for registration, so it must not be empty here
    List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
    if (subscribedTypes != null) {
        for (Class<?> eventType : subscribedTypes) {
            unsubscribeByEventType(subscriber, eventType);
        }
        typesBySubscriber.remove(subscriber);
    } else {
        //Exception prompt not registered
        logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
    }
}
/** Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber. */
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
    //Get all subscribers subscribed to the event type eventType
    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 the current subscriber from the list of subscribers for the eventType event type
            if (subscription.subscriber == subscriber) {
                subscription.active = false;
                subscriptions.remove(i);
                i--;
                size--;
            }
        }
    }
}

EventBus sends event. post(new MessageEvent())

/** Posts the given event to the event bus. */
public void post(Object event) {
    //currentPostingThreadState is a queue of event information
    PostingThreadState postingState = currentPostingThreadState.get();
    List<Object> eventQueue = postingState.eventQueue;
    //Add an event to the event queue
    eventQueue.add(event);

    if (!postingState.isPosting) {
        //Check whether events are sent on the main thread
        postingState.isMainThread = isMainThread();
        //Set event sending status
        postingState.isPosting = true;
        if (postingState.canceled) {
            throw new EventBusException("Internal error. Abort state was not reset");
        }
        try {
            while (!eventQueue.isEmpty()) {
                //Send event
                postSingleEvent(eventQueue.remove(0), postingState);
            }
        } finally {
            //Reset event sending status
            postingState.isPosting = false;
            postingState.isMainThread = false;
        }
    }
}
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
    //Event type [e.g. Msg1 event type is Msg1]
    Class<?> eventClass = event.getClass();
    boolean subscriptionFound = false;
    if (eventInheritance) {
        //For example, the parent class of Msg1 event / Msg1 subscribed in firstActivity, such as BaseMsg event / the subscription method of Msg1 interface, such as IMsg event, will accept events
        //If there is an event Msg1 extends BaseMsg implements IMsg
        //There are subscription methods onMsg1(Msg1)/onBaseMsg(BaseMsg)/onIMsg(IMsg) in firstActivity
        //At this time, post(Msg1) will call back all three subscription methods in firstActivity
        List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
        int countTypes = eventTypes.size();
        for (int h = 0; h < countTypes; h++) {
            Class<?> clazz = eventTypes.get(h);
            //Send event
            subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
        }
    } else {
        subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
    }
    //There is no subscription method to subscribe to event events
    if (!subscriptionFound) {
        if (logNoSubscriberMessages) {
            logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
        }
        if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                eventClass != SubscriberExceptionEvent.class) {
            post(new NoSubscriberEvent(this, event));
        }
    }
}
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
    CopyOnWriteArrayList<Subscription> subscriptions;
    synchronized (this) {
        //Subscription methods that subscribe to Msg1 events [such as onMsg1/onBaseMsg/onIMsg]
        subscriptions = subscriptionsByEventType.get(eventClass);
    }
    if (subscriptions != null && !subscriptions.isEmpty()) {
        for (Subscription subscription : subscriptions) {
            postingState.event = event;
            postingState.subscription = subscription;
            boolean aborted;
            try {
                //Send the event to the subscription object [e.g. (firstactivity, onmsg1 (MSG1) - Main), the thread of MSG1, MSG1 event post is in thread]
                postToSubscription(subscription, event, postingState.isMainThread);
                aborted = postingState.canceled;
            } finally {
                //reset state 
                postingState.event = null;
                postingState.subscription = null;
                postingState.canceled = false;
            }
            if (aborted) {
                break;
            }
        }
        return true;
    }
    return false;
}
//[e.g. (firstactivity, onmsg1 (MSG1) - Main), the thread of MSG1, MSG1 event post is in thread]
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
    switch (subscription.subscriberMethod.threadMode) {
        case POSTING:
            invokeSubscriber(subscription, event);
            break;
        case MAIN:
            if (isMainThread) {
                //If the onMsg1(msg1) subscription method is defined in the main thread and the msg1 event sending thread is also in the main thread
                //Reflection calling method [e.g. onMsg1.invoke(firstActivity, msg1)]
                invokeSubscriber(subscription, event);
            } else {
                //If the onMsg1(msg1) subscription method is defined to be processed in the main thread and the msg1 event sending thread is scheduled in the sub thread = > handler thread
                mainThreadPoster.enqueue(subscription, event);
            }
            break;
        case MAIN_ORDERED:
            if (mainThreadPoster != null) {
                mainThreadPoster.enqueue(subscription, event);
            } else {
                // temporary: technically not correct as poster not decoupled from subscriber
                invokeSubscriber(subscription, event);
            }
            break;
        case BACKGROUND:
            if (isMainThread) {
                backgroundPoster.enqueue(subscription, event);
            } else {
                invokeSubscriber(subscription, event);
            }
            break;
        case ASYNC:
            asyncPoster.enqueue(subscription, event);
            break;
        default:
            throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
    }
}

Reflection calls the subscription method that subscribes to the event event

void invokeSubscriber(Subscription subscription, Object event) {
    try {
        //Reflection call method
        subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
    } catch (InvocationTargetException e) {
        handleSubscriberException(subscription, event, e.getCause());
    } catch (IllegalAccessException e) {
        throw new IllegalStateException("Unexpected exception", e);
    }
}

Sticky event call process

Stickiness: that is, you can send events by post, and the subscription method registered after post can also receive events (get the sent events from the message cache) and start consumption

EventBus.getDefault().postSticky(new Msg());
public void postSticky(Object event) {
    synchronized (stickyEvents) {
        //Cache sticky events
        stickyEvents.put(event.getClass(), event);
    }
    // Should be posted after it is putted, in case the subscriber wants to remove immediately
    post(event);
}

If current subscription method is declared sticky, it is executed immediately after subscription

// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
    //...

    //The current subscription method is a sticky property. Call the subscription method directly
    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);
        }
    }
}

Topics: Android