1, Register
public void register(Object subscriber) { if (AndroidDependenciesDetector.isAndroidSDKAvailable() && !AndroidDependenciesDetector.areAndroidComponentsAvailable()) { // Crash if the user (developer) has not imported the Android compatibility library. throw new RuntimeException("It looks like you are using EventBus on Android, " + "make sure to add the \"eventbus\" Android library to your dependencies."); } Class<?> subscriberClass = subscriber.getClass(); //-------------------------------------------------------1 List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass); synchronized (this) { for (SubscriberMethod subscriberMethod : subscriberMethods) { //------------------------------------------------2 subscribe(subscriber, subscriberMethod); } } }
1. Get the method annotated by @ Subscriber in the current class. See findSubscriberMethods() for details
2. Traverse the methods annotated by @ Subscriber, save the annotated methods and store them in subscriptionsByEventType and typesBySubscriber. See subscribe() for details
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) { //------------------------------------------------------1 List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass); if (subscriberMethods != null) { return subscriberMethods; } //---------------------------------------------------------2 if (ignoreGeneratedIndex) { subscriberMethods = findUsingReflection(subscriberClass); } else { //--------------------------------------------------------3 subscriberMethods = findUsingInfo(subscriberClass); } if (subscriberMethods.isEmpty()) { throw new EventBusException("Subscriber " + subscriberClass + " and its super classes have no public methods with the @Subscribe annotation"); } else { //--------------------------------------------------------4 METHOD_CACHE.put(subscriberClass, subscriberMethods); return subscriberMethods; } }
1. Method of obtaining subscriber annotation from cache
2. The default is false. If true, the method of querying subscriber annotation through reflection. See findUsingReflectionInSingleClass() for details
4. Add the method to the cache
private void findUsingReflectionInSingleClass(FindState findState) { Method[] methods; try { // ----------------------------------------------1 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) { 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); } findState.skipSuperClasses = true; } //---------------------------------------------------2 for (Method method : methods) { //---------------------------------------------------3 int modifiers = method.getModifiers(); //-----------------------------------------------4 if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) { //-----------------------------------------------5 Class<?>[] parameterTypes = method.getParameterTypes(); if (parameterTypes.length == 1) { //---------------------------------------6 Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class); if (subscribeAnnotation != null) { Class<?> eventType = parameterTypes[0]; if (findState.checkAdd(method, eventType)) { ThreadMode threadMode = subscribeAnnotation.threadMode(); //-------------------------------7 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"); } } }
1. Find all methods of registered objects
3. Get the modifier of the method, i.e. public, private, etc
4. Only view methods where the modifier is public
5. Return the parameter type of the method
6. View only the methods annotated by @ subscribe
7. Add the method to the FindState collection
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) { //-------------------------------------------------------1 Class<?> eventType = subscriberMethod.eventType; //-------------------------------------------------------2 Subscription newSubscription = new Subscription(subscriber, subscriberMethod); //-------------------------------------------------------3 CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType); //-------------------------------------------------------4 if (subscriptions == null) { subscriptions = new CopyOnWriteArrayList<>(); subscriptionsByEventType.put(eventType, subscriptions); } else { if (subscriptions.contains(newSubscription)) { throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event " + eventType); } } //--------------------------------------------------------5 int size = subscriptions.size(); for (int i = 0; i <= size; i++) { if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) { subscriptions.add(i, newSubscription); break; } } //-------------------------------------------------------6 List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber); if (subscribedEvents == null) { subscribedEvents = new ArrayList<>(); typesBySubscriber.put(subscriber, subscribedEvents); } subscribedEvents.add(eventType); 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); } } }
1. Get the event EventType, such as String or user-defined object
2. Construct a subscription event according to the subscriber and subscription method
3. Find subscription events according to EventType to distribute and handle events
4. Traverse the subscription event, find the position with lower priority than the subscription event in the subscriptions, and then insert it. Here, judge whether the priority of the subscriberMethod subscription method is higher than that of the subscriberMethod in the collection. If so, insert the newSubscription, which also indicates that the priority in the subscription is higher, This will be obtained first when the event is distributed.
5. Obtain the collection of events subscribed by the subscriber through the subscriber
2, Post
public void post(Object event) { //---------------------------------------------------------1 PostingThreadState postingState = currentPostingThreadState.get(); List<Object> eventQueue = postingState.eventQueue; eventQueue.add(event); if (!postingState.isPosting) { postingState.isMainThread = isMainThread(); postingState.isPosting = true; if (postingState.canceled) { throw new EventBusException("Internal error. Abort state was not reset"); } try { while (!eventQueue.isEmpty()) { //------------------------------------------------2 postSingleEvent(eventQueue.remove(0), postingState); } } finally { postingState.isPosting = false; postingState.isMainThread = false; } } }
1.PostingThreadState stores event queue and thread state information
2.post a single event. See postSingleEvent() for details
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error { Class<?> eventClass = event.getClass(); boolean subscriptionFound = false; if (eventInheritance) { //------------------------------------------------------1 List<Class<?>> eventTypes = lookupAllEventTypes(eventClass); int countTypes = eventTypes.size(); for (int h = 0; h < countTypes; h++) { Class<?> clazz = eventTypes.get(h); //----------------------------------------------2 subscriptionFound |= postSingleEventForEventType(event, postingState, clazz); } } else { subscriptionFound = postSingleEventForEventType(event, postingState, eventClass); } 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)); } } }
1. Get the Class object collection of all parent classes of the Class object of the current Event and read it from the cache first. For details, see lookupAllEventTypes()
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) { CopyOnWriteArrayList<Subscription> subscriptions; synchronized (this) { subscriptions = subscriptionsByEventType.get(eventClass); } if (subscriptions != null && !subscriptions.isEmpty()) { for (Subscription subscription : subscriptions) { postingState.event = event; postingState.subscription = subscription; boolean aborted; try { postToSubscription(subscription, event, postingState.isMainThread); aborted = postingState.canceled; } finally { postingState.event = null; postingState.subscription = null; postingState.canceled = false; } if (aborted) { break; } } return true; } return false; }
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) { //---------------------------------------------1 switch (subscription.subscriberMethod.threadMode) { case POSTING: //----------------------------------------2 invokeSubscriber(subscription, event); break; case MAIN: if (isMainThread) { invokeSubscriber(subscription, event); } else { //-----------------------------------------3 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); } }
1. Take out the thread mode of the subscription method, and then process it separately according to the thread mode
2. Internal direct reflection call
3. Add the current method to the queue, then send a message through the handler, execute the method in the handler's handleMessage, and finally use reflection call
3, UnRegister
public synchronized void unregister(Object subscriber) { List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber); if (subscribedTypes != null) { for (Class<?> eventType : subscribedTypes) { //All subscription information of this subscriber is removed for subscriptionsByEventType unsubscribeByEventType(subscriber, eventType); } //Removed the registered object and all its corresponding Event linked lists typesBySubscriber.remove(subscriber); } else { 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) { 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); if (subscription.subscriber == subscriber) { subscription.active = false; subscriptions.remove(i); i--; size--; } } } }