preface

In the project, I encountered some problems related to EventBus data, so I referred to the source code to sort out the whole data process, and finally sorted out the output.

Introduction to the

EventBus is a publish/subscribe EventBus based on the observer mode. It solves the problems of unsafe and time-consuming communication between components. Instead of Intent,Handler, and BroadCast, it transmits messages between fragments, activities, services, and threads.

The advantages and disadvantages

Advantages -> Simplify communication between components, achieve decoupling to make business simple and efficient, you can set the event processing thread and priority. Disadvantages -> Many event classes need to be maintained.

use

1. Define events:

    public static class MessageEvent { /* Additional fields if needed */ }
Copy the code

2. Prepare subscribers: Declare and annotate your subscription method, specifying the thread mode (optional)

@Subscribe(threadMode = ThreadMode.MAIN)  
public void onMessageEvent(MessageEvent event) {/* Do something */};
Copy the code

Register and unregister subscribers in the class used, such as in Activity/fragment:

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

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

3. Sending event:

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

Internal implementation

1.EventBus.getDefault().register(this);

Register a given subscriber to receive events. After the subscriber is no longer interested in receiving the event, {@link #unregister (Object)} must be called.

Public void register(Object subscriber) {// Get the subscriber Class Object Class<? > subscriberClass = subscriber.getClass(); / / 1.5 List < SubscriberMethod > subscriberMethods = subscriberMethodFinder. FindSubscriberMethods (subscriberClass); Synchronized (this) {// subscribe for (SubscriberMethod SubscriberMethod: subscriberMethods) { //2 subscribe(subscriber, subscriberMethod); }}}Copy the code

Gets a collection of its subscribed events from a Class object

  • 1.SubscriberMethodFinder#List findSubscriberMethods(Class
    subscriberClass)
List<SubscriberMethod> findSubscriberMethods(Class<? > subscriberClass) { //private static final Map<Class<? >, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>(); // The Map key is the class that registered the event (such as an Activity), Value is the method that holds the event class (MessageEvent) and the method that handles the event (onMessageEvent(MessageEvent Event)).  subscriberMethods = METHOD_CACHE.get(subscriberClass); if (subscriberMethods ! = null) { return subscriberMethods; } if (ignoreGeneratedIndex) { subscriberMethods = findUsingReflection(subscriberClass); } else {//1.1 Get a list of methods for all subscribers subscriberMethods = findUsingInfo(subscriberClass); } / / no is empty Is added to the map if (subscriberMethods isEmpty ()) {throw new EventBusException (" Subscriber "+ subscriberClass +" the and its super classes have no public methods with the @Subscribe annotation"); } else { METHOD_CACHE.put(subscriberClass, subscriberMethods); return subscriberMethods; }} 1.1 SubscriberMethodFinder#List<SubscriberMethod> findUsingInfo(Class<? > subscriberClass) private List<SubscriberMethod> findUsingInfo(Class<? > subscriberClass) {// From FIND_STATE_POOL (private static final FindState[] FIND_STATE_POOL = new FindState[POOL_SIZE]; POOL_SIZE->4) prepareFindState = prepareFindState(); findState.initForSubscriber(subscriberClass); while (findState.clazz ! = null) { findState.subscriberInfo = getSubscriberInfo(findState); // The first time is null 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 {/ / by reflecting access to all the methods in the class of subscriber findUsingReflectionInSingleClass (findState); } findState.moveToSuperclass(); } return getMethodsAndRelease(findState); }Copy the code
  • 2.EventBus#subscribe(Object subscriber, SubscriberMethod subscriberMethod)
Private void subscribe(Object subscriber, SubscriberMethod SubscriberMethod) {// Obtain the Class Object Class<? > eventType = subscriberMethod.eventType; Subscription newSubscription = newSubscription (subscriber, subscriberMethod); // Find CopyOnWriteArrayList by event type CopyOnWriteArrayList<Subscription> SUBSCRIPTIONS = subscriptionsByEventType.get(eventType); if (subscriptions == null) { subscriptions = new CopyOnWriteArrayList<>(); subscriptionsByEventType.put(eventType, subscriptions); } else {// register only once if (subscriptions.contains(newSubscription)) {throw new EventBusException("Subscriber "+ subscriber.getClass() + " already registered to event " + eventType); } // Add subscription(subscriber, subscriberMethod) to CopyOnWriteArrayList based on priority int size = subscriptions.size(); // Add subscription(subscriber, subscriberMethod) to CopyOnWriteArrayList based on priority 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; } } List<Class<? >> subscribedEvents = typesBySubscriber.get(subscriber); if (subscribedEvents == null) { subscribedEvents = new ArrayList<>(); typesBySubscriber.put(subscriber, subscribedEvents); } subscribedEvents.add(eventType); / / if it is sticky call checkPostStickyEventToSubscription method send viscous events 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

The registration section deals primarily with stickiness events by getting the subscription methods in the subscriber’s Class object, binding the methods to the subscriber, and then handling stickiness events.

2. EventBus.getDefault().post(new MessageEvent());

Public void post (Object event) {/ / currentPostingThreadState (ThreadLocal) get PostingThreadState 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 {// Send while (!) if there are still events in the event queue. eventQueue.isEmpty()) { //3 postSingleEvent(eventQueue.remove(0), postingState); } } finally { postingState.isPosting = false; postingState.isMainThread = false; } } } final static class PostingThreadState { final List<Object> eventQueue = new ArrayList<>(); // Thread event queue Boolean isPosting; // Whether the Boolean isMainThread is being sent; // Whether to send Subscription in the main thread; // Wrap an Object event about an event; // The event object is Boolean canceled; // Whether to cancel sending}Copy the code
  • 3.EventBus#postSingleEvent(Object event, PostingThreadState postingState)
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error { Class<? > eventClass = event.getClass(); boolean subscriptionFound = false; If (eventInheritance) {// find all Class objects, including superclasses and interfaces List<Class<? >> eventTypes = lookupAllEventTypes(eventClass); int countTypes = eventTypes.size(); for (int h = 0; h < countTypes; h++) { Class<? > clazz = eventTypes.get(h); / / call postSingleEventForEventType method to send events 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)); } } } private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<? > eventClass) { CopyOnWriteArrayList<Subscription> subscriptions; Synchronized (this) {/ / get all subscribers subscriptions = subscriptionsByEventType. Get (eventClass); } if (subscriptions ! = null && ! subscriptions.isEmpty()) { for (Subscription subscription : subscriptions) { postingState.event = event; postingState.subscription = subscription; boolean aborted = false; Try {/ / send events 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, Object event,) {private void postToSubscription(Subscription, Object event, Boolean isMainThread) {switch (subscription. SubscriberMethod. ThreadMode) {case POSTING: / / direct reflection method is called without switching threads invokeSubscriber(subscription, event); break; If (isMainThread) {invokeSubscriber(Subscription, event); } else {// Otherwise add a queue to the main thread calling 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 {// Subscription () invokeSubscriber; } break; Case ASYNC:// Calls asyncPoster. Enqueue (Subscription, event) directly in the child thread; break; default: throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode); }}Copy the code

The post method basically adds the event to the collection and sends the collection along with the data, then gets all the subscribers to the event class, determines which thread they need to run on and determines which thread to run on.

3.EventBus.getDefault().postSticky(new MessageEvent());

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

Add the data to the sticky event list and determine whether to post the event in the registe method

4.EventBus.getDefault().unregister(this);

Synchronized public synchronized void unregister(Object subscriber) {// synchronized public synchronized void unregister(Object subscriber) { >> subscribedTypes = typesBySubscriber.get(subscriber); if (subscribedTypes ! = null) { for (Class<? > eventType: subscribedTypes) {unsubscribeByEventType(subscriber, eventType); } // Remove typesbysubscriber.remove (subscriber); } else { logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass()); }}Copy the code

conclusion

This is the end of the EventBus source process, which is relatively simple compared to the rest of the source code. If there are any shortcomings in this article, please point out.