EventBus for Android Development Series Communication

Source: Internet
Author: User
Tags eventbus

EventBus for Android Development Series Communication
Overview and Basic Concepts

** EventBus ** is an Android-optimized publish/subscribe message bus that simplifies communication between components in the application and between components and background threads. For example, if you request a network and notify the UI through Handler or Broadcast when the network returns, the two Fragment must communicate with each other through Listener. These requirements can be implemented through ** EventBus.

As a message bus, there are three main elements:

Event: Event Subscriber, receives a specific Event Publisher: Event Publisher, used to notify Subscriber of an Event

** Event ** can be any type of object.

Subscriber

In EventBus, conventions are used to specify event subscribers for simplified use. That is, all event subscriptions start with onEvent. Specifically, the function names are onEvent, onEventMainThread, onEventBackgroundThread, and onEventAsync. This is related to ThreadMode.

Publisher

You can send events anywhere in any thread and directly call the 'post (Object) 'method of EventBus. You can instantiate the EventBus Object by yourself, but generally you can use the default singleton: 'eventbus. getDefault () '. The function that subscribes to corresponding types of events is automatically called based on the type of the post function parameter.

ThreadMode

As mentioned above, the Subscriber function name can only be those four, because each event subscription function is associated with a 'threadmode' and ThreadMode specifies the function to be called. There are four threadmodes:

PostThread: the event is handled in the same process as the event is sent, so the event processing time should not be too long. Otherwise, the event sending thread will be affected, and this thread may be the UI thread. The corresponding function name is onEvent. MainThread: event processing is executed in the UI thread. The event processing time cannot be too long. Needless to say, it will be ANR long and the corresponding function name is onEventMainThread. BackgroundThread: event processing is performed in a background thread. The corresponding function name is onEventBackgroundThread. Although the name is BackgroundThread, event processing is performed in the background thread, however, the event processing time should not be too long, because if the thread sending the event is a background thread, the event will be executed directly. If the current thread is a UI thread, the event will be added to a queue, A thread processes these events in sequence. If the processing time of an event is too long, it will block the dispatching or processing of subsequent events. Async: event processing is executed in a separate thread, mainly used to execute time-consuming operations in the background thread. Each event starts a thread (with a thread pool ), but it is best to limit the number of threads.

Different threadmodes are used according to the function names of event subscriptions. If you want to display data in the UI thread when loading data in the background thread, the subscriber only needs to name the function onEventMainThread.

Easy to use

The basic steps are as follows. Click this link to view the example and introduction.

Define the event type:
'Public class MyEvent {} 'defines the event processing method:
'Public void oneventmainthread:
'Eventbus. getDefault (). register (this) 'sends an event:
'Eventbus. getDefault (). post (new MyEvent () 'Implementation

** EventBus ** is easy to use, but if you do not know how to use it, you will never know how to use it, so we need to study its implementation and Read the fucking Code. In fact, it is mainly a 'eventbus' class. When you look at the Code, you need to understand several concepts and members. After understanding these concepts, you can understand the implementation well.

EventType: the parameter in the onEvent \ * function, indicating the event type Subscriber: subscription source, that is, the object registered by calling register. This object contains the onEvent \ * function SubscribMethod: A specific onEvent \ * Method in 'subscriber '. The internal Member contains a method Member of the 'method' type to indicate the onEvent \ * Method, A 'threadmode' member ThreadMode represents the event processing thread, and an 'class' eventType member represents the event type 'eventtype '. Subscriber, indicating a Subscription object, including the Subscription source 'subscriber ', a specific method in the Subscription source 'subscribmethod', the priority of this Subscription 'priopity'


After learning about the above concepts, You can see several important members in 'eventbus '.

// EventType-> List
 
  
, Private final Map ing between events and subscription objects
  
   
, CopyOnWriteArrayList
   
    
> SubscriptionsByEventType; // Subscriber-> List
    
     
To subscribe to the ING private final Map of all event types subscribed to by the source.
     
      
> TypesBySubscriber; // stickEvent event. The private final Map is displayed later.
      
        , Object> stickyEvents; // EventType-> List
       Ing event to its parent event list. Cache all the parent class private static final Map of a class
       
         , List
        
          > EventTypesCache = new HashMap
         
           , List
          
            >> ();
          
         
        
       
      
     
    
   
  
 
Registration event: Register

Use 'eventbus. getDefault (). the register 'method can be used to register with 'eventbus' to subscribe to events. 'register 'has many reload forms, but most of them are marked as 'deprecated', so it is better not to subscribe to events, as mentioned above, all event handling methods start with * onEvent * and can be modified using the register method. However, the corresponding method is discarded and should not be used, use the default * onEvent *. In addition to the discarded register method, there are also four 'register 'Methods ** public **.

