preface

To be a good Android developer, you need a complete set ofThe knowledge systemHere, let us grow together into what we think ~.

Before you know it, the Android mainstream tripartite library source code analysis series is coming to an end. This time, I will analyze the source code of EventBus in Android in detail, together with you to uncover the veil behind it.

A simple example

1. First, define the event entity to be passed

public class CollectEvent { ... }
Copy the code

2. Prepare subscribers: Declare and annotate your subscription method

@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(CollectEvent event) {
    LogHelper.d("OK");
}
Copy the code

3. Register and unregister your subscribers in 2, the class in the subscription

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

@Override
public void onStop() {
    super.onStop();
    EventBus.getDefault().unregister(this);
}
Copy the code

4. Send events

EventBus.getDefault().post(new CollectEvent());
Copy the code

Before the formal explanation, I think it is necessary to explain some basic concepts in detail. We all know that before EventBus, developers used one of Android’s four major components, broadcast, for messaging between components, so why use an EventBus mechanism instead of broadcast? Mainly because:

  • Broadcast: Time consuming, easy to capture (insecure).
  • Event bus: More resource – saving, more efficient, can pass information to a variety of objects outside of native.

So, again, what is an event bus?

As shown in the figure below, the event bus mechanism notifies objects of various events by logging them, using the observer pattern. (Of course, you can also send basic data types like int, String, etc as an event)

What are the pros and cons of EventBus? Here is a brief summary:

  • Advantages: Low overhead, more elegant and concise code, decoupled sender and receiver, dynamic setting of event processing threads and priorities.
  • Disadvantages: Each event must have a custom event class, increasing maintenance costs.

EventBus is an extension of the Observer mode. What is the Observer mode?

The observer pattern, also known as the publisk-subscribe pattern, defines a one-to-many dependency between objects. Whenever the state of one object changes, other objects are notified and automatically updated.

Observer mode has the following roles:

  • Abstract observed: Stores all registered observer objects in a collection.
  • Observer-specific: All registered observers are notified when the internal state changes.
  • Abstract Observer: defines an update interface that updates itself when the observed state changes.
  • Concrete observer: An update interface that implements abstract observers.

Here I give you a simple example to further understand the idea of the observer model:

First, create an abstract observer

public interface observer {
    
    public void update(String message);
}
Copy the code

2. Next, create the concrete observer

public class WeXinUser implements observer { private String name; public WeXinUser(String name) { this.name = name; } @Override public void update(String message) { ... }}Copy the code

3. Then, create the abstract observed

public interface observable {
    
    public void addWeXinUser(WeXinUser weXinUser);
    
    public void removeWeXinUser(WeXinUser weXinUser);
    
    public void notify(String message);
}
Copy the code

4. Finally, create the specific observed

public class Subscription implements observable { private List<WeXinUser> mUserList = new ArrayList(); @Override public void addWeXinUser(WeXinUser weXinUser) { mUserList.add(weXinUser); } @Override public void removeWeXinUser(WeXinUser weXinUser) { mUserList.remove(weXinUser); } @Override public void notify(String message) { for(WeXinUser weXinUser : mUserList) { weXinUser.update(message); }}}Copy the code

In specific use, we can use it as follows:

Subscription subscription = new Subscription();

WeXinUser hongYang = new WeXinUser("HongYang");
WeXinUser rengYuGang = new WeXinUser("RengYuGang");
WeXinUser liuWangShu = new WeXinUser("LiuWangShu");

subscription.addWeiXinUser(hongYang);
subscription.addWeiXinUser(rengYuGang);
subscription.addWeiXinUser(liuWangShu);
subscription.notify("New article coming");
Copy the code

Here, hongYang, rengYuGang, liuWangShu, etc. subscribe to my wechat public account and receive the latest updates (wexinuser.update ()) whenever my public account publishes articles (subscription.notify()). (PS: Of course, this is all YY, the fact is, I do not have a wechat official account -0v0-)

Of course, EventBus’s observer mode is different from the normal observer mode. It uses the extended observer mode to subscribe and distribute events. In fact, the extended observer mode refers to the use of EventBus as a mediator, removing many responsibilities.

