usage method
Step 1: define events
Subscribed event class
public static class MessageEvent { /* Additional fields if needed */ }
Step 2: prepare subscribers
-
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 */};
-
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); } } }