Free at home?Look at EventBus parsing and write EventBus with me

Preface

Originally notified to resume work this Monday, it seemed that things were not simple, and it was postponed to next week's 1, but I still had to work from home for some time. Yesterday, some fans asked me why I didn't change my mind. Yesterday, I went to the company in batches for an afternoon meeting. All the people in batches had masks in them, so don't panic at all. When you go out, you remember to wear masks and wash your hands frequently. ~ (Do it at home)Gong is still very happy, he can work while singing ^^)

The OKHTTP and Retrofit parses were updated the other day, and the last EventBus parse is available today.

I have also compiled a PDF that summarizes my recent knowledge and plans to use it for reference.

The PDF related content of the knowledge summary above is updated later by GitHub. If you want to hit Golden Triple Silver Four, you can look for it. Welcome to star
(Leave a GitHub link by hand, and you can find it yourself if you need access to relevant interviews, etc.)
https://github.com/xiangjiana/Android-MS
(VX: mm14525201314)

1. Introduction to EventBus

EventBus is an Android-side optimized publish/subscribe message bus that simplifies communication between components and between components and background threads within an application.

As a message bus, there are three main components:
Event: Can be any type of object.Pass events through the publisher of the event.
Event Subscriber: Receives a specific event.
Event Publisher: Used to notify Subscriber that an event has occurred.Events can be sent anywhere on any thread.

The figure above explains the general workflow of the entire EventBus: the publisher of the event (Publisher) sends the event (Event) through the post() method.Processing inside EventBus finds the event subscriber (Subscriber) who subscribed to the event.Subscriber to the event then receives the event through the onEvent() method for related processing (about changes in onEvent() in EventBus 3.0, described in more detail below).

2. Simple use of EventBus

1. Dependent EventBus on Project

build.gradle Add Reference

  compile 'de.greenrobot:eventbus:3.0.0-beta1'
2. Construct Event objects.That is, the sending message class, each message class, corresponds to an event.Here we define two messaging classes.The specific role is explained later.
  public class NewsEvent { 
     private String message; 

     public NewsEvent(String message) { 
        this.message = message; 
     }

     public String getMessage() { 
        return message; 
     }
     public void setMessage(String message) { 
        this.message = message; 
     } 
  }
  public class ToastEvent { 
     private String content; 

     public ToastEvent(String content) { 
        this.content = content; 
     }
     public String getContent() { 
        return content; 
     }

     public void setContent(String content) { 
        this.content = content; 
     } 
  }
3. Register/Unsubscribe Events (Subscriber)
  EventBus.getDefault().register(this);//Register event where this represents the subscriber

A subscription to what Event is specifically registered, which requires the onEvent() method to illustrate.Prior to EventBus 3.0, the onEvent() method was used to receive objects of the specified Event type and perform related processing operations.After EventBus 3.0, the onEvent() method can customize the method name, but add the comment @Subscribe.

  @Subscribe 
      public void onToastEvent(ToastEvent event){ 
         Toast.makeText(MainActivity.this,event.getContent(),Toas t.LENGTH_SHORT).show();
   }

A register(this) denotes that the subscriber has subscribed, and an onToastEvent(ToastEvent event) denotes a specified subscription to the event ToastEvent.Subscription is done here.

It is important to note that subscriptions are typically registered in the onCreate() method.Unsubscribe in the onDestory() method.

  @Override 
      protected void onDestroy() { 
          super.onDestroy(); 
          EventBus.getDefault().unregister(this); 
  }
4. Send message subscription is complete, then send subscription is ready.
   EventBus.getDefault().post(new ToastEvent("Toast,Send a hint, wish you all good health, wash your hands and wear masks frequently!"));

The onToastEvent(ToastEvent event) receives the event and pops up a prompt.

This is the underlying use process of EventBus.In fact, EventBus has many other features.Let's introduce one below.

Advanced Use of EventBus

1. Thread Mode

What do you do if you are on a non-UI thread after the event you receive?If you're on a UI thread, what do you do if you're running a time-consuming operation? For other things, use ThreadMode to help you.

Usage Display:

  @Subscribe(threadMode = ThreadMode.MainThread) 
      public void onNewsEvent(NewsEvent event){ 
         String message = event.getMessage(); 
         mTv_message.setText(message); 
  }

It is easy to use and can be specified with @Subscribe(threadMode = ThreadMode.MainThread).ThreadMode is described in detail below.

There are four modes for ThreadMode: PostThread, PostThread, BackgroundThread, and Async.

PostThread: Event processing is in the same process as event sending, so event processing time should not be too long or affect the sending thread of the event.

MainThread: Processing of events is performed in the UI thread.Event processing time cannot be too long, needless to say, it will be ANR long.