After learning how EventBus works, we noticed that every time we register, we have to unregister. Why is that?

Because register is a strong reference, it prevents objects from being reclaimed, leading to memory leaks. So you must free the object’s memory in the unregister method.

For those of you who used eventBus2.x, how does it differ from eventBus3.x?

  • Eventbus2.x uses runtime annotations that reflect all methods of a registered class to complete the registration, thus impacting performance.
  • Eventbus3. x uses compile-time annotations. Java files are compiled into.class files, and the class files are packaged. Writing translated into. The class file, the EventBus will use EventBusAnnotationProcessor annotation processor reads @ the Subscribe () annotations and parsing, processing the information, then generate Java classes to hold all the subscriber’s subscription information. This creates an index relationship to a file or class and compiles it into apK.
  • Object pool caching has been used since EventBus3.0 to reduce the overhead of creating objects.

In addition to EventBus, RxBus is a popular EventBus. How does it compare to EventBus?

  • 1. RxJava Observable has onError, onComplete and other state callbacks.
  • 2. Rxjava avoids callback hell by using composition rather than nesting.
  • 3, Rxjava thread scheduling design is more excellent, more simple to use.
  • 4. Rxjava can use a variety of operators to make chain calls to achieve complex logic.
  • 5. The information efficiency of Rxjava is higher than eventBus2. x and lower than EventBus3.x.

Now that we know the difference between EventBus and RxBus, what should we think about when choosing an EventBus for a new project?

Simply, if RxJava is used in your project, use RxBus, otherwise use EventBus3.x.

The source code for EventBus will be analyzed in the following order:

  • 1. Subscriber: eventbus.getDefault ().register(this);
  • Eventbus.getdefault ().post(new CollectEvent());
  • 3. Subscriber: eventbus.getDefault ().unregister(this).

Now, let’s explore EventBus

Second, the EventBus. GetDefault (). The register (this)

We’ll start with getDefault(), which gets an EventBus instance:

public static EventBus getDefault() { if (defaultInstance == null) { synchronized (EventBus.class) { if (defaultInstance  == null) { defaultInstance = new EventBus(); } } } return defaultInstance; }Copy the code

The singleton pattern of double check and lock is used in getDefault() to create the EventBus instance.

Next, we see what happens in the default constructor of EventBus:

private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();

public EventBus() {
    this(DEFAULT_BUILDER);
}
Copy the code

In the default constructor of EventBus, another of its parameterized constructors is called, passing in an object DEFAULT_BUILDER of type EventBusBuilder. The EventBusBuilder here is clearly an EventBus builder so that EventBus can add custom parameters and install a custom default EventBus instance.

Let’s look at the construction of EventBusBuilder again:

public class EventBusBuilder {

    ...

    EventBusBuilder() {
    }
    
    ...
    
}
Copy the code

The EventBusBuilder constructor does nothing, so we continue to look at the EventBus constructor with arguments:

