EventBus EventBus EventBus

Source code version:

  • EventBus: 3.3.1

Navigation:

  • EventBus library -EventBus Library
  • EventBus library -EventBus Library
  • See more articles here: home page

use

Define events

class MessageEvent(val message: String)
Copy the code

Events are ordinary objects with no specific requirements.

The subscribe method

@Subscribe(threadMode = ThreadMode.MAIN)
fun onMessageEvent(event: MessageEvent) {
    Toast.makeText(applicationContext, event.message, Toast.LENGTH_SHORT).show()
}
Copy the code

Create a method that receives events, onMessageEvent, annotated with @SUBSCRIBE, and mark the thread that receives the event as the master thread.

Registration and logout

override fun onStart(a) {
    super.onStart()
    EventBus.getDefault().register(this)}override fun onStop(a) {
    EventBus.getDefault().unregister(this)
    super.onStop()
}
Copy the code

Call the eventbus.getDefault ().register(this) method to register and the eventbus.getDefault ().unregister(this) method to unregister within the subscriber.

In Android, you should usually register activities and fragments based on their life cycle. For the most part, onStart/onStop works fine:

Send the event

Ordinary events

EventBus.getDefault().post(MessageEvent("Hello everyone!"))
Copy the code

Viscous event

EventBus.getDefault().postSticky(MessageEvent("Hello everyone!"))
Copy the code

The source code

The Subscribe annotations

The Subscribe class

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
    // The threading model, which defaults to POSTING.
    ThreadMode threadMode(a) default ThreadMode.POSTING;

    // Is it a sticky event
    boolean sticky(a) default false;

    / / priority
    int priority(a) default 0;
}
Copy the code

Subscribe is an annotation class that can only be declared on methods and has three configurable options.

  • threadMode.threadModel, the default isPOSTINGPublish thread.
  • stickyWhether,viscousEvent, the default isfalse.
  • priority.priorityBy default,0.

ThreadMode class

public enum ThreadMode {
    POSTING,
    MAIN,
    MAIN_ORDERED,
    BACKGROUND,
    ASYNC
}
Copy the code

ThreadMode is an enumeration class and a thread model. There are five types of ThreadMode.

  • POSTING.Release the thread.
  • MAIN.The main thread.
  • MAIN_ORDERED.Main thread (ordered).
  • BACKGROUND.A background thread.
  • POASYNCTING.Asynchronous thread.

This is just a brief introduction. See EventBus post below for details.

EventBus creation

EventBus.getDefault()

EventBus –> getDefault method

public class EventBus {
    static volatile EventBus defaultInstance;
    private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
    
    // Get the default EventBus object, singleton mode.
    public static EventBus getDefault(a) {
        EventBus instance = defaultInstance;
        if (instance == null) {
            synchronized (EventBus.class) {
                instance = EventBus.defaultInstance;
                if (instance == null) {
                    // Call the no-argument constructor to create EventBus.
                    instance = EventBus.defaultInstance = newEventBus(); }}}returninstance; }}Copy the code

The eventbus.getDefault () method creates an EventBus object using the singleton mode.

new EventBus()

EventBus –> constructor

public class EventBus {
    private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
   
    public EventBus(a) {
        // Call the constructor with the EventBusBuilder parameter, passing in a default EventBusBuilder object.
        this(DEFAULT_BUILDER);
    }
    