public void register(Object subscriber) {    register(subscriber, defaultMethodName, false, 0);}public void register(Object subscriber, int priority) {    register(subscriber, defaultMethodName, false, priority);}public void registerSticky(Object subscriber) {    register(subscriber, defaultMethodName, true, 0);}public void registerSticky(Object subscriber, int priority) {    register(subscriber, defaultMethodName, true, priority);}

We can see that the four methods call the same method:

private synchronized void register(Object subscriber, String methodName, boolean sticky, int priority) {    List
 
   subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass(),methodName);    for (SubscriberMethod subscriberMethod : subscriberMethods) {        subscribe(subscriber, subscriberMethod, sticky, priority);    }}
 

 

The first parameter is the subscription source, and the second parameter uses the specified method name convention. The default parameter starts with * onEvent *, which means that the default parameter can be modified, but as mentioned above, the method has been abandoned and it is best not to use it. The third parameter indicates whether it is * Sticky Event *, and the second parameter indicates the priority.

In the above method, a class named 'subscribermethodfinder 'is used to locate a 'subscribermethod' list through its 'findsubscribermethods' method, as we have known before, 'subscribermethod' indicates an onEvent \ * method in the Subcriber. We can see that the role of the 'subscribermethodfinder 'class is to find all the methods in the Subscriber (that is, the default onEvent) method. Each method found is represented as a 'subscribermethod' object.

'Subscribermethodfinder 'will not be analyzed, but there are two points to know:

All event processing methods ** must be of the 'public void' type **, and only one parameter indicates * EventType *. 'Findsubscribermethods 'not only looks for the event handling methods in * Subscriber *, but also finds the event handling methods in all the base classes in its inheritance system **.

After all the event handling methods in * Subscriber * are found, the 'subscribe' method is called for registration for each found method (represented as the 'subscribermethod' object. The 'subscribe' method does three things:

Store the 'subscribermethod' object in 'subscriptionsbyeventtype' Based on the * EventType * type in 'subscribermethod. Creates a ing between * EventType * and * subscribe *. Each event can have multiple subscribers. Store 'eventtype' in 'typesbysubscriber 'based on 'subscriber' and create a ing between * Subscriber * and * EventType *. Each Subscriber can subscribe to multiple events. If it is a * Sticky * type subscriber, send the last saved event directly to it (if any ).

Through the subing between * Subscriber * and * EventType *, we can easily cancel the event receiving by a Subscriber and use * EventType * to map * Sucscribtion, you can easily send events to every subscriber.

Post event

You can directly call 'eventbus. getDefault (). post (Event) to send events. The events can be sent to the corresponding Event subscriber Based on the Event type.

public void post(Object event) {    PostingThreadState postingState = currentPostingThreadState.get();    List eventQueue = postingState.eventQueue;    eventQueue.add(event);    if (postingState.isPosting) {        return;    } else {        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()) {                postSingleEvent(eventQueue.remove(0), postingState);            }        } finally {            postingState.isPosting = false;            postingState.isMainThread = false;            }    }}

We can see that the post object uses 'postingthreadstate' and is 'threadlocal'. Let's take a look at the definition of 'postingthreadstate:

final static class PostingThreadState {    List eventQueue = new ArrayList();    boolean isPosting;    boolean isMainThread;    Subscription subscription;    Object event;    boolean canceled;}

There is a member 'eventqueue '. Because it is ThreadLocal, the result is that each thread has a 'postingthreadstate' object, which contains an event queue, in addition, the 'isposting' Member indicates whether an event is being distributed. When an event is sent, events in the queue are retrieved and sent out in sequence. If an event is being distributed, then post directly adds the event to the queue and returns the event with a member 'ismainthread', which will be used in the actual event dispatching and will be used in 'postsingleevent.

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {    Class
  eventClass = event.getClass();    List
 
  > eventTypes = findEventTypes(eventClass); // 1    boolean subscriptionFound = false;    int countTypes = eventTypes.size();    for (int h = 0; h < countTypes; h++) { // 2        Class
   clazz = eventTypes.get(h);        CopyOnWriteArrayList
  
    subscriptions;        synchronized (this) {            subscriptions = subscriptionsByEventType.get(clazz);        }        if (subscriptions != null && !subscriptions.isEmpty()) { // 3            for (Subscription subscription : subscriptions) {                postingState.event = event;                postingState.subscription = subscription;                boolean aborted = false;                try {                    postToSubscription(subscription, event, postingState.isMainThread); // 4                    aborted = postingState.canceled;                } finally {                    postingState.event = null;                    postingState.subscription = null;                    postingState.canceled = false;                }                if (aborted) {                    break;                }            }            subscriptionFound = true;        }    }    if (!subscriptionFound) {        Log.d(TAG, "No subscribers registered for event " + eventClass);        if (eventClass != NoSubscriberEvent.class && eventClass != SubscriberExceptionEvent.class) {            post(new NoSubscriberEvent(this, event));        }    }}
  
 

Let's take a look at the 'postsingleevent' function. First, let's take a look at the first point. We call the 'findeventtypes 'function. The Code will not post it. The application of this function is, save the class object, implemented interface, and parent class object of this class to a List and return.

Next, go to step 2, traverse the List obtained in step 1, and perform step 3 operations on each class Object (that is, the event type) in the List, all subscribers of this event type are found to send events to them. As you can see, ** when we Post an event, the parent event of this event (the parent event of the event class) will also be Post, therefore, if an event subscriber receives an Object-type event, it can receive all events **.

We can also see that the event is actually sent through 'posttosubstatus' in Step 4, and the event and subscriber are saved to 'postingstate' before sending. Let's take a look at 'posttosubnotification'

private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {    switch (subscription.subscriberMethod.threadMode) {    case PostThread:        invokeSubscriber(subscription, event);        break;    case MainThread:        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);    }}

'Threadmode' is used here:

If it is PostThread, directly execute MainThread to determine the current thread. If it is already a UI thread, directly execute it. Otherwise, add the 'mainthreadposter' queue to the background thread. If it is a UI thread, add to the 'backgroundposter' queue. Otherwise, if the execution is Async, add the 'asyncposter' queue backgroundPoster.
private final PendingPostQueue queue;public void enqueue(Subscription subscription, Object event) {    PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);    synchronized (this) {        queue.enqueue(pendingPost);        if (!executorRunning) {            executorRunning = true;            EventBus.executorService.execute(this);        }    }}

