Recently we’ve been talking about square components, but today we’ll continue with square’s Otto framework

According to otto’s website

An enhanced Guava-based event bus with emphasis on Android support.

Otto is an 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 an already refined
event bus as well as specializing it to the Android platform.Copy the code

Otto is based on Google’s Guava(never heard of it??) Android event Bus. If you want to communicate components in Android projects, you can use this library to reduce coupling between programs.

The popular science Guava:

Guava is based on GoogleJava1.6 class library collection extension project.Provides the collections, caching, eventbus high quality API to youJava code is more elegantGuava's EventBus starts with a publish-subscription-based messaging library that finds subscribers by default with annotationsCopy the code

Guava Related Information:

Guava Study Notes: Introduction to the Google Guava class library

Google Guava Tutorial (Chinese version)

Source package of simple explanation: com.google.com mon. Annotations: ordinary annotation types. Com.google.common. base: Basic tool class library and interface. Com.google.common. cache: a very simple and powerful in-JVM cache toolkit. Com.google.com mon. Collect: with a collection of generic interface extension and implementation, and tools, here you will find a collection of a lot of fun. Com.google.com mon. Eventbus: publish-subscribe style event bus. Com.google.common. hash: Hash tool kit. Com.google.common. IO: I/O tool package. Com.google.mon. math: Toolkit for primitive arithmetic types and hypernumbers. Com.google.common.net: Web toolkit. Com.google.com mon. Primitives: eight primitive types and unsigned types of static toolkit. Com.google.com mon. Reflect: reflect toolkit. Com.google.com mon. Util. Concurrent: multi-threaded kit.Copy the code

Otto is on the verge of being scrapped by Square, according to the website

Deprecated!

This project is deprecated in favor of RxJava and RxAndroid. 
These projects permit the same event-driven programming model as Otto, 
but they’re more capable and offer better control of threading.

IfYou 're &for guidance on migrating from Otto to Rx, 
this post is a good start.Copy the code

Square recommends using RxJava to implement the RxBus approach instead of Otto.

Because the original project was otto, at least so far otto has not been abandoned by the developers.

This paper also introduces the use and principle of Otto.

Otto uses gestures

  1. First, introduce the AAR package
// Otto
compile 'com. Squareup: otto: 1.3.8'Copy the code
  1. Construct a Bus singleton
public class BusProvider {

    private static final Bus bus = new Bus();

    public static Bus getBus(a){
        returnbus; }}Copy the code

3. Implement in code (just as an example, Otto can communicate without being in the same class)

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button button = (Button) this.findViewById(R.id.post_event);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                   // Send events
                BusEvent busEvent = new BusEvent("TODO"); BusProvider.getBus().post(busEvent); }}); }@Override
    protected void onResume(a) {
        super.onResume();

        // Register events
        BusProvider.getBus().register(this);
    }

    @Override
    protected void onPause(a) {
        super.onPause();
    }

    @Override
    protected void onStop(a) {
        super.onStop();

        // Cancel the event
        BusProvider.getBus().unregister(this);
    }


    /** * receive event *@param event
     */
    @Subscribe
    public void getEventBus(BusEvent event) {
        String key = event.getEventKey();
        Toast.makeText(MainActivity.this."key = "+ key,Toast.LENGTH_LONG).show(); }}Copy the code

See above actually use very simple, is to register, logout, send event, receive event.

It is important to note that sending and receiving can not be in the same class, but the receiving must register time and logout events.

Important knowledge points

The method of defining Subcribe needs to be remembered

The number of parameter types can only be1If the first argument is of type interface, the public type of the method must bepublic.privateThrows an exceptionCopy the code

The method for defining Produce needs to be kept in mind

If the parameter Produce is not equal to0If the return type of Produce isvoidOtherwise, if the return type of Produce is an interface type, the public type of the method that throws this exception must bepublic.privateThrows an exceptionCopy the code
When post, the @subcribe method is searched for the same parameter type as the one passed in

There were a lot of rules and regulations when Otto used it

Throw a question? In what way does Otto communicate between threads?

So with that in mind, let’s look at the code

The code on

As usual, from a code architecture perspective. This should be the least viewed class in a while.

. ├ ─ ─ AnnotatedHandlerFinder.javaMethod ├─ Bus for finding classes where @subcribe and @produce exist.java-- External provider, provide register, Unregister, Post method exercises ─ DeadEvent.javaPost Exercises ─ EventHandler will not be performed if this Event is sent.java--@Subcribe method and class entity object Exercises ─ EventProducer.java--@Produce method and class entity Object Exercises ─ HandlerFinder.java├── Find methods for classes @subcribe and @produce.java--Produce annotation definition ├─ Subscribe.java├ ─ Subscribe, imp.java-- Thread force, default provide MAIN thread, not MAIN thread, raise exceptionCopy the code

