EventBus principle and source code analysis

I. Overview

  EventBus is a model-based publishing observer / subscribe event bus frame. The sender and receiver separated events, which can simplify the communication between the components, which is more lightweight with respect BroadcastReceiver also easier to use.

Second, the use of introduced

  EventBus usage is very simple, it is roughly four steps:

  1. Registration Event

 @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate (savedInstanceState);
        setContentView(R.layout.activity_main);
        /**
         * Registration observer objects
         */
        EventBus.getDefault().register(this);
    }

  2. Consumer Events

 /**
     * For receiving events sent by post
     * @Param event specific event subject
     */
    @Subscribe
    public void onMessageEvent(MessageEvent event) {
        switch (event.getArg1()) {
            case 0:
                break;
            case 1:
                break;

        }
    }

  3. Cancel Event

 @Override
    protected void onDestroy() {
        super.onDestroy ();
        /**
         * unsubscribe
         */
        EventBus.getDefault().unregister(this);
    }

  4. Send event

    /**
     * send messages
     */
    public void toPost() {
        MessageEvent event = new MessageEvent();
        event.setArg1(0);
        event.setObj(new Object());
        event.setMsg ( "I send a message");
        EventBus.getDefault().post(event);
    }

  

Third, the specific source code analysis

  1. We start from the beginning to use, look under EventBus.getDefault () have done something to write.

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

  From the above we can see that the code is a singleton class EventBus but lazy formula single embodiment, to ensure that only one instance of EventBus throughout the application.

  Some will be initialized in its constructor work, such as:

public EventBus() {
        this(DEFAULT_BUILDER);
    }

    EventBus(EventBusBuilder builder) {
        logger = builder.getLogger();
        // Find the subscription object by subscribing to a set of event types map
        subscriptionsByEventType = new HashMap<>();
        typesBySubscriber = new HashMap<>();
        // viscous event
        stickyEvents = new ConcurrentHashMap<>();
        // main thread support
        mainThreadSupport = builder.getMainThreadSupport();
        // handler objects
        mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
        // runable objects
        backgroundPoster = new BackgroundPoster(this);
        //Thread Pool
        asyncPoster = new AsyncPoster(this);
        indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
        // set of methods subscribers
        subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
                builder.strictMethodVerification, builder.ignoreGeneratedIndex);
        logSubscriberExceptions = builder.logSubscriberExceptions;
        logNoSubscriberMessages = builder.logNoSubscriberMessages;
        sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
        sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
        throwSubscriberException = builder.throwSubscriberException;
        eventInheritance = builder.eventInheritance;
        // default thread, CachedThreadPool
        executorService = builder.executorService;
    }

  1.subscriptionsByEventType Find HashMap collection of subscription by event.getClass type. The subscription is stored in the object subscriberMethod, subscriberMethod object and is stored in a subscriber by subscriber comment marked Method, time types event.getClass, whether it is viscous event, threading model, and priority execution time.

  2.mainThreadPoster, in fact, it is HandlerPoster inherited handler and implemented poster, which can be understood as the main thread handler. Enqueue incoming calls its method of subscription and subscriber. And put a subscription method is executed in the main thread by eventbus.invokeSubscriber (pendingPost) method in its handleMessage

  3.BackgroundPoster achieved runnable and poster object, the method enqueue incoming subscriber and subscription, performs eventbus.invokeSubscriber (pendingPost) in the run method

  4.AsyncPoster Poster and implements the Runnable interface also call enqueue the Subscription and subscriber incoming and AsyncPoster object runs in the thread pool, and execution eventBus.invokeSubscriber run method (pendingPost)

  5.executorService thread pool, the specific implementation is CachedThreadPool  

  More than a few steps behind the analysis will return to use.

  2.EventBus.getDefault().register(this)方法

    /**
     * Registered subscriber objects
     * @Param subscriber subscribers
     */
    public void register(Object subscriber) {
        // Get the subscriber's class objects
        Class<?> subscriberClass = subscriber.getClass();
        // set of SubscriberMethod method according to the subscriber class object or subscribers subscribe marked
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        synchronized (this) {// locked, you can only enter one thread
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                // start a subscription, the subscription cycle to add objects to subscriptionsByEventType in. Specific approach is to remove the subscription set according to the type of event, and then constantly added to the collection of subscription
                // subscribe to, and eventually stored in subscriptionsByEventType
                subscribe(subscriber, subscriberMethod);
            }
        }
    }

  In the internal register method, you will get to the subscriber subscriber (activity) of the class object. And the method of obtaining the subscriber with the subscriber class by a set of labeled objects. Obtained by SubscriberMethodFinder.findSubscriberMethods (subscriberClass).

  First, the cache will be retrieved from inside the findSubscriberMethods, if the cache and direct return, if not resolved subscriberClass objects in the cache, and the method used subscriberClass target subscriber to get annotated annotated method parameter types, threading model, event execution priority, whether the event is an adhesive and the package information into SubscriberMethod above object. Then SubscriberMethod objects to the collection returned.

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
        // start acquiring the cache, if you get to a direct return. If the cache does not have to look for methods with subscribe labeled and placed in the collection
        // Because the method using a class of objects subscribe label may be more than one, so we use the set to go into all of them
        List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
        if (subscriberMethods != null) {
            return subscriberMethods;
        }
        // whether to force a reflection (even if there is generated index)
        if (ignoreGeneratedIndex) {
            subscriberMethods = findUsingReflection(subscriberClass);
        } else {
            subscriberMethods = findUsingInfo(subscriberClass);
        }
        if (subscriberMethods.isEmpty()) {
            throw new EventBusException("Subscriber " + subscriberClass
                    + " and its super classes have no public methods with the @Subscribe annotation");
        } else {
            METHOD_CACHE.put(subscriberClass, subscriberMethods);
            return subscriberMethods;
        }
    }

  