private final Map<Class<? >, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType; private final Map<Object, List<Class<? >>> typesBySubscriber; private final Map<Class<? >, Object> stickyEvents; EventBus(EventBusBuilder builder) { ... // 1 subscriptionsByEventType = new HashMap<>(); // 2 typesBySubscriber = new HashMap<>(); // 3 stickyEvents = new ConcurrentHashMap<>(); // 4 mainThreadSupport = builder.getMainThreadSupport(); mainThreadPoster = mainThreadSupport ! = null ? mainThreadSupport.createPoster(this) : null; backgroundPoster = new BackgroundPoster(this); asyncPoster = new AsyncPoster(this); . // 5 subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes, builder.strictMethodVerification, builder.ignoreGeneratedIndex); // Select some columns from builder to assign information about subscription... // 6 executorService = builder.executorService; }Copy the code

In comment 1, a subscriptionsByEventType object is created. You can see that it is a subscriptionsByEventType object of type HashMap with a key of type Event. Value is the Subscription list. Subscription is a Subscription Object that holds two important fields, one of which is a subscriber of type Object, which is the registered Object (usually an Activity Object in Android). The other SubscriberMethod is the SubscriberMethod annotated by @subscribe, which holds an important field eventType, Class<? > represents the type of Event. In comment 2, a typesBySubscriber object of Map type is created. Its key is the subscriber object, and its value is the linked list of all events in the subscriber object. In daily use, the typesBySubscriber object is only used to determine whether an object has been registered. A new stickyEvents object of type ConcurrentHashMap is created at comment 3, which is a field dedicated to sticky event handling, with key as the Class object for the event and value as the current event. Some students may not know sticky Event, here is the explanation:

  • We all know that normal events are registered and sent before they can be received; Sticky events can also be received if you subscribe to the event after sending it. In addition, sticky events are stored in memory, and every time you enter, you will search for the latest sticky events in memory, unless you manually unregister them.

In comment 4, there are three new event transmitters of different types, summarized here:

  • MainThreadPoster: The mainthread event sender enqueue(subscription, event) enqueues the subscription information and the corresponding event through its mainThreadposter.enqueue (subscription, event) method and sends a message through the handler. The method is executed in the handleMessage of the handler.
  • BackgroundPoster: The background event sender, which adds methods to a background queue through its enqueue() and finally executes them through the thread pool, adds the synchronized keyword to Executor’s execute() method and sets the control flag, Ensure that only one and only one task is executed by the thread pool at any one time.
  • AsyncPoster: The implementation logic is similar to backgroundPoster. Unlike backgroundPoster, which guarantees that only one task can be executed by the thread pool at any time, asyncPoster runs asynchronously and can receive multiple tasks at the same time.

Going back to the line in Comment 5, where you create a subscriberMethodFinder object that is queried from the subscription method detached from EventBus, we often see the idea of composition over inheritance in good source code. Note 6: A default thread pool object is pulled from Builder by Executors’ newCachedThreadPool() method. It is an open/open thread pool with no upper limit.

Now that we’ve analyzed these core fields, the rest of the tutorial is a bit easier. Let’s look at EventBus’s regist() method:

public void register(Object subscriber) { Class<? > subscriberClass = subscriber.getClass(); // 1 List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass); synchronized (this) { for (SubscriberMethod subscriberMethod : subscriberMethods) { // 2 subscribe(subscriber, subscriberMethod); }}}Copy the code

In comment 1, get the list of subscription methods subscriberMethods based on the current registered class. In comment 2, an enhanced for loop is used to make the Subsciber object subscribe to each SubscriberMethod in subscriberMethods.

Next we look at the findSubscriberMethods() method of the SubscriberMethodFinder:

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 { subscriberMethods = findUsingInfo(subscriberClass); } if (subscriberMethods.isEmpty()) { throw new EventBusException("Subscriber " + subscriberClass + " and its super classes have no public methods with the @Subscribe annotation"); } else { METHOD_CACHE.put(subscriberClass, subscriberMethods); return subscriberMethods; }}Copy the code

In comment 1, it returns directly if there is a list of subscription methods for the subscriberClass object in the cache. Note 2: The ignoreGeneratedIndex field is used to determine whether to use generated APT code to optimize finding received events. If enabled, Information about the receiving event method is quickly obtained by subscriberInfoIndexes. If we do not plug EventBus’s APT into our project, we can set the ignoreGeneratedIndex field to false to improve performance. The default value of ignoreGeneratedIndex is false, so the findUsingInfo() method is executed and the subscriberMethods generated will be added to the cache if they succeed, and an exception will be thrown if they fail.

Next we look at the SubscriberMethodFinder findUsingInfo() method:

private List<SubscriberMethod> findUsingInfo(Class<? > subscriberClass) { // 1 FindState findState = prepareFindState(); findState.initForSubscriber(subscriberClass); // 2 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 { // 3 findUsingReflectionInSingleClass(findState); } findState.moveToSuperclass(); } // 4 return getMethodsAndRelease(findState); }Copy the code

In comment 1, we create a new FindState class by calling the SubscriberMethodFinder’s prepareFindState() method:

private static final FindState[] FIND_STATE_POOL = new FindState[POOL_SIZE]; private FindState prepareFindState() { // 1 synchronized(FIND_STATE_POOL) { for (int i = 0; i < POOL_SIZE; i++) { FindState state = FIND_STATE_POOL[i]; if (state ! = null) { FIND_STATE_POOL[i] = null; return state; } } } // 2 return new FindState(); }Copy the code

At comment 1, the available FindState is first fetched from the FIND_STATE_POOL, or FindState pool (where POOL_SIZE is 4), and if not, a new FindState object is created directly from the code at comment 2.

Next, let’s examine the FindState class:

static class FindState {
    ....
    void initForSubscriber(Class<?> subscriberClass) {
        this.subscriberClass = clazz = subscriberClass;
        skipSuperClasses = false;
        subscriberInfo = null;
    }
    ...
}
Copy the code

It’s an internal class for SubscriberMethodFinder, a method that does the initialization, reclaiming objects, and so on.

Let’s go back to the SubscriberMethodFinder() method at Comment 2 of the SubscriberMethodFinder:

private SubscriberInfo getSubscriberInfo(FindState findState) { 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) { SubscriberInfo info = index.getSubscriberInfo(findState.clazz); if (info ! = null) { return info; } } } return null; }Copy the code

In the previous initialization, the subscriberInfo and subscriberInfoIndexes fields of findState were empty, so null is returned.

Then we look at comment 3 findUsingReflectionInSingleClass () method:

private void findUsingReflectionInSingleClass(FindState findState) { Method[] methods; try { // This is faster than getMethods, especially when subscribers are fat classes like Activities methods = findState.clazz.getDeclaredMethods(); } catch (Throwable th) { methods = findState.clazz.getMethods(); findState.skipSuperClasses = true; } for (Method method: methods) { int modifiers = method.getModifiers(); if ((modifiers & Modifier.PUBLIC) ! = 0 && (modifiers & MODIFIERS_IGNORE) == 0) { Class<? > [] parameterTypes = method.getParameterTypes(); if (parameterTypes.length == 1) { Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class); if (subscribeAnnotation ! = null) {// key Class<? > eventType = parameterTypes[0]; if (findState.checkAdd(method, eventType)) { 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"); }}}Copy the code

It’s a long method, and it does something like this:

  • 1. Get all declared methods in the subscriber class by reflection, and then look for methods annotated with @SUBSCRIBE.
  • 2, after after a test to check findState. SubscriberMethods exists, if not, will the method name, threadMode, priority, whether it is sticky method information such as packaging to SubscriberMethod object, It is finally added to the subscriberMethods list.

Finally, we move on to the getMethodsAndRelease() method at comment 4:

private List<SubscriberMethod> getMethodsAndRelease(FindState findState) { // 1 List<SubscriberMethod> subscriberMethods  = new ArrayList<>(findState.subscriberMethods); // 2 findState.recycle(); // 3 synchronized(FIND_STATE_POOL) { for (int i = 0; i < POOL_SIZE; i++) { if (FIND_STATE_POOL[i] == null) { FIND_STATE_POOL[i] = findState; break; } } } // 4 return subscriberMethods; }Copy the code

Here, the saved subscriberMethods are first fetched from findState in comment 1. At comment 2, reclaim all the saved objects in findState. In note 3, store findState in the findState pool for next use to improve performance. Finally, in comment 4, return subscriberMethods. The subscribe method is then called at the end of EventBus’s register() method:

public void register(Object subscriber) { Class<? > subscriberClass = subscriber.getClass(); List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass); synchronized (this) { for (SubscriberMethod subscriberMethod : subscriberMethods) { subscribe(subscriber, subscriberMethod); }}}Copy the code

Let’s look at what the subscribe() method does:

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) { Class<? > eventType = subscriberMethod.eventType; Subscription newSubscription = new Subscription(subscriber, subscriberMethod); // 1 CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType); 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); } } int size = subscriptions.size(); // 2 for (int i = 0; i <= size; i++) { if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) { subscriptions.add(i, newSubscription); break; } } // 3 List<Class<? >> subscribedEvents = typesBySubscriber.get(subscriber); if (subscribedEvents == null) { subscribedEvents = new ArrayList<>(); typesBySubscriber.put(subscriber, subscribedEvents); } subscribedEvents.add(eventType); // 4 if (subscriberMethod.sticky) { if (eventInheritance) { 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

First, in comment 1, a CopyOnWriteArrayList will be found in subscriptionsByEventType according to the subscriberMethod eventType. If not, create a new CopyOnWriteArrayList and place this CopyOnWriteArrayList in subscriptionsByEventType. In comment 2, add the newSubscription object, which is a Subscription class containing subscriber and subscriberMethod information, with a priority judgment indicating that it was added according to priority. The higher the priority is, the higher the priority is, the higher the priority is. In comment 3, add typesBySubscriber, which is used in the isRegister() method of EventBus to determine whether the Subscriber object has been registered. Finally, at comment 4, it determines if it is a sticky event. If it is sticky, invoked checkPostStickyEventToSubscription () method.

We went on to look at the checkPostStickyEventToSubscription () method:

private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
    if (stickyEvent != null) {
        postToSubscription(newSubscription, stickyEvent, isMainThread());
    }
}
Copy the code

As you can see, the postToSubscription() method is called to send sticky events, which we’ll look at at the end, and see how events are posted.

Third, the EventBus. GetDefault (). The post (new CollectEvent ())

public void post(Object event) {
    // 1
    PostingThreadState postingState = currentPostingThreadState.get();
    List <Object> eventQueue = postingState.eventQueue;
    eventQueue.add(event);
    // 2
    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()) {
                postSingleEvent(eventQueue.remove(0), postingState);
            }
        } finally {
            postingState.isPosting = false;
            postingState.isMainThread = false;
        }
    }
}
Copy the code