Bus constructor

Start by constructing the object for the Bus

Bus bus = new Bus(a);Copy the code

In fact, this is all default values, the specific default values, see the following code

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 constructor *@paramEnforcer thread decides that by default MAIN is not the MAIN thread and throws an exception *@paramIdentifier BUS defines *@paramHandlerFinder lookup@Subcribeand@ProduceClass */
Bus(ThreadEnforcer enforcer, String identifier, HandlerFinder handlerFinder) {

    ThreadEnforcer.MAIN by default, only allowed in the MAIN thread
    this.enforcer = enforcer;

    // Default "default"
    this.identifier = identifier;

    // provide methods to find classes for @subcribe and @produce. Implement the following methods
    //  Map<Class<?>, EventProducer> findAllProducers(Object listener);
    // Map
       
        , Set
        
         > findAllSubscribers(Object listener);
        
       >
    this.handlerFinder = handlerFinder;

}Copy the code

BUS Register method

Bus bus = new Bus(a);bus.register(this)Copy the code

What do you see in the code?

/** * BUS annotation method * @param object */
public void register(Object object) {
    if (object = = null) {
        throw new NullPointerException("Object to register must not be null.");
    }
    // Force judgment, provide two objects of class,ANY and MAIN,
    // MAIN indicates that only the MAIN thread is allowed
    // ANY indicates that ANY thread is supported
    enforcer.enforce(this);

    // Obtain the @produce method of the current Object
    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 registered by type " + previousProducer.target.getClass() + ".");
        }
        Set<EventHandler> handlers = handlersByType.get(type);
        if(handlers ! =null && !handlers.isEmpty()) {
            for (EventHandler handler : handlers) {
                // throw @produce to @subcribe for executiondispatchProducerResultToHandler(handler, producer); }}}// Get the @subcribe method in the current Object
    Map<Class<? >,Set<EventHandler>> foundHandlersMap = handlerFinder.findAllSubscribers(object);
    for (Class<? >type : foundHandlersMap.keySet()) {
        Set<EventHandler> handlers = handlersByType.get(type);
        if (handlers == null) {
            //concurrent put if absent
            Set<EventHandler> handlersCreation = new CopyOnWriteArraySet<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<? >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()) {

                    // throw @produce to @subcribe for execution
                    dispatchProducerResultToHandler(foundHandler, producer);
                }
            }
        }
    }
}Copy the code

Here’s how to find the @subcribe and @Produce methods defined in the class

private static void loadAnnotatedMethods(Class<? >listenerClass.
                                         Map<Class<? >,Method> producerMethods.Map<Class<? >,Set<Method>> subscriberMethods) {// Get the method in the current classfor (Method method : listenerClass.getDeclaredMethods()) {
        // The compiler sometimes creates synthetic bridge methods as part of the
        // type erasure process. As of JDK8 these methods now include the same
        // annotations as the original declarations. They should be ignored for// subscribe/produce. // Whether the bridge method existsif (method.isBridge()) {
            continue; } // find if the method is annotated with @subscribeif (method.isAnnotationPresent(Subscribe.class)) {
            Class<? > []parameterTypes = method.getParameterTypes(a);// The number of parameter types can only be1, an exception is thrownif(parameterTypes.length ! =1) {
                throw new IllegalArgumentException("Method " + method+"has @Subscribe annotation but requires "
                        + parameterTypes.length + " arguments. Methods must require a single argument.");
            }

            Class<? >eventType = parameterTypes[0].// If the first argument is of type interface, an exception is thrownif (eventType.isInterface()) {
                throw new IllegalArgumentException("Method " + method+"has @Subscribe annotation on"+eventType
                        + " which is an interface. Subscription must be on a concrete class type."); } // The public type of the method must bepublic.privateThrows an exceptionif ((method.getModifiers() & Modifier.PUBLIC) = =0) {
                throw new IllegalArgumentException("Method " + method+"has @Subscribe annotation on"+eventType
                        + " but is not 'public'.");
            }

            Set<Method> methods = subscriberMethods.get(eventType);
            if (methods == null) {
                methods = new HashSet<Method> ();
                subscriberMethods.put(eventType, methods);
            }
            methods.add(method);
        } else if (method.isAnnotationPresent(Produce.class)) {
            Class<? > []parameterTypes = method.getParameterTypes(a);// Produce if not equal to0, throws an exceptionif(parameterTypes.length ! =0) {
                throw new IllegalArgumentException("Method " + method+"has @Produce annotation but requires "
                        + parameterTypes.length + " arguments. Methods must require zero arguments."); } // Produce returns a value of type Void, otherwise an exception is thrownif (method.getReturnType() == Void.class) {
                throw new IllegalArgumentException("Method " + method
                        + " has a return type of void. Must declare a non-void type.");
            }

            Class<? >eventType = method.getReturnType(a);
            // ReturnThrows an exception if the type is interfaceif (eventType.isInterface()) {
                throw new IllegalArgumentException("Method " + method+"has @Produce annotation on"+eventType
                        + " which is an interface. Producers must return a concrete class type."); } // Produce returns a value of type void, otherwise an exception is thrownif (eventType.equals(Void.TYPE)) {
                throw new IllegalArgumentException("Method " + method+"has @Produce annotation but has no return type. ");} // The public type of the method must bepublic.privateThrows an exceptionif ((method.getModifiers() & Modifier.PUBLIC) = =0) {
                throw new IllegalArgumentException("Method " + method+"has @Produce annotation on"+eventType
                        + " but is not 'public'.");
            }

            if (producerMethods.containsKey(eventType)) {
                throw new IllegalArgumentException("Producer for type " + eventType + " has already been registered.");
            }
            producerMethods.put(eventType, method);
        }
    }

    PRODUCERS_CACHE.put(listenerClass, producerMethods);
    SUBSCRIBERS_CACHE.put(listenerClass, subscriberMethods);
}Copy the code