private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
        FindState findState = prepareFindState();
        findState.initForSubscriber(subscriberClass);
        while (findState.clazz! = null) {// If the subscriber object is not empty, perform the following while
            findUsingReflectionInSingleClass(findState);
            // find parent
            findState.moveToSuperclass();
        }
        return getMethodsAndRelease(findState);
    }

    private void findUsingReflectionInSingleClass(FindState findState) {
        Method[] methods;
        try {
            // subscriber acquired by the reflection method set
            methods = findState.clazz.getDeclaredMethods();
        } catch (Throwable th) {
            methods = findState.clazz.getMethods();
            findState.skipSuperClasses = true;
        }
        for (Method method : methods) {
            int modifiers = method.getModifiers();
            // filtration method public tagging
            if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
                <?> Class [] parameterTypes = method.getParameterTypes (); // Get Parameter Type
                // Find a single parameter of
                if (parameterTypes.length == 1) {
                    // find a way to have annotated subscribe
                    Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                    if (subscribeAnnotation != null) {
                        // Get the type of event, since there is only one parameter so parameterTypes take the first on the line
                        Class<?> eventType = parameterTypes[0];
                        if (findState.checkAdd(method, eventType)) {
                            // Get the threading model, this model is used for labeling, user-defined event is executed or executed in the child thread in the main thread
                            ThreadMode threadMode = subscribeAnnotation.threadMode();
                            // The method subscribers into subscriberMethod, and the SubscriberMethod into the collection method
                            findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
                                    subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
                        }
                    }
             .... omitted some code
        }
    }

  Then look back subscriber method findSubscriberMethods method EventBus in this approach is the implementation of a subscription operation.

  synchronized (this) {// locked, you can only enter one thread
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                // start a subscription, the subscription cycle to add objects to subscriptionsByEventType in. Specific approach is to remove the subscription set according to the type of event, and then constantly added to the collection of subscription
                // subscribe to, and eventually stored in subscriptionsByEventType
                subscribe(subscriber, subscriberMethod);
            }
        }

  SubscriberMethods loop through the collection, and the subscriber and SubscriberMethod encapsulated Subscription. Subscription and add to the collection of subscriptions. And to eventType is key, subscriptions for the value to be cached named HashMap subscriptionsByEventType in.

 private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        // Get event type from the subscriberMethod
        Class<?> eventType = subscriberMethod.eventType;
        // subscribe to the subscriber object and method of deposit Subscription
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
        // Remove the existing cache subscription set (based on the event type), if not create a
        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        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 || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
                // add a subscription to subscribe to the collection
                subscriptions.add(i, newSubscription);
                break;
            }
        }

        List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
        if (subscribedEvents == null) {
            subscribedEvents = new ArrayList<>();
            typesBySubscriber.put(subscriber, subscribedEvents);
        }
        subscribedEvents.add(eventType);

        ... some code omitted
    }

  Subscribe to this end.

  to sum up:

  1. initialized, such as the respective attributes EventBus: HashMap, thread pool

  2. Get the subscriber class of the object, and acquires the collection of attributes with subscriber annotation method subscriberClass marked (method, parameter types, threading model, whether the event is a viscous, priority events, etc.) by SubscriberMethodFinder.findSubscriberMethods (subscriberClass). And there are a determination condition, first check whether the cache set with these methods, if there is return directly, if not set editing method subscriberClass subscriber direct labeling method is selected, and the method of extraction, the parameter type, the thread model, whether it is a viscous event, event priority information, and package as SubscriberMethod object and objects in SubscriberMethod List collection returned.

  3. subscriberMethods combination is traversed by subscribing subscribe method. Subscription object creates an internal subscribe method and subscriber objects and SubscriberMethod deposit options. Subscription and added to the object set CopyOnWriteArrayList subscriptions. And to SubscriberMethod.eventType is key, subscriptions for the value of the data stored in subscriptionsByEventType HashMap. This subscription is over.

 

  2.EventBus.getDefault (). Post (event) using the post method to send subscription notifications

  

