usage method
The use method is very simple, according to Official documents The introduction is divided into three steps.
Step 1: define events
public static class MessageEvent { }
Step 2: prepare subscribers
Define subscription methods to handle received events.
@Subscribe(threadMode = ThreadMode.MAIN) public void onMessageEvent(MessageEvent event) {/* Do something */};
And register and log off in Activity and Fragment according to their life cycle.
@Override public void onStart() { super.onStart(); EventBus.getDefault().register(this); } @Override public void onStop() { super.onStop(); EventBus.getDefault().unregister(this); }
Step 3: send event
Send the defined event to the subscriber.
EventBus.getDefault().post(new MessageEvent());
Source code analysis
The use method is very simple. Here is the source code according to the use steps.
Preparing subscribers
The step of preparing subscribers is divided into two steps: registration and logout and preparing subscription methods.
Prepare subscription method
It can also be seen from the usage method that the subscription method is implemented by annotating @ Subscribe.
@Documented Indicates that the element using this annotation should be javadoc Or similar tools @Retention(RetentionPolicy.RUNTIME) Annotations are retained in the class In the file, it will also be recognized during operation, so the reflection mechanism can be used to obtain annotation information. @Target({ElementType.METHOD}) Scope of use, indicating the method used to describe public @interface Subscribe { /** * Thread mode, the default is POSTING */ ThreadMode threadMode() default ThreadMode.POSTING; /** * Whether it is a sticky event. The default value is false */ boolean sticky() default false; /** * Priority of event subscription. The default is 0. * When the subscribers are in the same thread mode, the priority will work, and the subscribers with high priority will receive the pushed events first. */ int priority() default 0; }
Three meta annotations are used in the source code:
- @Documented: indicates that the element using this annotation should be documented by javadoc or similar tools.
- @Retention(RetentionPolicy.RUNTIME): indicates that the annotation will be retained in the class file and recognized during operation, so the annotation information can be obtained using the reflection mechanism.
- @Target({ElementType.METHOD}): indicates the scope of use. This annotation is used to describe the method.
Each subscriber will have a thread mode. The thread mode determines which thread its subscription method runs in. These thread modes are:
- POSTING: the default thread mode. The thread that sends the event will process the event in the corresponding thread.
- MAIN: if the event is sent in the MAIN thread, the event is processed directly in the MAIN thread. Conversely, if an event is sent in a child thread, you need to switch to the MAIN thread to process the event. (used more in Android)
- MAIN_ORDERED: no matter which thread sends events, the events will be queued and executed orderly on the main thread.
- Backgroup: if an event is sent in a child thread, the event is processed directly in the child thread. On the contrary, if you send an event in the main thread, you need to put the event in the message queue, switch to the sub thread, and use the thread pool to process the event in order. (always use this mode if not used in Android)
- ASYNC: no matter which thread sends an event, the event will be put into the message queue and processed on the child thread through the thread pool. This mode should be used if the execution of the subscriber method may take some time, such as network access.
register
As described in the above usage method, only one line is required to complete the subscriber registration.
EventBus.getDefault().register(this);
The EventBus.getDefault() method actually returns an instance of EventBus through singleton mode. Let's take a look at the register method directly.
public void register(Object subscriber) { //Get subscriber class Class<?> subscriberClass = subscriber.getClass(); //Get the subscription method according to the subscriber class List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass); synchronized (this) { for (SubscriberMethod subscriberMethod : subscriberMethods) { //Traverse the subscription method and call the subscription method subscribe(subscriber, subscriberMethod); } } }
The parameter subscriber in the method is this passed in when we call the method, so it means Activity and Fragment. To sum up, we get the class object of the subscriber, find its subscription method, and call the subscribe subscription method to subscribe.
So the key point is to see how he finds the subscription method and what he does in the subscription method? Go down:
Subscription method found
/** * Subscription method found * * @param subscriberClass Subscriber class * @return Subscription method list */ List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) { //First find the subscription method from the cache //METHOD_ CACHE -> Map<Class<?>, List < subscribermethod > >: key is the subscriber class and value is the subscription method list List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass); if (subscriberMethods != null) { //If there is in the cache, it is returned directly return subscriberMethods; } //Whether to use subscriber index. ignoreGeneratedIndex is false by default if (ignoreGeneratedIndex) { subscriberMethods = findUsingReflection(subscriberClass); } else { subscriberMethods = findUsingInfo(subscriberClass); } if (subscriberMethods.isEmpty()) { //If no subscription method is found, an exception is thrown to remind the user to use the @ Subscribe method to declare the subscription method //That is, if the user register s but does not have any @ Subscribe subscription method, an exception will be thrown to prompt the user throw new EventBusException("Subscriber " + subscriberClass + " and its super classes have no public methods with the @Subscribe annotation"); } else { //If the subscription method is not empty, put it into the cache to facilitate reuse next time. key is the class name of the subscription class METHOD_CACHE.put(subscriberClass, subscriberMethods); return subscriberMethods; } }
To sum up, first go to method according to the subscriber class_ Search in cache. If it is found, it will directly return the subscriber method list. If it is not found, it will determine whether to use findUsingInfo or findUsingReflection method according to whether to use subscriber index. Find the subscription method list and add it to METHOD_CACHE is convenient for the next use. On the contrary, if the subscription method cannot be found, an exception will be thrown.
Next, let's look at how to find and return the subscriber list. First, let's look at the findUsingReflection method:
private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) { FindState findState = prepareFindState(); findState.initForSubscriber(subscriberClass); while (findState.clazz != null) { findUsingReflectionInSingleClass(findState); //Find the parent class. The specific implementation depends on the skisupersuperclasses flag bit findState.moveToSuperclass(); } //Returns a list of subscription methods return getMethodsAndRelease(findState); } /** * Extract subscription information through class reflection * * @param findState */ private void findUsingReflectionInSingleClass(FindState findState) { Method[] methods; try { // This is faster than getMethods, especially when subscribers are fat classes like Activities // getMethods(): returns all public methods declared by a class or interface and inherited from superclasses and superinterfaces. // getDeclaredMethods(): returns the methods declared by the class, including public, protected, default (package), but excluding inherited methods // Therefore, this method is faster than the getMethods method, especially in complex classes, such as Activity. methods = findState.clazz.getDeclaredMethods(); } catch (Throwable th) { // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149 try { 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) { //Consider using the EventBus annotation processor to avoid reflection msg += ". Please consider using EventBus annotation processor to avoid reflection."; } else { msg += ". Please make this class visible to EventBus annotation processor to avoid reflection."; } //The method in this class cannot be found. An exception is thrown throw new EventBusException(msg, error); } // Because the getMethods() method has obtained the method of the superclass, it is not set here to check the superclass findState.skipSuperClasses = true; } //Traverse the methods found for (Method method : methods) { //Get method modifier: public - > 1; private->2; protected->4; static->8; final->16 int modifiers = method.getModifiers(); //If it is public and not abstract | static if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) { //Get method parameter type Class<?>[] parameterTypes = method.getParameterTypes(); if (parameterTypes.length == 1) { //Get annotation of method Subscribe Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class); if (subscribeAnnotation != null) { //The first parameter is the event type Class<?> eventType = parameterTypes[0]; //Check whether the subscription method for subscribing to this type of event has been added. True - > not added; False - > added if (findState.checkAdd(method, eventType)) { //Not added. Create a new subscription method object according to the found parameters and add it to the subscriber methods list 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"); } } }
For the checkAdd method, go further:
final Map<Class, Object> anyMethodByEventType = new HashMap<>(); final Map<String, Class> subscriberClassByMethodKey = new HashMap<>(); 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. // Usually, a subscriber will not have multiple subscription methods to subscribe to the same type of event (in this case, the user writes blindly in JB) // Extension: return value description of HashMap put() method: // If the same key already exists, the value corresponding to the previous key is returned, and the new value of the key overwrites the old value; // If it is a new key, null is returned; Object existing = anyMethodByEventType.put(eventType, method); if (existing == null) { //Indicates that there is no subscription method to subscribe to this type of event return true; } else { //A subscription method for subscribing to this type of event already exists //existing is the subscription method that stores anyMethodByEventType to subscribe to unified type events 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()); // Method name > event type // Intention: if multiple subscription methods in the same class subscribe to the same event, all subscription methods will receive the event when the event is distributed. String methodKey = methodKeyBuilder.toString(); Class<?> methodClass = method.getDeclaringClass(); Class<?> methodClassOld = subscriberClassByMethodKey.put(methodKey, methodClass); /* Extension: isAssignableFrom() method description: * Whether the Class represented by the current Class object is the parent Class, super interface, or the same type of the Class represented by the Class object passed in the parameter. * If yes, it returns true; otherwise, it returns false. */ //methodClassOld is empty, which means it has not been added, or methodClassOld is the parent class of methodClass if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) { // Only add if not already found in a sub class // Add only if it is not found in the subclass return true; } else { // Revert the put, old class is further down the class hierarchy subscriberClassByMethodKey.put(methodKey, methodClassOld); return false; } }
Using Subscriber Index
We just saw how to directly call the findUsingReflection method to find the subscription method using reflection, and then we saw how to use the Subscribe index to find the subscription method.
Note: we highly recommend the EventBus annotation processor with its subscriber index. This will avoid some reflection related problems seen in the wild.
As described on the official website, EventBus recommends that you use the annotation processor to avoid using reflection to find subscription methods at run time, but at compile time.
Use the annotation processor to generate the index
apply plugin: 'kotlin-kapt' // ensure kapt plugin is applied dependencies { def eventbus_version = '3.2.0' implementation "org.greenrobot:eventbus:$eventbus_version" kapt "org.greenrobot:eventbus-annotation-processor:$eventbus_version" } kapt { arguments { arg('eventBusIndex', 'com.example.myapp.MyEventBusIndex') } }
rebuild the project to generate MyEventBusIndex, for example:
/** This class is generated by EventBus, do not edit. */ public class MyEventBusIndex implements SubscriberInfoIndex { private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX; static { //Key - > subscriber class object; Value - > subscription information SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>(); putIndex(new SimpleSubscriberInfo(MainActivity.class, true, new SubscriberMethodInfo[] { new SubscriberMethodInfo("firstOnTestEvent", TestEvent.class, ThreadMode.MAIN), new SubscriberMethodInfo("messageEvent", MessageEvent.class, ThreadMode.BACKGROUND, 6, true), })); putIndex(new SimpleSubscriberInfo(SecondActivity.class, true, new SubscriberMethodInfo[] { new SubscriberMethodInfo("onTestEvent", TestEvent.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; } } }
Then pass the MyEventBusIndex instance to EventBus.
EventBus eventBus = EventBus.builder().addIndex(new MyEventBusIndex()).build();
Next, let's look back at the Subscriber Index lookup method findUsingInfo()
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) { //Get a FindState object from the FindState pool and initialize it FindState findState = prepareFindState(); findState.initForSubscriber(subscriberClass); //A while loop is used here, which means that after the subclass is searched, it will go to the parent class to continue searching while (findState.clazz != null) { //Go to the index to find subscription information findState.subscriberInfo = getSubscriberInfo(findState); if (findState.subscriberInfo != null) { SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods(); //Traversal subscription method for (SubscriberMethod subscriberMethod : array) { //Check whether the subscription method has been added if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) { //Not added, add the found subscription method to the subscription method list findState.subscriberMethods.add(subscriberMethod); } } } else { //Use the radial method to find subscription methods findUsingReflectionInSingleClass(findState); } //Find parent class findState.moveToSuperclass(); } //Returns a list of subscription methods return getMethodsAndRelease(findState); } private SubscriberInfo getSubscriberInfo(FindState findState) { //subscriberInfo is not empty, which means that the subscription information has been found. You need to find the parent class this time if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) { SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo(); //Confirm that the parent class is found this time if (findState.clazz == superclassInfo.getSubscriberClass()) { return superclassInfo; } } //subscriberInfoIndexes is added by EventBus.addIndex(MyEventBusIndex()) if (subscriberInfoIndexes != null) { for (SubscriberInfoIndex index : subscriberInfoIndexes) { //This is to execute the getSubscriberInfo method in the MyEventBusIndex class to obtain subscription information SubscriberInfo info = index.getSubscriberInfo(findState.clazz); if (info != null) { return info; } } } return null; }
The principle is also very simple, that is, the index is generated during compilation, so we don't need to find it through reflection at runtime. We can find it directly through the index file. In addition, through the generated file, we can clearly see the distribution of our declared subscription methods.
Precautions for using Subscriber Index:
- @The Subscribe method and its classes must be public.
- Event classes must be public.
- @Subscribe cannot be used in anonymous classes.
subscribe
Above, we have seen how to find the subscription method, and then we will take a further look at how to implement the subscription action in the subscription method.
/** * A map collection of event types and subscription object lists * * key -> eventType Event type * value -> Subscription Subscription object list, where subscription is an encapsulated class of subscribers and subscription methods */ private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType; /** * A map collection that records a list of all event types that subscribers subscribe to * * key -> subscriber * value -> List of all event types subscribed to by subscribers */ private final Map<Object, List<Class<?>>> typesBySubscriber; private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) { //Get event type parameters through subscription method Class<?> eventType = subscriberMethod.eventType; //A subscription object is constructed by subscriber and subscription method Subscription newSubscription = new Subscription(subscriber, subscriberMethod); //Through the event type, find the collection of subscription objects in the form of CopyOnWriteArrayList CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType); if (subscriptions == null) { //If the subscription object collection is empty, it indicates that the subscription method subscribed to this type of event has not been registered. //Create a new list, and then put the event type and the new list into the subscriptionsByEventType Map subscriptions = new CopyOnWriteArrayList<>(); subscriptionsByEventType.put(eventType, subscriptions); } else { //If the subscription object collection already contains the newSubscription if (subscriptions.contains(newSubscription)) { //Throw an exception to prompt the user that the subscriber has subscribed to this type of event throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event " + eventType); } } //Traverse the list of subscription objects int size = subscriptions.size(); for (int i = 0; i <= size; i++) { //If there is a declared priority in the subscription method, the subscription method is added to the specified location according to the priority //Otherwise, add the subscription method to the end of the subscription object list if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) { subscriptions.add(i, newSubscription); break; } } //Find the type list of all events subscribed to by subscribers subscribedEvents List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber); //If the subscriber does not have any subscription method, that is, subscribedEvents is empty if (subscribedEvents == null) { //Create a new list to put all the event types subscribed by this subscriber subscribedEvents = new ArrayList<>(); //And put the subscriber and the new list into the typesBySubscriber map typesBySubscriber.put(subscriber, subscribedEvents); } //Add the event type to the event type list subscribedEvents.add(eventType); //If the subscription method supports sticky events if (subscriberMethod.sticky) { //Whether to consider the hierarchy of event classes. The default value is true 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(); //Check sending sticky events checkPostStickyEventToSubscription(newSubscription, stickyEvent); } } } else { //Get sticky events based on event type Object stickyEvent = stickyEvents.get(eventType); //Check sending sticky events checkPostStickyEventToSubscription(newSubscription, stickyEvent); } } }
Note that the subscribe method needs to run in the synchronize block. You can refer to another article about synchronize Learn more about the Synchronize keyword
cancellation
After reading the registration, we then look at the logout. The logout method is also very simple. It can be completed in one sentence of code.
EventBus.getDefault().unregister(this);
Let's take a closer look at the unregister method.
public synchronized void unregister(Object subscriber) { //Find a list of all event types subscribed to by subscribers List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber); if (subscribedTypes != null) { //Traverse the event type list for (Class<?> eventType : subscribedTypes) { //Unregister subscriber by event type unsubscribeByEventType(subscriber, eventType); } //Remove the subscriber from the typesBySubscriber map typesBySubscriber.remove(subscriber); } else { //log prompt: logout is performed without registration logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass()); } } private void unsubscribeByEventType(Object subscriber, Class<?> eventType) { //Find the list of related subscription objects by event type List<Subscription> subscriptions = subscriptionsByEventType.get(eventType); if (subscriptions != null) { int size = subscriptions.size(); //Traverse the list of subscription objects for (int i = 0; i < size; i++) { Subscription subscription = subscriptions.get(i); if (subscription.subscriber == subscriber) { //Find the subscriber from the subscription object list, change its active state to false, and remove it from the subscription object list subscription.active = false; subscriptions.remove(i); i--; size--; } } } }
Send event
Then it is to send events. Sending events is also very simple. It can be done in the same sentence.
EventBus.getDefault().post(new MessageEvent());
Let's take a closer look at the source code.
public void post(Object event) { //PostingThreadState is an encapsulated class of events and send states PostingThreadState postingState = currentPostingThreadState.get(); List<Object> eventQueue = postingState.eventQueue; //Add the event to the event queue eventQueue.add(event); if (!postingState.isPosting) { //Check whether it is in the main thread postingState.isMainThread = isMainThread(); //Set to sending postingState.isPosting = true; //Check whether to cancel sending. If you cancel sending, an exception will be thrown if (postingState.canceled) { throw new EventBusException("Internal error. Abort state was not reset"); } try { while (!eventQueue.isEmpty()) { //Traverse the event queue and send events one by one postSingleEvent(eventQueue.remove(0), postingState); } } finally { //Reset send status postingState.isPosting = false; postingState.isMainThread = false; } } } private void postSingleEvent(Object event, PostingThreadState postingState) throws Error { //Gets the class object of the event Class<?> eventClass = event.getClass(); boolean subscriptionFound = false; //Whether to consider the hierarchy of event classes. The default value is true if (eventInheritance) { //Find all event types of the superclass List<Class<?>> eventTypes = lookupAllEventTypes(eventClass); int countTypes = eventTypes.size(); for (int h = 0; h < countTypes; h++) { Class<?> clazz = eventTypes.get(h); //Send events according to event type subscriptionFound |= postSingleEventForEventType(event, postingState, clazz); } } else { //Send events according to event type subscriptionFound = postSingleEventForEventType(event, postingState, eventClass); } //No subscription object was found to subscribe to this type of event, that is, there is no subscription method to subscribe to this type of event 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)); } } } /** * According to the event type, send the event to the subscription method subscribed to the event type * @param event * @param postingState * @param eventClass * @return true->Find the subscription method that subscribed to this type of event; False - > there is no subscription method to subscribe to this type of event */ private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) { //Subscription object list CopyOnWriteArrayList<Subscription> subscriptions; synchronized (this) { //Query the list of subscribers subscribing to events of this type according to the event type subscriptions = subscriptionsByEventType.get(eventClass); } //If the subscription object list is not empty, events are sent to these subscribers one by one if (subscriptions != null && !subscriptions.isEmpty()) { for (Subscription subscription : subscriptions) { postingState.event = event; postingState.subscription = subscription; boolean aborted; try { //Send events to subscription objects postToSubscription(subscription, event, postingState.isMainThread); aborted = postingState.canceled; } finally { //Reset PostingState postingState.event = null; postingState.subscription = null; postingState.canceled = false; } if (aborted) { break; } } return true; } return false; } private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) { //According to the thread mode selected by the subscriber, choose which thread mode to use to distribute and process the event switch (subscription.subscriberMethod.threadMode) { case POSTING: //Call subscription methods directly using reflection invokeSubscriber(subscription, event); break; case MAIN: if (isMainThread) { //If you are currently in the main thread, direct reflection calls the subscription method invokeSubscriber(subscription, event); } else { //Use the Handler to switch to the main thread, and finally execute invokeSubscriber mainThreadPoster.enqueue(subscription, event); } break; case MAIN_ORDERED: if (mainThreadPoster != null) { //Queue the events and execute them orderly on the main thread mainThreadPoster.enqueue(subscription, event); } else { // temporary: technically not correct as poster not decoupled from subscriber invokeSubscriber(subscription, event); } break; case BACKGROUND: if (isMainThread) { //If you are currently in the main thread, you will use the thread pool to switch to the child thread for processing, and finally call invokeSubscriber backgroundPoster.enqueue(subscription, event); } else { //If you are currently in a child thread, the event is processed directly in that child thread invokeSubscriber(subscription, event); } break; case ASYNC: //No matter what thread you are in, you will eventually use the thread pool to switch to the child thread for processing, and eventually call invokeSubscriber asyncPoster.enqueue(subscription, event); break; default: throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode); } } void invokeSubscriber(Subscription subscription, Object event) { try { //Call subscription method with reflection subscription.subscriberMethod.method.invoke(subscription.subscriber, event); } catch (InvocationTargetException e) { handleSubscriberException(subscription, event, e.getCause()); } catch (IllegalAccessException e) { throw new IllegalStateException("Unexpected exception", e); } }
To sum up in one sentence: find the corresponding subscription object list according to the event type, and then determine which thread handles the event according to the thread mode of the subscription object.
Send sticky events
If you have not registered a subscriber before sending a normal event, the event you send will not be received and executed, and the event will be recycled.
The sticky event is different. You can register the subscriber after sending the sticky event. Once the subscription is completed, the subscriber will receive the sticky event.
Let's see how it is implemented from the source code!
EventBus.getDefault().postSticky(new MessageEvent("Hello everyone!"));
Unlike sending normal events, sticky events are sent using the postSticky() method.
/** * Used to store sticky events * * key -> Class object for sticky events * value -> Viscous event */ private final Map<Class<?>, Object> stickyEvents; public void postSticky(Object event) { //Sync lock to store sticky events in stickyEvents synchronized (stickyEvents) { stickyEvents.put(event.getClass(), event); } //After the event is added, just like normal events, call the post() method to send the event post(event); }
A stickyEvents collection is used to save sticky events. After storing them, the post() method is called as usual.
?? Um??, At this time, I have a question. For the above usage scenario, I send sticky events first, and then register the subscription. At this time, I execute the post method to send events. There is no corresponding subscriber at all. It must have failed to send. So, think about it. To achieve this effect, the subscriber should send the saved event after registering the subscription.
Go back to register - > subscribe method:
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) { //Get event type parameters through subscription method Class<?> eventType = subscriberMethod.eventType; //A subscription object is constructed by subscriber and subscription method Subscription newSubscription = new Subscription(subscriber, subscriberMethod); ... .Omit some codes. ... //If the subscription method supports sticky events if (subscriberMethod.sticky) { //Whether to consider the hierarchy of event classes. The default value is true if (eventInheritance) { Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet(); for (Map.Entry<Class<?>, Object> entry : entries) { Class<?> candidateEventType = entry.getKey(); //Is eventType a parent of candidateEventType if (eventType.isAssignableFrom(candidateEventType)) { Object stickyEvent = entry.getValue(); //Check sending sticky events checkPostStickyEventToSubscription(newSubscription, stickyEvent); } } } else { //Get sticky events based on event type Object stickyEvent = stickyEvents.get(eventType); //Check sending sticky events checkPostStickyEventToSubscription(newSubscription, stickyEvent); } } } private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) { //If the sticky event is not empty, send the event if (stickyEvent != null) { postToSubscription(newSubscription, stickyEvent, isMainThread()); } }
If it is true, in the registered subscription method, if the current subscription method supports sticky events, the subscriber will check whether there is a corresponding sticky event in the stickyEvents collection. If a sticky event is found, the subscriber will send the event.
At this point, the source code analysis of EventBus is finished. If you want to view all comments, you can click Github EventBus all comments View.
In fact, the biggest purpose of sharing articles is to wait for someone to point out my mistakes. If you find any mistakes, please point them out without reservation and ask for advice with an open mind. In addition, if you think the article is good and helpful to you, please give me a praise and be encouraged. Thank you ~ Peace ~!