BackgroundThread: If the event is published in the UI thread, then event processing will run in the child thread, and if the event was originally published in the child thread, then event processing will execute directly in the child thread.All pending events are added to a queue and processed sequentially by the corresponding threads. If an event takes too long to process, it can block the dispatch or processing of subsequent events.

Async: Event processing is performed in a separate thread, primarily for time-consuming operations in the background thread, where each event opens a thread.

2.priority Event Priority

The priority of events is similar to that of broadcasts. The higher the priority, the better the message.Usage Display:

  @Subscribe(priority = 100) 
      public void onToastEvent(ToastEvent event){ 
         Toast.makeText(MainActivity.this,event.getContent(),Toas t.LENGTH_SHORT).show(); 
  }

When multiple subscribers (Subscriber s) subscribe to the same event type, that is, the event types received in the corresponding event handling method are the same, the higher the priority (the higher the value set by priority), the event will be received for processing first, and the lower the priority (the smaller the value set by priority), the event will be received for processing later.

In addition, EventBus can terminate the ability to continue to pass events.Usage Display:

  @Subscribe(priority = 100) 
      public void onToastEvent(ToastEvent event){ 
         Toast.makeText(MainActivity.this,event.getContent(),Toas t.LENGTH_SHORT).show(); 
         EventBus.getDefault().cancelEventDelivery(event);
   }

This way other priorities are lower than 100 and subscribers who subscribe to the event will not receive it.

3.EventBus stickiness Events

EventBus supports sticky events in addition to normal events.It can be understood that a subscription receives events after publishing them.Subscription/unsubscribe is the same as normal events, but the way subscriptions are handled differs, requiring sticky = true to be added to the comment.Usage Display:

  @Subscribe(priority = 100,sticky = true) 
      public void onToastEvent(ToastEvent event){ 
         Toast.makeText(MainActivity.this,event.getContent(),Toas t.LENGTH_SHORT).show(); 
         EventBus.getDefault().cancelEventDelivery(event); 
  }

This way, suppose an event for a ToastEvent has been published and no subscription has been registered at this time.When sticky = true is set, registration occurs after the events for ToastEvent are published.Previously published events can still be received.

At this point, however, the way events are published has changed.

   EventBus.getDefault().postSticky(new ToastEvent("Toast,Send a hint to avoid visiting crowded places!"));

We can remove this sticky event if we no longer need it

  EventBus.getDefault().removeStickyEvent(ToastEvent.class);

Or call to remove all sticky events

  EventBus.getDefault().removeAllStickyEvents();
4.EventBus Configuration

EventBus added EventBuilder in version 2.3 to configure all aspects of EventBus.
For example, how to build an EventBus that stays silent when publishing events without subscribers.

  EventBus eventBus = EventBus.builder() 
    .logNoSubscriberMessages(false) 
    .sendNoSubscriberEvent(false) 
    .build();

With the above settings, when an event has no subscribers, no log information is output, and no default information is published.

Configuring the default EventBus instance using EventBus.getDefault() is a simple way.Gets an EventBus instance of a single instance.EventBusBuilder also allows you to configure default EventBus instances using the installDefaultEventBus method.

Note: Data for different EventBus objects is not shared.Publishing an event through an EventBus object requires subscribing to the event through the same EventBus object to receive it.So all of the above uses EventBus.getDefault() to get the same instance.

4. Source Code Resolution

Register Subscriber
  EventBus.getDefault().register(this);
event processing
  @Subscribe(threadMode = ThreadMode.MainThread) 
      public void onNewsEvent(NewsEvent event){ 
         String message = event.getMessage(); 
         mTv_message.setText(message); 
  }
Publish Events
  EventBus.getDefault().post(new NewsEvent("I am from SecondActivity Hello, Message!"));

These are the basic uses of EventBus.Let's start with getDefault.

getDefault()

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

From the code above, you can see that getDefault() implements a single mechanism for EventBus through a dual-check lock (DCL) mechanism and obtains a default configured EventBus object.Let's continue with the register() method.

register()

Before we get to know register(), let's look at several key member variables in EventBus.Convenient to understand the following.

  /** Map<Subscribe to an event, a collection of subscribers that subscribe to it> 
  */ 
   private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType; 

  /** Map<Subscriber, subscribe to event collection> 
  */ 
   private final Map<Object, List<Class<?>>> typesBySubscriber; 

  /** Map<Subscribe to event class type, Subscribe to event instance object >. 
  */ 
   private final Map<Class<?>, Object> stickyEvents;