public void post(Object event) {
        // current thread state data extracted from a ThreadLocal
        PostingThreadState postingState = currentPostingThreadState.get();
        // Remove the event queue from the current thread (in fact, not a queue is a list collection)
        List<Object> eventQueue = postingState.eventQueue;
        // adding events to the event queue in the current thread
        eventQueue.add(event);

        if (!postingState.isPosting) {
            // determine the current thread is not the main thread
            Postidargshttekisnaentread = Isnaentread ();
            postingState.isPosting = true;
            if (postingState.canceled) {
                throw new EventBusException("Internal error. Abort state was not reset");
            }
            try {
                while (! eventQueue.isEmpty ()) {// If the event is not empty, then the event distribution
                    postSingleEvent(eventQueue.remove(0), postingState);
                }
            } finally {
                postingState.isPosting = false;
                postingState.isMainThread = false;
            }
        }
    }

  It creates a PostingThreadState object and a message queue eventQueue inside the post. Wherein PostingThreadState is a ThreadLocal object (which may store data among different threads, and independent data between threads)

  And postingState initialized, followed by calls postSingleEvent methods, and events and postingState pass inside. In turn, calls postSingleEventForEventType method within postSingleEvent method.

private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
        CopyOnWriteArrayList <Subscription> subscriptions; // subscription set
        synchronized (this) {
            // Remove the subscription set subscription from the map based on the event type
            subscriptions = subscriptionsByEventType.get(eventClass);
        }
        if (subscriptions != null && !subscriptions.isEmpty()) {
            // Loop through subscription set
            for (Subscription subscription : subscriptions) {
                postingState.event = event;
                postingState.subscription = subscription;
                boolean aborted = false;
                try {
                    // events are distributed according threading model
                    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;
    }

  Subscriptions taken in accordance eventClass HashMap subscriptionsByEventType set of loops through the collection and distribution of subscriptions to events by methods postToSubscription

 private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
        switch (subscription.subscriberMethod.threadMode) {// thread removed from the subscription model object
            case POSTING: // default mode, in which the subscription is executed in the thread which thread
                // specific distribution
                invokeSubscriber(subscription, event);
                break;
            case MAIN: // transmitted as the main thread (UI thread) event, the main thread handling events directly; if the transmission event child thread, the first event queues, and then switch to the main thread by Handler, sequentially processes the event.
                if (isMainThread) {
                    invokeSubscriber(subscription, event);
                } else {
                    mainThreadPoster.enqueue(subscription, event);
                }
                break;
            case MAIN_ORDERED: // send events both in that thread are first event into the queue, and then switch to the main thread by Handler, in order to handle events
                if (mainThreadPoster != null) {
                    mainThreadPoster.enqueue(subscription, event);
                } else {
                    // temporary: technically not correct as poster not decoupled from subscriber
                    invokeSubscriber(subscription, event);
                }
                break;
            case BACKGROUND: // send event if the main thread, the first event queues, and then sequentially processed by an event thread pool; child thread if the transmission event, the event process thread sends event directly
                if (isMainThread) {
                    backgroundPoster.enqueue(subscription, event);
                } else {
                    invokeSubscriber(subscription, event);
                }
                break;
            case ASYNC: // send events both in that thread, the event will enter the queue, and then processed by the thread pool
                asyncPoster.enqueue(subscription, event);
                break;
            default:
                throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
        }
    }

  As can be seen from the above code different models based on the thread will execute the contents of different methods. Look under invokeSubscriber (subscription, event) did what methods are

 /**
     * Subscribe to the specific implementation method of the class
     * @Param subscription subscription object
     * @Param event event
     */
    void invokeSubscriber(Subscription subscription, Object event) {
        try {
            // Remove SubscriberMehtod from the subscription object, and then call its method Method and implement methods invoke to complete execution of events
            subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
        } catch (InvocationTargetException e) {
            handleSubscriberException(subscription, event, e.getCause());
        } catch (IllegalAccessException e) {
            throw new IllegalStateException("Unexpected exception", e);
        }
    }

  Implementation of the above method is very simple, in fact the object is removed from the subscription subscriberMethod in and taken out subscriberMethod object invoke method performs the method and process function method. After completion of the implementation of this paragraph in the object method subscriber by subscriber mark notes it will be executed. That is where the unknown, from the subscription to send to the receiver has finished execution.

  It says that sending thread model to receive events in which thread in the thread. Said lower surface of the other two lines, anyway executed in the main thread and the child thread executed anyway. Mainly to see two classes HandlerPoster and BackgroundPoster represented by the two explain.

  Look at HandlerPoster

public class HandlerPoster extends Handler implements Poster {
  ..... omitted some code
    public 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");
                }
            }
        }
    }

    @Override
    public void handleMessage(Message msg) {
        boolean rescheduled = false;
        try {
            long started = SystemClock.uptimeMillis();
            while (true) {
               ... some code omitted
                eventBus.invokeSubscriber(pendingPost);
                .... omitted some code
}

  If the child thread then sends the event, and then let the execution in the main thread. So we must first call HandlerPoster.enqueue method, internal enqueue method calls sendMessage send a message to the main thread Handler, will receive the message notification within handleMessage Handler and perform eventBus.invokeSubscriber (pendingPost). This step is in fact clear to see, that is, by performing the chant reflection method, except that the execution has been switched to the main thread.

  Then look at BackgroundPoster this class.

final class BackgroundPoster implements Runnable, Poster {

    ... omits some diamante
    public void enqueue(Subscription subscription, Object event) {
        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
        synchronized (this) {
            queue.enqueue(pendingPost);
            if (!executorRunning) {
                executorRunning = true;
                eventBus.getExecutorService().execute(this);
            }
        }
    }

    @Override
    public void run() {
        try {
            try {
                while (true) {
                   ... some code omitted
                    eventBus.invokeSubscriber(pendingPost);
                }
            } catch (InterruptedException e) {
                eventBus.getLogger().log(Level.WARNING, Thread.currentThread().getName() + " was interruppted", e);
            }
        } finally {
            executorRunning = false;
        }
    }

}

  From the above we can see the source code implements Runnable BackgroundPoster poster and interfaces, and implements methods enqueue and run method. In the run method calls inside enqueue eventbus thread pool execute (this) method runs BackgroundPoster object inside the run method calls eventBus.invokeSubscriber (pendingPost) method, which purpose is very clear, with the implementation of subscriber target by reflection subscriber method annotated notes.

  to sum up:

  1. postingThreadState inside the post method is initialized and assigned

  2. Call postSingleEvent method, method call postSingleEventForEventType inside postSingleEvent method according eventType subscriptionsByEventType removed from inside postSingleEventForEventType method Subscriptions set. Subscriptions loop through the set, and performs postToSubscription method. Corresponding method will be performed inside postToSubscription live models according to different methods. Such as: a direct execution invokeSubscriber (subscription, eventObj), 2 performs the handover event is performed by the main thread HandlerPoster.enqueue, ultimately performs invokeSubscriber (pendingPost) method, a method BackgroundPoster.enqueue 3 by switching to time. or sub-thread execution thread pool will eventually execute eventBus.invokeSubscriber (pendingPost) method in the run method. The final three steps above will call subscription.subscriberMethod.method.invoke (subscriptions.subscriber, eventObj), to complete the final implementation, the method of execution subscriber mark notes will be executed.

3. Next, we look to receive events, as a way for subscriber annotated labels. In fact, do not look at this method, because this method can customize the rules they meet on the line. Mainly look at the definition Subscriber annotation and ThreadMode definition deepen understanding of annotations and threading models.

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
    // Specify subscribe to an event in which thread runs, the default is POSTING, that is sent in which it is received in the thread which thread
    ThreadMode threadMode() default ThreadMode.POSTING;
    // viscous event, the default is false
    boolean sticky() default false;
    // Subscribe priority event execution
    int priority() default 0;
}

  

/**
 * EventBus threading model
 */
public enum ThreadMode {
    /**
     * In which thread to send events to receive event in which the thread
     */
    POSTING,
    /**
     * If it is sent to the main thread event handling in the main thread, if the child thread then transmits the event to put the event such as queues, then switch to the main thread handler process by
     */
    MAIN,
    /**
     * Whether the main thread or sub-thread sending events, the event will be first in the queue, and then switch to the main thread by Handler, in turn processes the event.
     */
    MAIN_ORDERED,
    /**
     * If the main thread Send event, then the event into the queue, in order to perform in the thread pool. If you send events in the sub-thread, the thread handling events directly send event.
     */
    BACKGROUND,
    /**
     * Whether or send the main thread in the sub-thread event, a great time to queues, followed by the implementation of the thread pool
     */
    ASYNC
}

  Not much to say, comment is very clear.

 

4. Next, look at how the event is canceled registration, namely EventBus.getDefault (). UnRegister (this)

  public synchronized void unregister(Object subscriber) {
        List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
        if (subscribedTypes != null) {
            for (Class<?> eventType : subscribedTypes) {
                unsubscribeByEventType(subscriber, eventType);
            }
            typesBySubscriber.remove(subscriber);
        } else {
            logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
        }
    } 
   private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
        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);
                if (subscription.subscriber == subscriber) {
                    subscription.active = false;
                    subscriptions.remove(i);
                    i--;
                    size--;
                }
            }
        }
    }

  Registration is very simple on this two-step process: 1. remove the object typesBySubscriber collection. 2. Remove the Subscriptions by eventType from subscriptionsByEventType set, and then locate the subscriber equal Subscription.subscriber, and then removed from the set on the line off.

  

  

  

  

  

Guess you like

Origin www.cnblogs.com/tony-yang-flutter/p/12459798.html