The code is simple. In fact, the event to be sent is encapsulated into the 'pendingpost' object, and 'pendingpostqueue 'is a queue of the 'pendingpost' object, this event is put into the queue when 'enableput', and 'backgroundposter' is actually a Runnable object. If this Runnable object is not executed currently, add 'backgroundposter' to a thread pool in EventBus. When 'backgroundposter' is executed, events in the queue are retrieved and distributed in sequence. When there is no event for a long time, the thread to which 'backgroundposter' belongs will be destroyed, and a new thread will be created when the next Post event occurs.

HandlerPoster

'Mainthreadposter' is a 'handlerposter' object. 'handlerposter' inherits from 'handler'. The constructor receives a 'login' object. When an event is sent to 'handlerposter' enqueue, this event will be added to the queue like 'backgroundposter', but if the Message is not sent, the Message will be sent to itself.

void enqueue(Subscription subscription, Object event) {    PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);    synchronized (this) {        queue.enqueue(pendingPost);        if (!handlerActive) {            handlerActive = true;            if (!sendMessage(obtainMessage())) {                throw new EventBusException("Could not send handler message");            }        }    }}

In 'handlemessage', messages in the queue are retrieved in sequence and handed over to 'eventbus' to directly call the event processing function, the thread where the 'handlemessage' execution is located is the thread of the 'logging' passed in by the constructor. When 'eventbus' is constructed, the mainlogger' is passed in, so it will be executed in the UI thread.

AsyncPoster

'Asyncposter' is simple, and every event is added to the thread pool for processing.

public void enqueue(Subscription subscription, Object event) {    PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);    queue.enqueue(pendingPost);    EventBus.executorService.execute(this);}
Stick Event

You can use 'registersticky' to register the Stick event handler. As we know before, either 'register 'or 'registersticky' will call the 'subscribe' function, there is such a piece of code in 'subscribe:

 

That is, the event type is used to check whether the corresponding event exists from 'stickyevents'. If yes, the event is directly sent to the subscriber. When is this event stored? Like 'register 'and 'registersticky', there is also a 'poststicky' function with 'post:

if (sticky) {    Object stickyEvent;    synchronized (stickyEvents) {        stickyEvent = stickyEvents.get(eventType);    }    if (stickyEvent != null) {        // If the subscriber is trying to abort the event, it will fail (event is not tracked in posting state)        // --> Strange corner case, which we don't take care of here.        postToSubscription(newSubscription, stickyEvent, Looper.getMainLooper() == Looper.myLooper());    }}

When an event is sent through 'posticky', the last event of this type of event will be cached. When a subscriber registers through 'registersticky, the cached event is directly sent to it.

Event Priority

The 'register 'function overload contains a subscriber priority that can be specified. We know that there is an event type in 'eventbus' to List In this ing, all subpartitions are sorted by priority. In this way, when a post event has a higher priority, it will first get a chance to process the event.

An application with a higher priority can finally pass events through the 'canonicaleventdelivery' method. However, note that 'the ThreadMode of this event must be postthread ', and only events that are being processed.

# Disadvantages
There is no way to communicate between processes. If there are multiple processes in an application, there is no way to communicate.

# Notes and highlights

The same onEvent function cannot be registered twice, so it cannot be registered in a class, and it is also registered in the parent class. When an event is Post, the parent class event of this event class will also be Post. When the Post event is not processed by the Subscriber, the 'nosubscriberevent' event is Post. When the Subscriber fails to be called, The 'subscriberexceptionevent' event is Post.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.