EventBus use

1. Customize event classes

public class GirlsLoadedEvent { public List girls; Public GirlsLoadedEvent(List girls) {this.girls = girls; }}Copy the code

2. Register subscribers: eventbus.getDefault ().register(this); The subscriber class declares that the subscriber method handles events:

Public void onEvent(GirlsLoadedEvent event) {girlsView.showgirls (event.girls); // event handler logic}Copy the code

EventBus.getDefault().post(new GirlsLoadedEvent(girlList)); 4. Unregister a subscriber: eventbus.getDefault ().unregister(this);

Why use EventBus

EventBus is an observer-based Publish/ subscribe design that replaces traditional Intent, Handler, Broadcast, and custom callback interfaces between threads (mainly mainthreads and subthreads). Communication between components (activities, services, fragments) (passing data) because it is simpler to use EventBus, see the following example.

Example: Brevity of EventBus

Send GirlsActivity the response result of the network request through EventBus, Handler, Callback, and Broadcast, and update the UI.




1.png




2.png

GitHub source: github.com/homcin/Even… There is also a complete App project GitHub source: github.com/homcin/Tuik…

Source code analysis

GetDefault () of eventbus.getDefault ().register(this) is a singleton method, equivalent to getInstance(), that returns an EventBus singleton.

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

The register() method has multiple overloads: The difference is that you need to set priorities for subscribers, Whether to post the given event to the event bus and hold on to the event (because it is sticky). The most recent sticky Event of an event’s type is kept in memory for future access.

Public void register(Object subscriber) // Public void register(Object subscriber, int priority) public void registerSticky(Object subscriber) public void registerSticky(Object subscriber) public void RegisterSticky (Object subscriber, int priority) // The following method is called internally in the above four methods. The value of non-sticky is false and the value of priority is 0 by default. Private synchronized void register(Object subscriber, Boolean sticky, int priority) {// A subscriber class can have multiple subscriber methods: handle multiple event types. List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass()); for (SubscriberMethod subscriberMethod : // Subscribe to all the subscriber methods of the subscriber, and the key parameters are subscriber + subscriber method SUBSCRIBE (subscriber, subscriberMethod, sticky, priority); }}Copy the code

Registered Subscribers: Find all subscriber methods for that subscriber and store them in the List through the tool method findSubscriberMethods(subscriber.getClass()) of the Tool class SubscriberMethodFinder instance In subscriberMethods set, subscribe(subscriber, subscriberMethod, sticky, priority) to each subscriber method in this set; So, register() registers a subscriber by essentially calling the subscribe() method on all the subscriber methods of that subscriber.

Episode:

Methods declared inside the subscriber class that start with onEvent, onEvent, onEventMainThread, onEventBackgroundThread and onEventAsync. See the beginning for examples. 2. What does the MainThread behind onEvent do? Role: Determines the execution environment of the method, that is, on which thread the method is executed. And identified with a ThreadMode. OnEvent: Executed on the current thread. ThreadMode = ThreadMode PostThread. OnEventMainThread: Executes on the main thread. ThreadMode = ThreadMode MainThread. OnEventBackgroundThread: The tasks are executed in a single sequence in the thread pool. ThreadMode = ThreadMode BackgroundThread. OnEventAsync: The task is executed concurrently in the thread pool. ThreadMode = ThreadMode. Async.

The subscribe method

First, there are two wrapper classes:

Final class SubscriberMethod {// Final Method Method; // Subscriber's subscriber method final ThreadMode ThreadMode; // Thread mode, which determines which thread will execute the final Class eventType; // Custom event class}Copy the code
Final class Subscription {// Subscription information wrapper class: uniquely identifies a subscriber method: subscriber + a subscriber method final Object subscriber; // Final SubscriberMethod SubscriberMethod; // Subscriber method final int priority; // Priority}Copy the code

Two important HashMap member variables for EventBus:

private final Map, CopyOnWriteArrayList> subscriptionsByEventType;
private final Map>> typesBySubscriber;Copy the code

1. An event type can have multiple subscribers. Class eventType –> List; So use subscriptionsByEventType to save the mapping between the event class and all of its subscribers. 2. A subscriber can have multiple subscriber methods, each of which handles different event types, i.e., a subscriber can handle multiple event types. That is, the mapping: Object Subscriber –> List, so use typesBySubscriber to hold the mapping of the subscriber and all the event types it can handle.

Begin reading the subscribe method:
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority) { Class eventType = subscriberMethod.eventType; / / PartA: 1. To get the event types corresponding collection all subscribers List CopyOnWriteArrayList subscriptions = subscriptionsByEventType. Get (eventType); Subscription newSubscription = new Subscription(subscriber, subscriberMethod, priority); 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(); for (int i = 0; i <= size; I++) {if (I = = size | | newSubscription. Priority > subscriptions. Get (I). The priority) {/ / PartA: 2. Add (I, newSubscription) this new subscriber to the List of all subscriptions that correspond to this event type; break; }} //PartB: 1. Get the set of all event types that can be processed by the subscriber. if (subscribedEvents == null) { subscribedEvents = new ArrayList>(); //PartB: 2. Add the event type to List typesbysubscriber. put(subscriber, subscribedEvents); } subscribedEvents.add(eventType); }Copy the code

sosubscribeMethod: add the subscriber in the parameter and the event type in the subscriber method toEventBusIn the two important HashMap member variables subscriptionsByEventType and typesBySubscriber.

The first step:EventBus.getDefault().register(this)End of analysis:

register()— > This subscriber all subscriber methods are calledsubscribe()Method –> Adds the subscriber, the event type in the subscriber method toEventBusIn the two important HashMap member variables subscriptionsByEventType and typesBySubscriber.

Step 2: InterpretEventBus.getDefault().post(new GirlsLoadedEvent(girlList));

See first PostingThreadState types of member variables currentPostingThreadState:

Final Static class PostingThreadState {// Final List eventQueue = new ArrayList(); // The event queue, the key is the Boolean isPosting; // Whether an event is being published Boolean isMainThread; // Whether the current thread is the main thread // Subscribe message (subscriber + one subscriber method) Object event; // Custom event Boolean Canceled; } // The EventBus member variable is of type PostingThreadState. ThreadLocal is used only to resolve multithreaded access conflicts. private final ThreadLocal currentPostingThreadState = new ThreadLocal() { @Override protected PostingThreadState initialValue() { return new PostingThreadState(); }};

Analyze the POST () method:

public void post(Object event) { PostingThreadState postingState = currentPostingThreadState.get(); List eventQueue = postingState.eventQueue; //1. Insert the published event into the PostingThreadState eventQueue.add(event); if (! postingState.isPosting) { postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper(); postingState.isPosting = true; if (postingState.canceled) { throw new EventBusException("Internal error. Abort state was not reset"); } try { while (! Eventqueue.isempty ()) {//2. The loop calls postSingleEvent by continually fetching events from the eventQueue. postSingleEvent(eventQueue.remove(0), postingState); } } finally { postingState.isPosting = false; postingState.isMainThread = false; }}}

That is, multiple events can be published at the same time by inserting them all into the event queue and then sequentially pulling them out of the event queue to execute the postSingleEvent() method individually. PostSingleEvent () : traversal custom all the parent class of events (because the custom events can also inherit the parent class), for all events the following postSingleEventForEventType method. Generally speaking, we don't have inherited the custom event, so only call postSingleEventForEventType method about the custom event.

private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class eventClass) { CopyOnWriteArrayList subscriptions; Synchronized (this) {/* 1. Retrieve all subscriptions of this event type from the HashMap member variable subscriptionsByEventType List */ SUBSCRIPTIONS = subscriptionsByEventType.get(eventClass); } if (subscriptions ! = null && ! subscriptions.isEmpty()) { // 2. Publish the event type to all of its subscribers. 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

Publish this event type to all of its subscribers: postToSubscription is called for each subscriber of this event type. Let's take a look at three thread-handling classes.

final class HandlerPoster extends Handler {} final class BackgroundPoster implements Runnable {} class AsyncPoster implements Runnable {} private final PendingPostQueue queue; public void enqueue(){queue.enqueue(pendingPost); } final class PendingPost {// A whole: an event + its subscriber (unique identifier) Object event; Subscription subscription; }Copy the code

First, each has an internal queue member, and each provides an enqueue method for inserting events (PendingPost). MainThreadPoster: Handler message handling mechanism, sendMessagge, handleMessage,MainLooper. BackgroundPoster: Thread pool where tasks are executed in a single order. AsyncPoster: Thread pool where tasks are executed concurrently. Three decision execution environment, how to deal with the tasks are called eventBus. InvokeSubscriber (pendingPost);

private void postToSubscription(Subscription subscription, Object event, Boolean isMainThread) {// Select which thread-handling class to handle published messages according to ThreadMode. The switch (subscription. SubscriberMethod. ThreadMode) {case PostThread: / / on the current thread processing invokeSubscriber (subscription, event); break; If (isMainThread) {invokeSubscriber(subscription, event); } else { mainThreadPoster.enqueue(subscription, event); } break; Case BackgroundThread: // If (isMainThread) {backgroundPoster. Enqueue (subscription, event); } else { invokeSubscriber(subscription, event); } break; Case Async: // asyncPoster. Enqueue (subscription, event); break; default: throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode); }}Copy the code
void invokeSubscriber(PendingPost pendingPost) { invokeSubscriber(pendingPost.subscription, pendingPost.event); } void invokeSubscriber(Object event) {/* Method.invoke (subscription. Subscriber, event) The subscriber is the main call for executing the subscriber method, and event is the argument passed in when executing the subscriber method. */ subscription.subscriberMethod.method.invoke(subscription.subscriber, event); }Copy the code

At this point, the source code analysis is complete. Summary: Register (): Adds all the event types in the subscriber and all of its subscriber methods to the two important HashMap member variables subscriptionsByEventType and typesBySubscriber of EventBus. Post () : Takes all subscribers of the event type from the HashMap member variable subscriptionsByEventType, using reflection to call the subscriber method with parameters of the event type in each subscriber.

Read the source code to learn knowledge

1. Design Patterns: Singleton + Observer + Builder examples. Builder mode: The Builder() method returns EventBusBuilder(: contains optional member variables in EventBus), the EventBus constructor: EventBus(EventBuilder Builder){assign member variables in Builder to member variables in EventBus}. 2. Writing of four execution environments for task processing. Use flag bit Boolean executorRunning to ensure that tasks are delivered to a thread pool in a single order. 3. Task queue + Task object pool PendingPostQueue Queue + List pendingPostPool in PendingPost reduces the overhead of creating task objects. 4. Reflect knowledge and call the class method.

Copy the codeCopy the code