Register is to cache the @subcribe and @produce methods of each registration

Bus post method

Post (Object) is the parameter type of the @subcribe method registered according to the corresponding Class of Object. Queue up a consistent list of methods and execute them one by one

public void post(Object event) {
    if (event= =null) {
        throw new NullPointerException("Event to post must not be null.");
    }
    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) {
                // Queue the method of the Subcribe method corresponding to the Post argument
                enqueueEvent(event, wrapper); }}}if(! dispatched && ! (event instanceof DeadEvent)) {
        post(new DeadEvent(this.event));
    }
    // Send events for the methods saved in the queue above
    dispatchQueuedEvents();
}Copy the code

Execution method (which is implemented by reflection, inter-thread communication)

Method.invoke (target, event);

  public void handleEvent(Object event) throws InvocationTargetException {
if(! valid) {throw new IllegalStateException(toString() + " has been invalidated and can no longer handle events.");
}
try {
  method.invoke(target, event);
} catch (IllegalAccessException e) {
  throw new AssertionError(e);
} catch (InvocationTargetException e) {
  if (e.getCause() instanceof Error) {
    throw (Error) e.getCause();
  }
  throw e;
}Copy the code

}

Bus unregister method

Unregister (object) is a method that removes the @subcribe and @produce corresponding to this object from the cache

public void unregister(Object object) {
    if (object= =null) {
        throw new NullPointerException("Object to unregister must not be null.");
    }
    enforcer.enforce(this);

    // Empty the @produce method in this ObjectMap<Class<? >, EventProducer> producersInListener = handlerFinder.findAllProducers(object);
    for(Map.Entry<Class<? >, EventProducer> entry : producersInListener.entrySet()) { final Class<? > key = entry.getKey(); EventProducer producer = getProducerForEventType(key); EventProducervalue = 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(); } Map<Class<? >, Set<EventHandler>> handlersInListener = handlerFinder.findAllSubscribers(object);
    for(Map.Entry<Class<? >, Set<EventHandler>> entry : handlersInListener.entrySet()) { Set<EventHandler> currentHandlers = getHandlersForEventType(entry.getKey()); Collection<EventHandler> eventMethodsInListener = entry.getValue();if (currentHandlers == null| |! currentHandlers.containsAll(eventMethodsInListener)) {throw new IllegalArgumentException(
                    "Missing event handler for an annotated method. Is " + object.getClass()
                            + " registered?");
        }

        for (EventHandler handler : currentHandlers) {
            if(eventMethodsInListener.contains(handler)) { handler.invalidate(); }}// Empty the @subcribe method in this ObjectcurrentHandlers.removeAll(eventMethodsInListener); }}Copy the code

The relevant data

Android Event Bus OTTO quick start usage

EventBus vs Otto vs Guava– Custom message bus

Use and comparison of EventBus & Otto

Analyze the Otto framework and compare it with EventBus





Come on