Note 1, currentPostingThreadState here is a ThreadLocal object of type, PostingThreadState was stored inside, PostingThreadState contains an eventQueue and other flag bits.

private final ThreadLocal <PostingThreadState> currentPostingThreadState = new ThreadLocal <PostingThreadState> () { @Override protected PostingThreadState initialValue() { return new PostingThreadState(); }}; final static class PostingThreadState { final List <Object> eventQueue = new ArrayList<>(); boolean isPosting; boolean isMainThread; Subscription subscription; Object event; boolean canceled; }Copy the code

The incoming event is then stored in the eventQueue of a variable PostingThreadState in the current thread. In comment 2, the postSingleEvent() method is finally called, which we continue to look at:

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error { Class<? > eventClass = event.getClass(); boolean subscriptionFound = false; // 1 if (eventInheritance) { // 2 List<Class<? >> eventTypes = lookupAllEventTypes(eventClass); int countTypes = eventTypes.size(); for (int h = 0; h < countTypes; h++) { Class<? > clazz = eventTypes.get(h); subscriptionFound |= // 3 postSingleEventForEventType(event, postingState, clazz); } } else { subscriptionFound = postSingleEventForEventType(event, postingState, eventClass); } if (! subscriptionFound) { ... }}Copy the code

If it is set to true, it will determine whether the parent Event needs to be launched when the Event is launched. If it is set to true, it will determine whether the parent Event needs to be launched when the Event is launched. Set to false. Can improve some performance. Next, in comment 2, lookupAllEventTypes() is called, which simply fetches the class list of events and their superclasses and interfaces. Of course, repeated fetches can affect performance, so it also caches eventTypesCache. This eliminates the need to call the getSuperclass() method repeatedly. Finally, in the annotations 3 invokes postSingleEventForEventType () method, we look at this method:

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 = false; 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; }Copy the code

