Android Event Bus Otto usage instructions and source code parsing

Source: Internet
Author: User
Tags wrappers eventbus

first, Otto Brief introduction

Otto is the library of square launch, address: Https://github.com/square/otto

Let's take a look at Otto's official introduction.

An enhanced guava-based event bus with emphasis on Android support. Otto is a event bus designed to decouple different parts of your application while still allowing them to communicate efficiently. forked from guava, Otto adds unique functionality to a already refined event bus as well as specializing it to the AN Droid Platform.

Otto based on the Android support library for the Guava project, you can use this library if you want to communicate effectively between different components during the development of the Android program. Through the Otto Library you can.

second, Otto Simple use1, create a bus of the singleton.

public class AppConfig {private static final bus bus = new bus ();p ublic static bus getinstance () {    return bus;}}
2. Register in a class that requires the use of Otto

Register when the class is created, or when it needs to be re-registered, typically in the activity's OnCreate () or OnPause () method

Appconfig.getbusinstance (). Register (this);

3. Define subscription methods

@Subscribepublic void Onwallpaperupdate (MyObject obj) {    //logical processing required for obj}<span style= "Font-family:simsun;" ></span>

4. Send a message

Appconfig.getbusinstance (). Post (myobj);

5. Unbind

Note that when the class is destroyed or is temporarily not required to receive the message, it is generally in the activity's OnDestroy () or Onresume () method

Appconfig.getbusinstance (). Unregister (this);

Third, Otto source code analysis1. Overall structurethe source structure of Otto is very simple, and all classes are contained in the Com.squareup.otto package.excluding internal classes, there are only 9 classes and interfaces, respectivelyAnnotatedhandlerfinder Annotation Parser
Bus Core class
Deadevent No receiver Events
EventHandler Event Subscribers
Eventproducer Event Producer
Handlerfinder Get recipient Producers
Notes from produce producers
Subscribe Subscriber Annotations
Threadenforcer Thread Validation
2.analysis of Bus's key attribute methods(1) Constructors
  Public Bus () {This    (default_identifier);  }  Public Bus (String identifier) {This    (Threadenforcer.main, identifier);  }  Public Bus (Threadenforcer enforcer) {This    (enforcer, default_identifier);  }  Public Bus (Threadenforcer enforcer, String identifier) {This    (enforcer, identifier, handlerfinder.annotated);  }  Bus (Threadenforcer enforcer, String identifier, Handlerfinder handlerfinder) {    this.enforcer =  Enforcer;    This.identifier = identifier;    This.handlerfinder = Handlerfinder;  }
We usually use the bus () This construction method to create a singleton in the entire app, which not only saves resources, but more importantly ensures that the message arrives normally if it is not a singleton, it is registered with one bus, and another bus is used to send the message, so the subscription method cannot receive the message. enforcer is a thread checksum, with two values, One is Threadenforcer.any, the other is Threadenforcer.main, and the default value is Threadenforcer.main, which allows message processing only on the main thread. If you register or send a message on a non-main thread, an exception is thrownthrow new IllegalStateException ("Event Bus" + bus + "accessed from Non-main thread" + looper.mylooper ());It is important to note that the bus instance created by the Otto default construction method can only be called on the main threadIf you want to use it on another thread, use Threadenforcer.any, or customize the threadenforcer. only in the main thread use, can guarantee concise, not chaotic. But we actually use a lot of time to communicate across threads. Identifier is an identity that is used in the ToString () method of the bus.
Handlerfinder is used to parse the registered object, the default implementation is handlerfinder.annotated, using annotation parsing
(2) object Registration
public void Register (object object) {if (object = = null) {throw new NullPointerException ("object to register Mus    T is null. ");}    Enforcer.enforce (this);    Map<class<?> eventproducer> foundproducers = Handlerfinder.findallproducers (object);      For (class<?> Type:foundProducers.keySet ()) {final Eventproducer producer = Foundproducers.get (type);      Eventproducer previousproducer = producersbytype.putifabsent (type, producer); Checking if the previous producer existed if (previousproducer! = null) {throw new illegalargumentexception (' Producer method for type ' + type + ' found on type ' + producer.target.getClass () + ', but already re      Gistered by Type "+ previousProducer.target.getClass () +". ");      set<eventhandler> handlers = Handlersbytype.get (type); if (handlers! = null &&!handlers.isempty ()) {for (EventHandler handler:handlers) {Dispatchpro DucerresulttOhandler (handler, producer); }}} Map<class<?>, set<eventhandler>> foundhandlersmap = handlerfinder.findallsubscribers (ob    ject);      For (class<?> Type:foundHandlersMap.keySet ()) {set<eventhandler> handlers = Handlersbytype.get (type); if (handlers = = null) {//concurrent put if absent set<eventhandler> handlerscreation = new Copyo        Nwritearrayset<eventhandler> ();        handlers = Handlersbytype.putifabsent (type, handlerscreation);        if (handlers = = null) {handlers = Handlerscreation;      }} final Set<eventhandler> foundhandlers = Foundhandlersmap.get (type);      if (!handlers.addall (foundhandlers)) {throw new IllegalArgumentException ("Object already registered."); }} for (Map.entry<class<?>, set<eventhandler>> Entry:foundHandlersMap.entrySet ()) {class& lt;?      > type = Entry.getkey (); Eventproducer producer = ProducerSbytype.get (type);        if (producer! = null && producer.isvalid ()) {set<eventhandler> foundhandlers = Entry.getvalue ();          for (EventHandler foundhandler:foundhandlers) {if (!producer.isvalid ())} {break;          } if (Foundhandler.isvalid ()) {Dispatchproducerresulttohandler (Foundhandler, producer); }        }      }    }  }
Object Registration First makes a non-null check, then a thread's checksum, the object cannot be empty, cannot be registered multiple times after registering the object, it resolves the production method and subscription method of the corresponding class of the object, the result of the Subscriber parsing is saved in Handlersbytype, and the result of producer parsing is saved in Producersbytype, the two properties are defined as follows
Private final concurrentmap<class<?>, set<eventhandler>> handlersbytype =          New Concurrenthashmap<class<?>, set<eventhandler>> ();  /** all registered the event producers, index by event type. */  private final concurrentmap<class<?>, eventproducer> producersbytype =          new Concurrenthashmap <class<?>, eventproducer> ();
The handlersbytype key holds the parameter type of the subscription method, and Vaue the specific object and corresponding method of the Subscriber.The Producersbytype key holds the return value parameter type of the production method, and the Vaue stores the specific object and corresponding method of the producer. from the source, we can find that after parsing the subscribers, if there is a corresponding producer, the production method will be automatically called, and the Subscriber method is automatically called.
The so-called counterpart producer, is the method that has the produce annotation the return value parameter type and the method parameter with the Subscribe annotation are the same. This process involves the following three methodsPublic void Register (Object object) private void Dispatchproducerresulttohandler (EventHandler handler, eventproducer producer)
protected void Dispatch (Object event, EventHandler Wrapper)
(3) message sending
public void Post (Object event) {    if (event = = null) {      throw new NullPointerException ("event to post must not is Nu LL. ");    }    Enforcer.enforce (this);    set<class<?>> dispatchtypes = FlattenHierarchy (Event.getclass ());    Boolean dispatched = false;    for (class<?> eventtype:dispatchtypes) {      set<eventhandler> wrappers = Gethandlersforeventtype ( EventType);      if (wrappers! = null &&!wrappers.isempty ()) {        dispatched = true;        for (EventHandler wrapper:wrappers) {          enqueueevent (event, wrapper);    }}} if (!dispatched &&!) ( Event instanceof Deadevent) {      post (new Deadevent (this, event));    }    Dispatchqueuedevents ();  }
There are two main properties involved
Private final threadlocal<concurrentlinkedqueue<eventwithhandler>> Eventstodispatch =      New Threadlocal<concurrentlinkedqueue<eventwithhandler>> () {        @Override protected Concurrentlinkedqueue<eventwithhandler> InitialValue () {          return new concurrentlinkedqueue< Eventwithhandler> ();        }      };  /** True If the current thread was currently dispatching an event. *  /private final threadlocal<boolean> isdispatching = new threadlocal<boolean> () {    @Override Protected Boolean InitialValue () {      return false;    }  };
When the public void post (Object event) method is called, the thread check is performed first, then the corresponding Subscriber is resolved, and if there is a subscriber, the event is placed in the queue, if not, as a deadevent, That's what the deadevent note says.* Wraps an event is posted, but which had no subscribers and thus could not being delivered.
* <p>subscribing A deadevent handler is useful for debugging or logging, as it can detect misconfigurations in a
* system ' s event distribution.has been very clear, will not repeat. The bus loops the value from the message queue after distributing the message, much like the Android handler message mechanism, but the loop in the bus ends when the message is finished.
protected void Dispatchqueuedevents () {    //don ' t dispatch if we ' re already dispatching, that would allow reentrancy an D Out-of-order Events. Instead, leave    //The events to being dispatched after the In-progress dispatch are complete.    if (Isdispatching.get ()) {      return;    }    Isdispatching.set (true);    try {      while (true) {        Eventwithhandler Eventwithhandler = Eventstodispatch.get (). poll ();        if (Eventwithhandler = = null) {break          ;        }        if (EventWithHandler.handler.isValid ()) {          Dispatch (eventwithhandler.event, Eventwithhandler.handler);        }      }    } finally {      isdispatching.set (false);    }  }
Message Queuing uses threadlocal to ensure the independence of the queue. Multiple threads Create multiple loops at the same time, improving efficiency. The messages sent will be ready for distribution soon. (4) Unbind operation
public void Unregister (Object object) {    if (object = = null) {      throw new NullPointerException ("object to unregister Must not is null. ");    Enforcer.enforce (this);    Map<class<?> eventproducer> Producersinlistener = Handlerfinder.findallproducers (object);    for (MAP.ENTRY<CLASS<?>, eventproducer> Entry:producersInListener.entrySet ()) {      final class<? > key = Entry.getkey ();      Eventproducer producer = Getproducerforeventtype (key);      Eventproducer value = Entry.getvalue ();      if (value = = NULL | |!value.equals (producer)) {        throw new IllegalArgumentException (            "Missing event producer for An annotated method. Is "+ object.getclass ()                +" registered ");      }      Producersbytype.remove (Key). Invalidate ();    }
Similar to register, the first is the object non-null check, then the thread check, and then unbind, registration and unbind must be paired, no registration is not allowed to unbind, after the unbind can not be directly re-bound. Unbinding is primarily a cleanup effort, reducing unnecessary memory and preventing memory leaks. After you unbind, you can no longer receive messages related to the bound object. 3, Annotatedhandlerfinder to the interpretation of annotations, the construction of producers and subscribers to resolve the information stored in the Producers_cache,subscribers_cache,
  /** Cache Event Bus producer methods for each class. */  private static final concurrentmap<class<?>, Map<class<?>, method>> Producers_cache =    new Concurrenthashmap<class<?>, Map<class<?>, method>> ();  /** Cache Event Bus subscriber methods for each class. */  private static final concurrentmap<class<?>, Map<class<?>, set<method>>> Subscribers_cache =    new Concurrenthashmap<class<?>, Map<class<?>, set<method>>> ( );

The same structure, the key value is the class,value of the resolution object is the method parameter type (producer is the return data type, the Subscriber is the data type of the method parameter) and the corresponding method. The parsing producer calls in turn: static Map<class<?>, eventproducer> findallproducers (Object listener),
private static void Loadannotatedproducermethods (Class<?> listenerclass,map<class<?>, Method> Producermethods),
private static void Loadannotatedmethods (Class<?> listenerclass,map<class<?>, method> Producermethods, Map<class<?>, set<method>> subscribermethods).
The subscriber is parsed in turn: static Map<class<?>, set<eventhandler>> findallsubscribers (Object listener),
private static void Loadannotatedsubscribermethods (Class<?> listenerclass,map<class<?>, Set< Method>> subscribermethods),
private static void Loadannotatedmethods (Class<?> listenerclassmap<class<?>, method> Producermethods, Map<class<?>, set<method>> subscribermethods).
Ultimately, it's called loadannotatedmethods. During parsing, violating some rules throws an exception @Subscribe Annotated method can have only one parameter
@Subscribe annotations must be implemented in a specific way, not as an interface the method that @Subscribe annotation must be public
The method in which the @Produce annotation must be empty The method return value type of the @Produce annotation cannot be void
The method of @Produce annotation must be a concrete implementation, not an interface
the method that @Produce annotation must be public
Methods that @Produce annotations return value types in the same way can have only one

Iv. Summary Otto is very lightweight, most implementations rely on annotation reflection, and in the process of using it, you should pay special attention to the confusion of the code.Android Event Bus processing There is also a good open source framework that is eventbus,eventbus slightly heavier, more complex, corresponds to more features, and more flexible with threading control. a detailed introduction to Eventbus can be referred to http://blog.csdn.net/robertcpp/article/details/51546714. The event bus can also be implemented with Rxjava, which is explained in detail later. Whichever frame is used, it is ultimately an observer pattern. The appropriate framework can be selected according to the project needs. Of course, through the native broadcast, handler mechanism, certainly can also achieve.

Welcome to scan QR Code, follow public account







Android Event Bus Otto usage instructions and source code parsing

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.