Below is a look at the code executed in the specific register().

  public void register(Object subscriber) { 
         //Subscriber Type 
         Class<?> subscriberClass = subscriber.getClass(); 
         //Determine if the class is anonymous and use reflection anonymously 
         boolean forceReflection = subscriberClass.isAnonymousCla ss(); 
         //Get the subscriber's complete response function information (that is, methods like onNewsEvent() above) 
         List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(sub scriberClass, forceReflection); 
        //Loop through each event response function, execute the subscribe() method, and update subscription-related information 
        for (SubscriberMethod subscriberMethod : subscriberMetho ds) { subscribe(subscriber, subscriberMethod); 
        } 
  }

Thus, register()
The first step is to get the subscriber's class type.
The second step uses the SubscriberMethodFinder class to parse the subscriber class and get a collection of all the response functions.
The third step iterates through the subscription function, executes the subscribe() method, and updates the subscription-related information.This is not covered here about subscriberMethodFinder.Follow the clue and continue with the subscribe() method.The subscribe function has three steps.

Step 1:

    //Get Subscription Event Type 
    Class<?> eventType = subscriberMethod.eventType; 
    //Get a collection of subscribers who subscribe to the event 
    CopyOnWriteArrayList<Subscription> subscriptions = subsc riptionsByEventType.get(eventType); 
    //Wrap subscribers subscribed through register() into Subscription object Subscription newSubscription = new Subscription(subscrib er, subscriberMethod); 
    //The subscriber collection is empty, a new collection is created, and newSubscription is added
    if (subscriptions == null) { 
        subscriptions = new CopyOnWriteArrayList<Subscriptio n>(); 

        subscriptionsByEventType.put(eventType, subscription s); 

    } else { 
        //The subscriber already exists in the collection, throwing an exception.Can't repeat subscription 
        if (subscriptions.contains(newSubscription)) { 
            throw new EventBusException("Subscriber " + subs criber.getClass() + " already registered to event " + eventType); 
        } 
    }
    //Add new subscribers to the subscriber collection as a priority. 
    synchronized (subscriptions) { 
        int size = subscriptions.size(); 
        for (int i = 0; i <= size; i++) { 
            if (i == size || subscriberMethod.priority > sub scriptions.get(i).subscriberMethod.priority) { subscriptions.add(i, newSubscription); 
            break; 
            } 
        } 
    }

Step 2:

      //Get a collection of event types that the subscriber subscribes to, based on the subscriber 
      List<Class<?>> subscribedEvents = typesBySubscriber.get( subscriber); 
      //If the event type collection is empty, create a new collection and add the event type of the new subscription. 
      if (subscribedEvents == null) { 
          subscribedEvents = new ArrayList<Class<?>>(); 
          typesBySubscriber.put(subscriber, subscribedEvents); 
      }
      //Add event type of new subscription if event type collection is not empty 
      subscribedEvents.add(eventType);

Step 3:

  //The event is stick=true. 
  if (subscriberMethod.sticky) { 
           //Parent events that respond to subscription events 
           if (eventInheritance) { 
               Set<Map.Entry<Class<?>, Object>> entries = stick yEvents.entrySet(); 
               //Loop to get each stickyEvent event 
               for (Map.Entry<Class<?>, Object> entry : entries ) { 
                    Class<?> candidateEventType = entry.getKey() ; 

                   //Is the parent of this class 
                   if (eventType.isAssignableFrom(candidateEven tType)) { 
                      //The most recent event of this event type is sent to the current subscriber. 
                      Object stickyEvent = entry.getValue(); 
                      checkPostStickyEventToSubscription(newSu bscription, stickyEvent); 
                   } 
              } 
          } else { 
             Object stickyEvent = stickyEvents.get(eventType) ; 
             checkPostStickyEventToSubscription(newSubscripti on, stickyEvent); 
          } 
  }

Thus it can be seen,
Step 1: Get all subscriber information queues for this event type through subscriptionsByEventType, insert current subscriber information into subscriptionsByEventType according to priority;
Step 2: Get all the event queues subscribed by the current subscriber in typesBySubscriber and save this event to the queue typesBySubscriber for subsequent unsubscribe;
Step 3: Check if this event is a Sticky event, and if so, remove the last event of this event type from the stickyEvents event save queue and send it to the current subscriber.

Subscription is now complete.Here is a flowchart of the subscription:

unregister()

  public synchronized void unregister(Object subscriber) { 
     // Gets the collection of subscription event class types owned by the subscriber. 
     List<Class<?>> subscribedTypes = typesBySubscriber.get(subsc riber); 
     if (subscribedTypes != null) { 
         for (Class<?> eventType : subscribedTypes) { 
              unsubscribeByEventType(subscriber, eventType); 
         }
         // Delete the <subscriber object from typesBySubscriber and subscribe to the collection of event class types> 
         typesBySubscriber.remove(subscriber); 
     } else { 
         Log.e("EventBus", "Subscriber to unregister was not regi stered before: "+ subscriber.getClass()); 
     } 
  }
  private void unsubscribeByEventType(Object subscriber, Class<?> eventType) { 
     // Gets the collection of subscriber information corresponding to the subscription event. 
     List<Subscription> subscriptions = subscriptionsByEventType. get(eventType); 
     if (subscriptions != null) { 
         int size = subscriptions.size(); 
         for (int i = 0; i < size; i ++) { 
              Subscription subscription = subscriptions.get(i); 
              // Delete a specific subscriber from the subscriber collection. 
              if (subscription.subscriber == subscriber) { 
                  subscription.active = false; 
                  subscriptions.remove(i); 
                  i --; 
                  size --; 
              } 
         } 
     } 
  }

The unregister() method is relatively simple and mainly completes the synchronization of subscriptionsByEventType and typesBySubscriber collections.

post()

  public void post(Object event) { 
     // Gets the Posting state of the current thread. 
     PostingThreadState postingState = currentPostingThreadState. get();
     // Gets the event queue for the current thread.
     List<Object> eventQueue = postingState.eventQueue; 
     //Add the current event to its event queue 
     eventQueue.add(event); 
     //Determine if newly added events are in distribution 
     if (!postingState.isPosting) { 
          postingState.isMainThread = Looper.getMainLooper() == Lo oper.myLooper(); 
          postingState.isPosting = true; 
          if (postingState.canceled) { 
              throw new EventBusException("Internal error. Abort s tate was not reset"); 
          }try {
              // Loop through each event object in the current thread eventQueue. 
              while (!eventQueue.isEmpty()) { 
                     postSingleEvent(eventQueue.remove(0), postingSta te); 
              } 
          } finally { 
             // Processing knows to reset some identity information for postingState. 
             postingState.isPosting = false; 
             postingState.isMainThread = false;
         }
     } 
  }

The post function first gets the current thread's postinformation, PostingThreadState, which contains an event queue, adds the current event to its event queue, and then loops through the postSingleEvent function to publish each event in the queue.

  private void postSingleEvent(Object event, PostingThreadState po stingState) { 
     //Type of Distribution Event 
     Class<?> eventClass = event.getClass(); 
     boolean subscriptionFound = false; 
     //Parent events that respond to subscription events 
     if (eventInheritance) { 
        //Find out the class types of all parent classes of the current subscription event class type eventClass and the class types of the interfaces it implements
        List<Class<?>> eventTypes = lookupAllEventTypes(eventCla ss); 
        int countTypes = eventTypes.size(); 
        for (int h = 0; h < countTypes; h ++) { 
             Class<?> clazz = eventTypes.get(h); 
             //Publish each event to each subscriber 
             subscriptionFound |= postSingleEventForEventType(eve nt, postingState, clazz); 
        } 
     } else { 
        subscriptionFound = postSingleEventForEventType(event, p ostingState, eventClass); 
     }
      .................................... 
  }

Call the postSingleEventForEventType function to publish each event to each subscriber

  private boolean postSingleEventForEventType(Object event, Postin gThreadState postingState, Class<?> eventClass) { 
     CopyOnWriteArrayList<Subscription> subscriptions; 
     synchronized (this) { 
         // Gets the set of subscriber information corresponding to the type of subscription event class. (set constructed when the register function) 
         subscriptions = subscriptionsByEventType.get(eventClass) ; 
     }
     if (subscriptions != null && !subscriptions.isEmpty()) { 
         for (Subscription subscription : subscriptions) { 
              postingState.event = event; 
              postingState.subscription = subscription; 
              boolean aborted = false; 
              try {
                  // Publish subscription events to subscription functions 
                  postToSubscription(subscription, event, postingS tate.isMainThread); 
                  aborted = postingState.canceled; 
              } finally {
                  postingState.event = null; 
                  postingState.subscription = null; 
                  postingState.canceled = false; 
             }
             if (aborted) { 
                  break; 
             } 
         }
         return true;
     }
     return false;
   }

Call the postToSubscription function to publish events to each subscriber.The postToSubscription function determines the subscriber's ThreadMode to determine under which Mode the event response function is executed.The source code is not pasted here.There are also reflex and thread scheduling issues that follow, and this is not expanded.This is the process of post, and below is the flow chart of the specific post.

The PDF related content of the knowledge summary above is updated later by GitHub. If you want to hit Golden Triple Silver Four, you can look for it. Welcome to star
(Leave a GitHub link by hand, and you can find it yourself if you need access to relevant interviews, etc.)
https://github.com/xiangjiana/Android-MS
(VX: mm14525201314)

Keywords: Android github OkHttp Retrofit

Added by tipjones on Wed, 19 Feb 2020 04:28:06 +0200