	EventBus(EventBusBuilder builder) {
	// Initialize EventBus.. }}Copy the code

New EventBus(), which uses the default configuration of EventBusBuilder.

EventBus.builder()

EventBus –> Builder method

public static EventBusBuilder builder(a) {
    return new EventBusBuilder();
}
Copy the code

The eventBus.Builder () method creates the EventBusBuilder and returns it. Let’s look at how the EventBusBuilder class creates an EventBus.

installDefaultEventBus

EventBusBuilder –> installDefaultEventBus method

Install the default EventBus
public EventBus installDefaultEventBus(a) {
    synchronized (EventBus.class) {
        if(EventBus.defaultInstance ! =null) {
            throw new EventBusException("Default instance already exists." +
                    " It may be only set once before it's used the first time to ensure consistent behavior.");
        }
        // Call the build method to create EventBus and assign a value to the default instance.
        EventBus.defaultInstance = build();
        // Return the default instance.
        returnEventBus.defaultInstance; }}Copy the code

The installDefaultEventBus() method creates an EventBus object from build, assigns it to eventBus.defaultInstance, and returns an example.

Description:

  1. installDefaultEventBus()Method, must be inFor the first time,callEventBus.getDefault()beforeCall, otherwise throw an exception.
  2. installDefaultEventBus()Method, can onlyOne callWhen called again, an exception is thrown.

build

EventBusBuilder –> Build method

// Build an EventBus based on the current configuration.
public EventBus build(a) {
    return new EventBus(this);
}
Copy the code

The build() method creates an EventBus object and passes in the current EventBusBuilder instance. Let’s first look at the EventBusBuilder class and then look at the initialization of EventBus.

summary

  1. EventBusIt can be created in three ways,EventBus.getDefault(),new EventBus(),EventBus.builder().build()They all passEventBus(EventBusBuilder)To create.

EventBusBuilder

The EventBusBuilder builds the EventBus for the builder pattern, and it provides all configurable methods, as well as some other methods for retrieving them, which we’ll look at separately.

Configuration items

EventBusBuilder class

public class EventBusBuilder {
    // Default thread pool
    private final static ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool();
    
    boolean logSubscriberExceptions = true;
    boolean logNoSubscriberMessages = true;
    boolean sendSubscriberExceptionEvent = true;
    boolean sendNoSubscriberEvent = true;
    boolean throwSubscriberException;
    boolean eventInheritance = true;
    boolean ignoreGeneratedIndex;
    booleanstrictMethodVerification; ExecutorService executorService = DEFAULT_EXECUTOR_SERVICE; List<Class<? >> skipMethodVerificationForClasses; List<SubscriberInfoIndex> subscriberInfoIndexes; Logger logger; MainThreadSupport mainThreadSupport; EventBusBuilder() { }// Whether to print exception information when calling the subscription method. The default value is true.
    public EventBusBuilder logSubscriberExceptions(boolean logSubscriberExceptions) {
        this.logSubscriberExceptions = logSubscriberExceptions;
        return this;
    }

    // Whether to print exception information when there are no subscribers. The default value is true.
    public EventBusBuilder logNoSubscriberMessages(boolean logNoSubscriberMessages) {
        this.logNoSubscriberMessages = logNoSubscriberMessages;
        return this;
    }

    // Whether to send the SubscriberExceptionEvent event when the subscription method is invoked abnormally. The default value is true.
    public EventBusBuilder sendSubscriberExceptionEvent(boolean sendSubscriberExceptionEvent) {
        this.sendSubscriberExceptionEvent = sendSubscriberExceptionEvent;
        return this;
    }

    // Whether to send NoSubscriberEvent event when there are no subscribers. The default is true.
    public EventBusBuilder sendNoSubscriberEvent(boolean sendNoSubscriberEvent) {
        this.sendNoSubscriberEvent = sendNoSubscriberEvent;
        return this;
    }

    // Whether to throw a SubscriberException exception when the subscription method is invoked. The default value is false.
    public EventBusBuilder throwSubscriberException(boolean throwSubscriberException) {
        this.throwSubscriberException = throwSubscriberException;
        return this;
    }

    // Whether the event has inheritance, default is true.
    public EventBusBuilder eventInheritance(boolean eventInheritance) {
        this.eventInheritance = eventInheritance;
        return this;
    }
    
    // Provide a custom thread pool for EventBus for asynchronous and background event passing.
    public EventBusBuilder executorService(ExecutorService executorService) {
        this.executorService = executorService;
        return this;
    }

    // Skip method signature validation (currently unused)
    public EventBusBuilder skipMethodVerificationFor(Class
        clazz) {
        if (skipMethodVerificationForClasses == null) {
            skipMethodVerificationForClasses = new ArrayList<>();
        }
        skipMethodVerificationForClasses.add(clazz);
        return this;
    }

    // Whether to ignore the index generated by the annotation handler. Default is false.
    public EventBusBuilder ignoreGeneratedIndex(boolean ignoreGeneratedIndex) {
        this.ignoreGeneratedIndex = ignoreGeneratedIndex;
        return this;
    }

    // Whether to strictly validate the subscription method. Default is false.
    public EventBusBuilder strictMethodVerification(boolean strictMethodVerification) {
        this.strictMethodVerification = strictMethodVerification;
        return this;
    }

    // Add the index generated by the annotation processor
    public EventBusBuilder addIndex(SubscriberInfoIndex index) {
        if (subscriberInfoIndexes == null) {
            subscriberInfoIndexes = new ArrayList<>();
        }
        subscriberInfoIndexes.add(index);
        return this;
    }
    
    // Set the log handler
    public EventBusBuilder logger(Logger logger) {
        this.logger = logger;
        return this; }}Copy the code

The default value of the configuration item is the default value of the property.

1. Configure exception handling

Configuration items describe The default value
logSubscriberExceptions Call subscription method exception, whether to print exception information true
sendSubscriberExceptionEvent Call subscription method exception, whether to sendSubscriberExceptionEventThe event true
throwSubscriberException Call subscription method exception, whether to throwSubscriberExceptionabnormal false
logNoSubscriberMessages When there are no subscribers, whether to print exception information true
sendSubscriberExceptionEvent When there are no subscribers, whether to sendNoSubscriberEventThe event true

2. Annotate processor configuration

Configuration items describe The default value
ignoreGeneratedIndex Whether to ignoreAnnotation processorGenerated index false
addIndex addAnnotation processorGenerated index There is no

3. Other configurations

Configuration items describe The default value
eventInheritance Whether the event hasinheritance true
executorService(ExecutorService) forEventBusTo provide aCustom thread poolsFor asynchronous and background event passing. Executors.newCachedThreadPool()
strictMethodVerification Whether or notStrict verificationThe subscribe method false
logger Set up theLog handler There is no
skipMethodVerificationFor(Class<? >) Skip method validation (Not currently in use) There is no

getLogger

EventBusBuilder –> getLogger method

// Get the log handler
Logger getLogger(a) {
    if(logger ! =null) {
        return logger;
    } else {
        returnLogger.Default.get(); }}Copy the code

The getLogger() method, if a logger has been set by calling logger(), use the set logger, otherwise use the Default logger.default.get () logger. Let’s look at the Logger class.

The Logger class

public interface Logger {

    void log(Level level, String msg);

    void log(Level level, String msg, Throwable th);

    class JavaLogger implements Logger {
        protected final java.util.logging.Logger logger;

        public JavaLogger(String tag) {
            logger = java.util.logging.Logger.getLogger(tag);
        }

        @Override
        public void log(Level level, String msg) {
            // TODO Replace logged method with caller method
            logger.log(level, msg);
        }

        @Override
        public void log(Level level, String msg, Throwable th) {
            // TODO Replace logged method with caller methodlogger.log(level, msg, th); }}class SystemOutLogger implements Logger {

        @Override
        public void log(Level level, String msg) {
            System.out.println("[" + level + "]" + msg);
        }

        @Override
        public void log(Level level, String msg, Throwable th) {
            System.out.println("[" + level + "]"+ msg); th.printStackTrace(System.out); }}class Default {
        public static Logger get(a) {
	    // Determine whether AndroidComponents is available, and if so, use the AndroidComponents logger.
            if (AndroidComponents.areAvailable()) {
                return AndroidComponents.get().logger;
            }

            return newSystemOutLogger(); }}}Copy the code

The Logger class, the log handler, is an interface that has two log() methods implemented by JavaLogger and SystemOutLogger.

Logger. The Default. The get () method, through AndroidComponents. AreAvailable () method to determine whether AndroidComponents available, if available, use AndroidComponents Logger implementation class, Otherwise use SystemOutLogger, so let’s look at the AndroidComponents class.

AndroidComponents class

public abstract class AndroidComponents {
    // AndroidComponents class implementer
    private static final AndroidComponents implementation;

    static {
        // Implementer instance, if Android SDK is available, create AndroidComponents implementer instance and assign to it, otherwise assign null.
        implementation = AndroidDependenciesDetector.isAndroidSDKAvailable()
            ? AndroidDependenciesDetector.instantiateAndroidComponents()
            : null;
    }

    // AndroidComponents is available
    public static boolean areAvailable(a) {
        returnimplementation ! =null;
    }

    // Get the AndroidComponents implementation class instance
    public static AndroidComponents get(a) {
        return implementation;
    }

    public final Logger logger;
    public final MainThreadSupport defaultMainThreadSupport;

    // Create AndroidComponents, need Logger, MainThreadSupport.
    public AndroidComponents(Logger logger, MainThreadSupport defaultMainThreadSupport) {
        this.logger = logger;
        this.defaultMainThreadSupport = defaultMainThreadSupport; }}Copy the code

The AndroidComponents class holds Logger, MainThreadSupport, and areAvailable(), get() static methods.

Implementation properties for AndroidComponents class implements the class instance, through AndroidDependenciesDetector judgment, if the Android SDK is available, AndroidComponents implementation class instance is created and assigned to it, Otherwise the value is null, we look at AndroidDependenciesDetector class.

AndroidDependenciesDetector class

public class AndroidDependenciesDetector {

    // Check whether the Android SDK is available, its internal check is whether looper.getMainLooper () has a value.
    public static boolean isAndroidSDKAvailable(a) {

        try {
            // Reflection gets the Looper ClassClass<? > looperClass = Class.forName("android.os.Looper");
            // Reflection gets the Looper class getMainLooper Method
            Method getMainLooper = looperClass.getDeclaredMethod("getMainLooper");
            // reflection gets the return value of the Looper class getMainLooper method
            Object mainLooper = getMainLooper.invoke(null);
            // Check whether the return value of the Looper class getMainLooper method is null
            returnmainLooper ! =null;
        }
        catch (ClassNotFoundException ignored) {}
        catch (NoSuchMethodException ignored) {}
        catch (IllegalAccessException ignored) {}
        catch (InvocationTargetException ignored) {}

        return false;
    }

    private static final String ANDROID_COMPONENTS_IMPLEMENTATION_CLASS_NAME = "org.greenrobot.eventbus.android.AndroidComponentsImpl";

    AndroidComponentsImpl class AndroidComponentsImpl class AndroidComponentsImpl class AndroidComponentsImpl class
    public static boolean areAndroidComponentsAvailable(a) {

        try {
            Class.forName(ANDROID_COMPONENTS_IMPLEMENTATION_CLASS_NAME);
            return true;
        }
        catch (ClassNotFoundException ex) {
            return false; }}// Create the AndroidComponents implementation class, which creates the AndroidComponentsImpl class for reflection.
    public static AndroidComponents instantiateAndroidComponents(a) {

        try{ Class<? > impl = Class.forName(ANDROID_COMPONENTS_IMPLEMENTATION_CLASS_NAME);return (AndroidComponents) impl.getConstructor().newInstance();
        }
        catch (Throwable ex) {
            return null; }}}Copy the code

AndroidDependenciesDetector class, it for Android depend on the discoverer, it contains the following three static method.

  • isAndroidSDKAvailable()To determineAndroid SDKWhether it is available, its internal judgment isLooper.getMainLooper()Whether it has a value.
  • areAndroidComponentsAvailable()To determineAndroidComponentsIf it is available, its internal judgment is whether it is availableAndroidComponentsImplClass.
  • instantiateAndroidComponents()To createAndroidComponentsImplementation class, created internally for reflectionAndroidComponentsImplClass.

Description:

  1. isAndroidSDKAvailable(), by which you can determine whether it is inAndroidPlatform.
  2. areAndroidComponentsAvailable()It can be used to determine whether it is only referencedeventbus-javaLibrary, unreferencedeventbusLibrary (Description:eventbus-javaThe library isJavaLibrary,eventbusThe library isAndroidLibraries, referenceeventbusThe default is to referenceeventbus-java).

Let’s look at the AndroidComponentsImpl class again.

AndroidComponentsImpl class

public class AndroidComponentsImpl extends AndroidComponents {

    public AndroidComponentsImpl(a) {
        super(new AndroidLogger("EventBus"), newDefaultAndroidMainThreadSupport()); }}Copy the code

AndroidComponentsImpl class, it is AndroidComponents implementation class, its Logger is AndroidLogger, its MainThreadSupport DefaultAndroidMainThreadSupport. AndroidLogger class, its internal use Log for printing, the source code to see, we look at the next DefaultAndroidMainThreadSupport class.

DefaultAndroidMainThreadSupport class

public class DefaultAndroidMainThreadSupport implements MainThreadSupport {

    @Override
    public boolean isMainThread(a) {
        return Looper.getMainLooper() == Looper.myLooper();
    }

    @Override
    public Poster createPoster(EventBus eventBus) {
        return new HandlerPoster(eventBus, Looper.getMainLooper(), 10); }}Copy the code

DefaultAndroidMainThreadSupport class, as the default Android main thread support class, through the isMainThread () to determine whether it is in the main thread, through createPoster () to create the main thread Poster, For HandlerPoster.

Description:

  1. isMainThread()Method of useLooper.myLooper() == Looper.getMainLooper()In comparison, yesWaste of resources. becauseLooper.myLooper()Methods to obtainLooper, if theA new threadIf I call it,ThreadLocalWill createThreadLocalMap. I suggest you use:Looper.getMainLooper().getThread() == Thread.currentThread().

getMainThreadSupport

EventBusBuilder –> getMainThreadSupport method

// Get MainThread support
MainThreadSupport getMainThreadSupport(a) {
    if(mainThreadSupport ! =null) {
        // mainThreadSupport is not used
        return mainThreadSupport;
    } else if (AndroidComponents.areAvailable()) {
        / / AndroidComponents available, access to its MainThreadSupport, namely DefaultAndroidMainThreadSupport.
        return AndroidComponents.get().defaultMainThreadSupport;
    } else {
        return null; }}Copy the code

The getMainThreadSupport() method, if AndroidComponents is available, gets its (AndroidComponents simpl) MainThreadSupport DefaultAndroidMainThreadSupport namely.

summary

  1. EventBusBuilderforBuilder patternbuildEventBusIt provides allconfigurableMethods.
  2. getLogger()Method, if yesAndroidPlatform, isAndroidLogger, or forSystemOutLogger.
  3. getMainThreadSupport()Method, if yesAndroidPlatform, isDefaultAndroidMainThreadSupport, or fornull.

Now that we’ve covered EventBusBuilder, let’s look at the initialization of EventBus.

Initialization of EventBus

All ways to create an EventBus eventually lead to the EventBus builder constructor, which we’ll look at next.

EventBus –> constructor

private finalMap<Class<? >, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;private finalMap<Object, List<Class<? >>> typesBySubscriber;private finalMap<Class<? >, Object> stickyEvents;private final MainThreadSupport mainThreadSupport;
private final Poster mainThreadPoster;
private final BackgroundPoster backgroundPoster;
private final AsyncPoster asyncPoster;
private final SubscriberMethodFinder subscriberMethodFinder;
private final ExecutorService executorService;

private final boolean throwSubscriberException;
private final boolean logSubscriberExceptions;
private final boolean logNoSubscriberMessages;
private final boolean sendSubscriberExceptionEvent;
private final boolean sendNoSubscriberEvent;
private final boolean eventInheritance;

private final int indexCount;
private final Logger logger;

EventBus(EventBusBuilder builder) {
    // Log handler
    logger = builder.getLogger();

    // ============= The following three sets are important ==================
    // [event --> List of subscriptions for this event].
    // 1.key: Class of Event, value: List of Subscription objects
    // 2. It is easy to find all Subscription objects for this Event via the Event.
    // 3. For example, if an Event is MessageEvent and it is subscribed by class A, class B and class C, then there is A data in it.
    // Key is the Class of MessageEvent, and value is A List of three classes (A, B, C) Subscription.
    subscriptionsByEventType = new HashMap<>();
    // [subscriber --> a Map of the List of events to which the subscriber subscribes.
    // 1.key: the subscriber Object, value: the Class List of the Event.
    // 2. It makes it easy to find all the Event types subscribed to by the subscriber object.
    // 3. For example, if there are two events, MessageEvent and SayHelloEvent, which are subscribed by class A, then there is one data in it.
    // Key is an object of Class A, and value is A List with two pieces of data (MessageEvent, SayHelloEvent).
    typesBySubscriber = new HashMap<>();
    // [Sticky Event --> Sticky event object] Map.
    // 1.key: Event Class, value: Event object.
    // 2. It is easy to find instances of sticky events through the Class of events.
    stickyEvents = new ConcurrentHashMap<>();

    // ============= the following is important in event sending ==================
    / / main thread support, the Android platform for DefaultAndroidMainThreadSupport, otherwise null.
    mainThreadSupport = builder.getMainThreadSupport();
    // Main thread event sender, HandlerPoster for Android, null otherwise.mainThreadPoster = mainThreadSupport ! =null ? mainThreadSupport.createPoster(this) : null;
    // Background event sender
    backgroundPoster = new BackgroundPoster(this);
    // Asynchronous event sender
    asyncPoster = new AsyncPoster(this);

    / / = = = = = = = = = = = = = the following configuration for EventBusBuilder = = = = = = = = = = = = = = = = = =
    // Add the number of SubscriberInfoIndex indexes generated by the annotation processorindexCount = builder.subscriberInfoIndexes ! =null ? builder.subscriberInfoIndexes.size() : 0;
    // Subscription methods look up objects (more on this class and its construction parameters later)
    subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
            builder.strictMethodVerification, builder.ignoreGeneratedIndex);
    // Whether to print exception information when calling the subscription method. The default value is true.
    logSubscriberExceptions = builder.logSubscriberExceptions;
    // Whether to print exception information when there are no subscribers. The default value is true.
    logNoSubscriberMessages = builder.logNoSubscriberMessages;
    // Whether to send the SubscriberExceptionEvent event when the subscription method is invoked abnormally. The default value is true.
    sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
    // Whether to send NoSubscriberEvent event when there are no subscribers. The default is true.
    sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
    // Whether to throw a SubscriberException exception when the subscription method is invoked. The default value is false.
    throwSubscriberException = builder.throwSubscriberException;
    // Whether the event has inheritance, default is true.
    eventInheritance = builder.eventInheritance;
    / / thread pool for asynchronous events and the background, the default for the Executors. NewCachedThreadPool ().
    executorService = builder.executorService;
}
Copy the code

summary

  1. EventBustheInitialize the, created three very important collections,subscriptionsByEventType,typesBySubscriber,stickyEventsSee the comments above for a detailed explanation.
  2. Three were createdPoster.mainThreadPoster(i.e.HandlerPoster),backgroundPoster,asyncPosterFor details, see the following –Poster.
  3. To obtainEventBusBuilderAnd record the configuration.
  4. createSubscriberMethodFinder, used to findThe subscribe methodFor details, see the following –SubscriberMethodFinder.

The registration of the EventBus

EventBus –> register()

public void register(Object subscriber) {
    if(AndroidDependenciesDetector.isAndroidSDKAvailable() && ! AndroidDependenciesDetector.areAndroidComponentsAvailable()) {// If it is an Android platform but does not rely on the EventBus (Android) library, an exception is thrown.
        throw new RuntimeException("It looks like you are using EventBus on Android, " +
                "make sure to add the \"eventbus\" Android library to your dependencies.");
    }
    // get the subscriber's Class object.Class<? > subscriberClass = subscriber.getClass();// find all subscription methods for that subscriber through the subscriberMethodFinder object.
    List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
    // 3. Synchronize to ensure thread safety.
    synchronized (this) {
        // 4
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            // Subscribe. Pass in the subscriber object and the subscriber's subscription method object.subscribe(subscriber, subscriberMethod); }}}Copy the code

The register() method, for the registered subscriber method, finds all of the subscriber’s subscription methods through the subscriberMethodFinder, and then iterates to subscribe in sequence. We’ll cover SubscriberMethodFinder separately (see -SubscriberMethodFinder later), but we’ll start by looking at the SubscriberMethod class.

SubscriberMethod class

public class SubscriberMethod {
    // Subscriber reflection method
    final Method method;
    // Subscribe method - thread model
    final ThreadMode threadMode;
    // Subscribe method - event type
    finalClass<? > eventType;// Subscription method - priority
    final int priority;
    // Subscription method - whether sticky or not
    final boolean sticky;
    /** Used for efficient comparison */
    String methodString;

    public SubscriberMethod(Method method, Class<? > eventType, ThreadMode threadMode,int priority, boolean sticky) {
        this.method = method;
        this.threadMode = threadMode;
        this.eventType = eventType;
        this.priority = priority;
        this.sticky = sticky;
    }

    @Override
    public boolean equals(Object other) {
        if (other == this) {
            return true;
        } else if (other instanceof SubscriberMethod) {
            checkMethodString();
            SubscriberMethod otherSubscriberMethod = (SubscriberMethod)other;
            otherSubscriberMethod.checkMethodString();
            // Don't use method.equals because of http://code.google.com/p/android/issues/detail?id=7811#c6
            return methodString.equals(otherSubscriberMethod.methodString);
        } else {
            return false; }}private synchronized void checkMethodString(a) {
        if (methodString == null) {
            // Method.toString has more overhead, just take relevant parts of the method
            StringBuilder builder = new StringBuilder(64);
            builder.append(method.getDeclaringClass().getName());
            builder.append(The '#').append(method.getName());
            builder.append('(').append(eventType.getName()); methodString = builder.toString(); }}@Override
    public int hashCode(a) {
        returnmethod.hashCode(); }}Copy the code

SubscriberMethod class, which is related to subscription Method information, records the information of Subscribe annotation, parameter information and reflection Method (convenient for subsequent reflection call) on the subscription Method.

Before we look at the Subscribe () method, let’s look at the Subscription class, which is used in the Subscribe () method (described later).

The Subscription class

final class Subscription {
    // Subscriber object
    final Object subscriber;
    // Subscribe method
    final SubscriberMethod subscriberMethod;
    // Is active
    volatile boolean active;

    Subscription(Object subscriber, SubscriberMethod subscriberMethod) {
        this.subscriber = subscriber;
        this.subscriberMethod = subscriberMethod;
        active = true;
    }

    @Override
    public boolean equals(Object other) {
        if (other instanceof Subscription) {
            Subscription otherSubscription = (Subscription) other;
            return subscriber == otherSubscription.subscriber
                    && subscriberMethod.equals(otherSubscription.subscriberMethod);
        } else {
            return false; }}@Override
    public int hashCode(a) {
        returnsubscriber.hashCode() + subscriberMethod.methodString.hashCode(); }}Copy the code

Subscription class, which encapsulates a subscriber object and a Subscription method for subsequent reflection calls to the subscriber object’s Subscription method.

Let’s continue with the subscribe() method.

EventBus –> subscribe()

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
    // get the event type of the subscription methodClass<? > eventType = subscriberMethod.eventType;Encapsulate the Subscription object (newSubscription will then be added to subscriptionsByEventType).
    Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
    // 8. Get the Subscription collection for this event by event type (newSubscription will be added to this subscriptions later).
    CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
    if (subscriptions == null) {
        SubscriptionsByEventType = subscriptionsByEventType = subscriptionsByEventType = subscriptionsByEventType
        subscriptions = new CopyOnWriteArrayList<>();
        subscriptionsByEventType.put(eventType, subscriptions);
    } else {
        // 10. If the set is not empty, it indicates that the event has been subscribed, then it can determine whether the subscriber has subscribed repeatedly.
        if (subscriptions.contains(newSubscription)) {
            Register (); // register(); // register();
            throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "+ eventType); }}// 12. Iterate over all subscribers of the event and insert according to priority.
    int size = subscriptions.size();
    // 13, I <= size, size is at least 0, so at least once for loop.
    for (int i = 0; i <= size; i++) {
        / / description:
        // 1. I == size
        / / 2. SubscriberMethod. Priority > subscriptions. Get (I). SubscriberMethod. Priority, illustrates the new priority is higher than the current priority.
        // 14. If it is the last one, or the new priority is higher than the current priority, the new priority is added to the current position, that is, the current position is moved backward, the overall order is from high to low.
        if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
            subscriptions.add(i, newSubscription);
            break; }}// 15. Get the collection of events to which the subscriber subscribed (eventType is later added to the subscribedEvents).List<Class<? >> subscribedEvents = typesBySubscriber.get(subscriber);if (subscribedEvents == null) {
        If the set is empty, the subscriber is added for the first time, and the set is created and the current subscriber is stored in typesBySubscriber.
        subscribedEvents = new ArrayList<>();
        typesBySubscriber.put(subscriber, subscribedEvents);
    }
    Add events to the collection of events subscribed to by the subscriber
    subscribedEvents.add(eventType);

    // 18, check whether the subscription method is a sticky event. If it is a sticky event, the event will be notified at the time of registration, because sending sticky events will be notified regardless of the registration before or after the registration.
    if (subscriberMethod.sticky) {
        // if it is a sticky event, the notification subscription method is implemented.
        if (eventInheritance) { // eventInheritance: Whether the event has inheritance, the default is true.
            // 20, events have inheritance, notify all sent sticky events that match itself or its subclasses of events.
            // get all the sent sticky events and iterate to see if they match itself or its subclasses.Set<Map.Entry<Class<? >, Object>> entries = stickyEvents.entrySet();for(Map.Entry<Class<? >, Object> entry : entries) { Class<? > candidateEventType = entry.getKey();if (eventType.isAssignableFrom(candidateEventType)) {
                    // 22. Subscriptions are its own or its subclasses, and subscriptions are notified.
                    Object stickyEvent = entry.getValue();
                    / / 23 sent, call checkPostStickyEventToSubscription method for inspection.checkPostStickyEventToSubscription(newSubscription, stickyEvent); }}}else {
            If the event has no inheritance, all sent sticky events are notified as their own events.
            Object stickyEvent = stickyEvents.get(eventType);
            / / 25, call checkPostStickyEventToSubscription method to check to send, stickyEvent likely is null, because before may never send such sticky events.checkPostStickyEventToSubscription(newSubscription, stickyEvent); }}}Copy the code

Subscribe (), which maintains subscriptionsByEventType, typesBySubscriber data, and handles stickiness methods. If it is sticky method is invoked checkPostStickyEventToSubscription inspect send () method.

Description:

  1. steps6-14formaintenancesubscriptionsByEventTypeAggregate data. steps14, will lead toThe eventtheallSubscriptionAccording to theSubscription method priorityfromHigh to lowSorting.
  2. steps15 to 17formaintenancetypesBySubscriberAggregate data.
  3. steps18 to 25, to determine whether or notViscosity methodIf theisIs to determineWhether the event has inheritanceAnd find its correspondingThe event object, and finally callcheckPostStickyEventToSubscription()methodsCheck to send, such asDetection of a successfulWill informThe subscribe method.
  4. eventInheritanceforThe eventIs there ainheritanceBy default,true, will receiveHas been senttheViscous eventisIts ownorIts subclassesIn the event.
  5. If you’ve already sent a lot of sticky events, you iterate through all of them, so it’s inefficient to have a lot of sticky events.

Next, we continue to look at the checkPostStickyEventToSubscription () method.

EventBus –> checkPostStickyEventToSubscription()

private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
    if(stickyEvent ! =null) {
        // If the sticky event is not empty, send the sticky event to the subscription method and pass whether it is sent in the main thread.postToSubscription(newSubscription, stickyEvent, isMainThread()); }}Copy the code

CheckPostStickyEventToSubscription () method, to check sent to subscribe to the method, the method of viscous events if viscosity event is not null, then to send, and whether the incoming is the main thread to send, behind postToSubscription () is our delivery process, See EventBus post below for details.

EventBus –> isMainThread()

private boolean isMainThread(a) {
    return mainThreadSupport == null || mainThreadSupport.isMainThread();
}
Copy the code

The isMainThread() method, which checks whether the current thread is on the main thread.

Description:

  1. If it is notAndroidThe platform is alwaystrueIf it isAndroidThe platform passes throughmainThreadSupport.isMainThread()judgeIs it on the main thread.

summary

  1. EventBustheregisteredAnd the incomingThe subscriberObject, throughreflectionAccess to theThe subscribertheClassObject.
  2. throughSubscriberMethodFinderTo obtainAll subscribersThe subscribe methodCollection.
  3. traverseThe collection obtained above willThe subscriberandThe eventFor packagingThe bindingforSubscription.
  4. maintenancesubscriptionsByEventType,typesBySubscriberCollection data, willSubscriptionAdded to thesubscriptionsByEventTypetheThe currenteventType(fromThe subscribe methodObtained from)List,eventTypeAdded to thetypesBySubscribertheCurrent subscriberstheListIn the.
  5. And determine whether this subscription method is sticky method. If it is sticky method and has sent this sticky event before, send sticky event to this subscription method.

conclusion

EventBus source code: EventBus Source code: EventBus After the other tripartite library source code series, please timely attention. If you have any questions, see you in the comments!

Finally, I would like to recommend my website, devbolg.cn, the developer’s technical blog, which currently contains android related technologies, and will be open to all developers later. Welcome to experience!