As you can see, the corresponding Subscriptions object was taken directly from subscriptionsByEventType based on the Event type, and the postToSubscription() method was called.

At this point we look again at the postToSubscription() method:

private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
    switch (subscription.subscriberMethod.threadMode) {
        case POSTING:
            invokeSubscriber(subscription, event);
            break;
        case MAIN:
            if (isMainThread) {
                invokeSubscriber(subscription, event);
            } else {
                mainThreadPoster.enqueue(subscription, event);
            }
            break;
        case MAIN_ORDERED:
            if (mainThreadPoster != null) {
                mainThreadPoster.enqueue(subscription, event);
            } else {
                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("Unknow thread mode: " + subscription.subscriberMethod.threadMode);
    }
}
Copy the code

As you can see from the above, we use threadMode to determine which thread to execute the method in:

  • POSTING: Implement invokeSubscriber() internally using reflection.
  • 2, the MAIN: If so, the call is reflected directly. Otherwise, mainThreadPoster’s enqueue() method is called to queue the current method, and a message is sent via handler. The method is executed in the handleMessage of the handler.
  • 3, MAIN_ORDERED: Similar to MAIN, but ensures that it is executed sequentially.
  • BACKGROUND: determines whether the UI thread is currently in use. If not, the call will be reflected directly. If yes, the method will be added to a queue in the BACKGROUND by the enqueue() method of backgroundPoster, and finally executed by the thread pool. Note that backgroundPoster adds the synchronized keyword to Executor’s execute() method and sets the control flag to ensure that only one and only one task will be executed by the thread pool at any one time.
  • 5, ASYNC: The implementation is similar to BACKGROUND, where the task is added to a queue in the BACKGROUND and eventually called by a thread pool in Eventbus that uses the same thread pool as in BACKGROUND logic. The thread pool is created by using Executors’ newCachedThreadPool() method. The thread pool has no upper limit. Unlike backgroundPoster, which guarantees that only one and only one task will be executed by the thread pool at any time, asyncPoster runs asynchronously and can receive multiple tasks at the same time.

