Can be used in the application of message event transmission, convenient and fast, low coupling

1. Basic usage

public class EventBusMain extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.content_main); EventBus.getDefault().register(this); OnEvent1 @subscribe public void onEvent1(RemindBean bean) onEvent2(UserInfo bean){ } @Override protected voidonDestroy() { super.onDestroy(); EventBus.getDefault().unregister(this); }}Copy the code

When you need to send a message:

EventBus.getDefault().post(new RemindBean())
Copy the code

2. Source code interpretation

Put a schematic diagram on the official website, feel pretty clear:

The Publisher only needs to post an event and then does not need to worry about it. EventBus will internally distribute the events to the subscribers who subscribe to the event one by one. Well, that’s one thing.

I still remember that in the past, when I wanted to implement some data between two different activities, I completed it by defining an interface. After a long time, there were a lot of interfaces defined, and it was really not beautiful to review the code. So without further ado, let’s take a look at Eventbus, which everyone is using.

3. The first

 EventBus.getDefault().register(this);
Copy the code

getDefault():

EventBus is an example of a /** Convenience singletonfor apps using a process-wide EventBus instance. */
    public static EventBus getDefault() {
        if (defaultInstance == null) {
            synchronized (EventBus.class) {
                if(defaultInstance == null) { defaultInstance = new EventBus(); }}}return defaultInstance;
    }
Copy the code

Just like you subscribe to a newspaper, there are several important questions to be answered:

  • Who is the subscriber (Subscriber)?
  • What newspaper do you subscribe to (Event)?

Is what I think is important, so the step of register is the event that Subscriber tells the newspaper to subscribe to

public void register(Object subscriber) { - 1. Let's get the bytecode Class<? For the subscriber Class. > subscriberClass = subscriber.getClass(); - 2. Through the bytecode of this class, Get all subscribed events and store them in the List List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass); Synchronized (this) {-3. Loops through all subscription methods to associate subscriber and subscriberMethodfor(SubscriberMethod subscriberMethod : subscriberMethods) { subscribe(subscriber, subscriberMethod); }}}Copy the code

Let’s see how this finds the subscribing method based on the subscriberClass, findSubscriberMethods:

List<SubscriberMethod> findSubscriberMethods(Class<? List<SubscriberMethod> subscriberMethods = method_cache.get (subscriberClass); - 2. Confirm null the first timeif(subscriberMethods ! = null) {returnsubscriberMethods; } - 3. Look for defaultfalse, those who are interested can have a lookif (ignoreGeneratedIndex) {
            subscriberMethods = findUsingReflection(subscriberClass);
        } elseSubscriberMethods = findUsingInfo(subscriberClass); subscriberMethods = findUsingInfo(subscriberClass); }if (subscriberMethods.isEmpty()) {
            throw new EventBusException("Subscriber " + subscriberClass
                    + " and its super classes have no public methods with the @Subscribe annotation");
        } else{-5. Add it to the cache, key is subscriber; Value is: Methods method_cache. put(subscriberClass, subscriberMethods);returnsubscriberMethods; }}Copy the code

See: findUsingInfo (subscriberClass)

private List<SubscriberMethod> findUsingInfo(Class<? FindState = prepareFindState(); - 2. Store the subscriberClass of the subscriber. // void initForSubscriber(Class<? > subscriberClass) { // this.subscriberClass = clazz = subscriberClass; //} findState.initForSubscriber(subscriberClass);while(findState.clazz ! = null) {enter the loop // Get the subscriberInfo information, return 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{- 3. Enter here the findUsingReflectionInSingleClass (findState); } - 4. Look for in the parent class method findState. MoveToSuperclass (); }return getMethodsAndRelease(findState);
    }
Copy the code

FindUsingReflectionInSingleClass is as follows:

private void findUsingReflectionInSingleClass(FindState findState) { Method[] methods; try { // This is faster than getMethods, especially when subscribers are fat classes like Activities - 1. Through the subscriber bytecode to find all the life in the current class methods. The methods = findState clazz. GetDeclaredMethods (); } catch (Throwable th) { // Workaroundfor java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
            methods = findState.clazz.getMethods();
            findState.skipSuperClasses = true; } -2. Loop through all methodsfor(Method method : methods) { - 3. Int modifiers = method.getModifiers(); - 4. Determine the modifier. The modifier for the subscription method cannot be private or staticif((modifiers & Modifier.PUBLIC) ! = 0 && (modifiers & MODIFIERS_IGNORE) == 0) { - 5. Get all parameters of a method Class<? >[] parameterTypes = method.getParameterTypes(); - 6. Determine the number of parameters. Only one parameter can be specified in the subscription methodif(parameterTypes.length == 1) { - 7. Subscribe subscribeAnnotation = method.getannotation (subscrib.class); -8. The method that contains the subscribe annotation is the method of this class, other methods that do not conform may be the common methodif(subscribeAnnotation ! = null) {-9. Get first argument eventType Class<? > eventType = parameterTypes[0];if(findState.checkAdd(method, eventType)) { - 10. Annotation mode, is our logo on the annotation, mainThread, Posting, background, async ThreadMode ThreadMode = subscribeAnnotation. ThreadMode (); -11. Subscribe to a list of methods (method name, threadMode, priority, Whether the viscosity, etc.) are added to the collection subscriberMethods to findState. SubscriberMethods. Add (new SubscriberMethod (method, eventType, threadMode, subscribeAnnotation.priority(), subscribeAnnotation.sticky())); }}}else if(strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) { - 12. 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)) { - 13. Method declaringClass () = method.getDeclaringClass().getName() +"." + method.getName();
                throw new EventBusException(methodName +
                        " is a illegal @Subscribe method: must be public, non-static, and non-abstract"); }}}Copy the code

So we’ve saved everything in the findState class. Going back to our original method, we come to step three:

private List<SubscriberMethod> findUsingInfo(Class<? FindState = prepareFindState(); - 2. Store the subscriberClass of the subscriber. // void initForSubscriber(Class<? > subscriberClass) { // this.subscriberClass = clazz = subscriberClass; //} findState.initForSubscriber(subscriberClass);while(findState.clazz ! = null) {enter the loop // Get the subscriberInfo information, return 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{- 3. Enter here, had analysis above all information stored to findState findUsingReflectionInSingleClass (findState); } - 4. Look for in the parent class method findState. MoveToSuperclass (); }return getMethodsAndRelease(findState);
    }
Copy the code

In this getMethodsAndRelease (findState) :

private List<SubscriberMethod> getMethodsAndRelease(FindState findState) { - 1. And get the subscriberMethods List < SubscriberMethod > subscriberMethods = new ArrayList < > (findState. SubscriberMethods); findState.recycle(); synchronized (FIND_STATE_POOL) {for (int i = 0; i < POOL_SIZE; i++) {
                if (FIND_STATE_POOL[i] == null) {
                    FIND_STATE_POOL[i] = findState;
                    break; }}} - 2. Return the collectionreturn subscriberMethods;
    }
Copy the code

At this point, we know that the method event methods for all subscriptions were found according to the subscriber’s clazz

Back to the original first step register:

public void register(Object subscriber) { Class<? > subscriberClass = subscriber.getClass(); - 2. Complete the List < SubscriberMethod > subscriberMethods = subscriberMethodFinder. FindSubscriberMethods (subscriberClass); synchronized (this) {for(SubscriberMethod subscriberMethod : subscriberMethods) { - 3. Loop through all subscription methods and establish an association subscribe(subscriber, subscriberMethod) between subscribers; }}}Copy the code

Subscribe (subscriber, subscriberMethod) method:

// Must be called insynchronized block private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) { - 1. The bytecode Class<? > eventType = subscriberMethod.eventType; Subscription newSubscription = New Subscription(subscriber, subscriberMethod); SubscriptionsByEventType is null the first time, according to eventType CopyOnWriteArrayList<Subscription> SUBSCRIPTIONS = subscriptionsByEventType.get(eventType); - 4. The value must be null the first timeif(subscriptions == null) { subscriptions = new CopyOnWriteArrayList<>(); - 5. The key for the eventType, value is an object subscriptions subscriptionsByEventType. Put (eventType, subscriptions); }else{- Throws an exceptionif (subscriptions.contains(newSubscription)) {
                throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "+ eventType); }} -6. Acquire all subscriptions int size = subscriptions.size();for(int i = 0; i <= size; I++) {-7. Will determine the priority of each subscription method and add it to this subscriptions, according to priorityif (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
                subscriptions.add(i, newSubscription);
                break; }} -8. Get the List of subscribed methods <Class<? >> subscribedEvents = typesBySubscriber.get(subscriber);if(subscribedEvents == null) { subscribedEvents = new ArrayList<>(); - 9. Add to typesBySubscriber typesbysubscriber. put(subscriber, subscribedEvents) if null; } -10. Add the subscribedEvents event to the subscribedEvents collection. - 11. Determine whether the sticky event is relatedif (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); }}}Copy the code

At this point, if you follow me all the way to this point, you should get a little bit of a sense, but there’s one more part, which is register, and the first part is to subscribe, to store, and so on; That leaves the post(Event) method to distribute the event to the corresponding subscriber who has subscribed to the event.