After analyzing the value of EventBus’s post() method, let’s look at its unregister().

Fourth, the EventBus. GetDefault (). The unregister (this)

Its core source code is as follows:

public synchronized void unregister(Object subscriber) { List<Class<? >> subscribedTypes = typesBySubscriber.get(subscriber); if (subscribedTypes ! = null) { for (Class<? > eventType : subscribedTypes) { //1 unsubscribeByEventType(subscriber, eventType); } // 2 typesBySubscriber.remove(subscriber); }}Copy the code

First, in comment 1, all subscription information for subscriptionsByEventType is removed from the unsubscribeByEventType() method. Finally, in comment 2, you remove the registered object and its corresponding Event linked list.

Finally, let’s look at how sticky events are handled in EventBus.

Five, the EventBus. GetDefault. PostSticky (new CollectEvent ())

If you want to fire a sticky event, you need to pass the postSticky() method of EventBus. The internal source code is as follows:

public void postSticky(Object event) {
    synchronized (stickyEvents) {
        // 1
        stickyEvents.put(event.getClass(), event);
    }
    // 2
    post(event);
}
Copy the code

Put the event into stickyEvents at comment 1, followed by post() at comment 2 to send the event. When we analyzed the last part of the register() method, the source code for sticky events was as follows:

if (subscriberMethod.sticky) {
    Object stickyEvent = stickyEvents.get(eventType);
    if (stickyEvent != null) {
        postToSubscription(newSubscription, stickyEvent, isMainThread());
    }
}
Copy the code

As you can see, whether the current event is a sticky event is determined here, and if so, the event is taken from stickyEvents and the postToSubscription() method is executed.

At this point, the EventBus source code analysis is complete.

Six, summarized

EventBus’s source code is relatively simple except for ButterKnife in the Android mainstream tripartite library source code analysis series. However, some of its ideas and designs are worth learning from. For example, it uses the FindState reuse pool to reuse FindState objects, and uses the synchronized keyword here and there for some optimization of code block synchronization. The core logic of EventBus is to make use of subscriptionsByEventType, an important list, to store the subscription object, that is, the method that receives events, in this list, and to query and execute the corresponding method when publishing events. So far, Android mainstream tripartite library source code analysis series to this end ~

Imagine a self from the future who is very confident, very successful, and has everything you want right now. What would he say to you right now? You do what he says, and in 10 years, you become him.Copy the code
Reference links:

EventBus V3.1.1 source code

2. Android advanced Light

3. Android componentized architecture

EventBus Design Zen

Learn EventBus 3 EventBus mechanism from source code


Thank you for reading this article and I hope you can share it with your friends or technical group, it means a lot to me.

I hope we can be friends inGithub,The Denver nuggetsTo share knowledge.