Android event bus: EventBus

tags: android  eventbus  eventbus upgrade

Recently another project to maintain, are relatively old variety of libraries used, eventbus using the 2.x version, then upgrade to one, by the way read the next eventbus source, make a note of this:

EventBus2.x upgrade 3.x
Correspondence between the 2.x and 3.x:
onEvent-- comment ThreadMode.POSTING;
 onEventMainThread-- comment ThreadMode.MAIN
 onEventAsync-- comment ThreadMode.BACKGROUND
 onEventBackgroundThread-- comment ThreadMode.ASYNC
1. Replace the package name

Eventbus were changed after the upgrade package, can ctrl + shift + r global substitution, may be provided to the studio name automatically imported package
import de.greenrobot.event.EventBus; replace import org.greenrobot.eventbus.EventBus; replace all.

2. The method requires the use annotation mode

ctrl + shift + f global search sequentially added 4 annotation method mentioned above.
method onEvent example, search for "public void onEvent (" Add "@Subscribe (threadMode = ThreadMode.POSTING)"
3.EventBus 3.0 version removed registerSticky, each process configured to replace the annotation sticky = true. Search ".registerSticky (", registerSticky replaced back register, then in the class Notes on the way to add sticky.

EventBus Source Interpretation:
1.EventBus constructor
/** Convenience singleton for apps using a process-wide EventBus instance. */
public static EventBus getDefault() {
    if (defaultInstance == null) {
        synchronized (EventBus.class) {
            if (defaultInstance == null) {
                defaultInstance = new EventBus();
            }
        }
    }
    return defaultInstance;
}

Here's a simple interest mode by double-check pattern, then look EventBus constructors have done what:

...
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
...
public EventBus() {
    this(DEFAULT_BUILDER);
}

this calls another constructor eventbus of:

EventBus(EventBusBuilder builder) {
    logger = builder.getLogger();
    subscriptionsByEventType = new HashMap<>();
    typesBySubscriber = new HashMap<>();
    stickyEvents = new ConcurrentHashMap<>();
    mainThreadSupport = builder.getMainThreadSupport();
    mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
    backgroundPoster = new BackgroundPoster(this);
    asyncPoster = new AsyncPoster(this);
    indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
    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;
    executorService = builder.executorService;
}

Pattern builder used here to be configured by configuring a EventbusBuilder Eventbus

2. Subscribe Register

After obtaining the Eventbus, you can be registered subscribers to Eventbus, register as follows:

public void register(Object subscriber) {
    Class<?> subscriberClass = subscriber.getClass();
         // Find the set of subscribers (passed in subscriber) all methods Subscribe
    List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
    synchronized (this) {
                 Methods // Traverse subscribers, complete the registration
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            subscribe(subscriber, subscriberMethod);
        }
    }
}
1) Find the subscription method subscriber

We can see, the registration method does two things: ① Find all subscribers registered in the subscription method, ② subscribers. SubscriberMethod principal method used to save the subscription Method object, the thread mode (threadMode), event type (eventType), priority (priority), whether the event viscous (sticky) and so on. findSubscriberMethods as follows:

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
         // Check whether there are cache
    List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
    if (subscriberMethods != null) {
        return subscriberMethods;
    }
    ```
         // determines whether to ignore the generated annotation MyEventBusIndex (Reference http://greenrobot.org/eventbus/documentation/subscriber-index/)
         // default false, can be set by EventbusBuilder. So we use commonly used method to obtain a findUsingInfo subscription set of methods
    ```
    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 {
                 // The subscription set of methods into the cache, direct access to the next, to avoid repeated lookups
        METHOD_CACHE.put(subscriberClass, subscriberMethods);
        return subscriberMethods;
    }
}

Then look findUsingInfo method:

private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
    FindState findState = prepareFindState();
    findState.initForSubscriber(subscriberClass);
    while (findState.clazz != null) {
                 // Get the subscriber information
        findState.subscriberInfo = getSubscriberInfo(findState);
                 // If we pass EventBuilder configured MyEventBusIndex, you will get to subscriberInfo
        if (findState.subscriberInfo != null) {
            SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
            for (SubscriberMethod subscriberMethod : array) {
                if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
                    findState.subscriberMethods.add(subscriberMethod);
                }
            }
                         // is not configured by EventBuilder MyEventBusIndex, calling this method will save the information to subscription findState, default is not configured, go to this method
        } else {
            findUsingReflectionInSingleClass(findState);
        }
        findState.moveToSuperclass();
    }
         // to findState do recycling, collection and returns a subscription method
    return getMethodsAndRelease(findState);
}

more is not configured MyEventBusIndex, look findUsingReflectionInSingleClass following methods:

private void findUsingReflectionInSingleClass(FindState findState) {
    Method[] methods;
    try {
                 // Get all subscribers in a method by reflection, this method () is faster than getMethods, more particularly of hydrocarbons such as the code Activity
        methods = findState.clazz.getDeclaredMethods();
    } catch (Throwable th) {
        methods = findState.clazz.getMethods();
        findState.skipSuperClasses = true;
    }
    for (Method method : methods) {
                 // Get method modifiers, eg. Public
        int modifiers = method.getModifiers();
        if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
            Class<?>[] parameterTypes = method.getParameterTypes();
            if (parameterTypes.length == 1) {
                Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                if (subscribeAnnotation != null) {
                    Class<?> eventType = parameterTypes[0];
                    if (findState.checkAdd(method, eventType)) {
                        ThreadMode threadMode = subscribeAnnotation.threadMode();
                                                 // method to save the subscription-related information to the findstate
                        findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
                                subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
                    }
                }
            } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
                String methodName = method.getDeclaringClass().getName() + "." + method.getName();
                throw new EventBusException("@Subscribe method " + methodName +
                        "must have exactly 1 parameter but has " + parameterTypes.length);
            }
        } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
            String methodName = method.getDeclaringClass().getName() + "." + method.getName();
            throw new EventBusException(methodName +
                    " is a illegal @Subscribe method: must be public, non-static, and non-abstract");
        }
    }
}

At this point, look for the end of the subscriber's subscription method.

2) the registration process subscribers

After completion of the subscription method to find subscribers, they begin to re-register all subscribers methods, back to the register method, see where the call subscribe method:

// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
         // type of subscriber subscription method
    Class<?> eventType = subscriberMethod.eventType;
    Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
         // eventType get subscription object according collection, if it is empty then re-create
    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) {
            subscriptions.add(i, newSubscription);
            break;
        }
    }

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

    if (subscriberMethod.sticky) {
        if (eventInheritance) {
            // Existing sticky events of all subclasses of eventType have to be considered.
            // Note: Iterating over all events may be inefficient with lots of sticky events,
            // thus data structure should be changed to allow a more efficient lookup
            // (e.g. an additional map storing sub classes of super classes: Class -> List<Class>).
            Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
            for (Map.Entry<Class<?>, Object> entry : entries) {
                Class<?> candidateEventType = entry.getKey();
                if (eventType.isAssignableFrom(candidateEventType)) {
                    Object stickyEvent = entry.getValue();
                    checkPostStickyEventToSubscription(newSubscription, stickyEvent);
                }
            }
        } else {
            Object stickyEvent = stickyEvents.get(eventType);
            checkPostStickyEventToSubscription(newSubscription, stickyEvent);
        }
    }
}

subscribe mainly do two things, ① the Subscription package according to evntType to subscriptionsByEventType, the subscribedEvents package according to typeBySubscriber subscriber; ② treatment of viscous events.

3. Send event

After acquiring the Eventbus object, through post method to complete the sending of the event:

/** Posts the given event to the event bus. */
public void post(Object event) {
         // PostingThreadState holds the event queue (eventQueue) and thread status information (isPosting, isMainThread, subscription, event, canceled)
    PostingThreadState postingState = currentPostingThreadState.get();
         // get the event queue, and the current event into the event queue
    List<Object> eventQueue = postingState.eventQueue;
    eventQueue.add(event);

    if (!postingState.isPosting) {
        postingState.isMainThread = isMainThread();
        postingState.isPosting = true;
        if (postingState.canceled) {
            throw new EventBusException("Internal error. Abort state was not reset");
        }
        try {
                         // processing queues events
            while (!eventQueue.isEmpty()) {
                postSingleEvent(eventQueue.remove(0), postingState);
            }
        } finally {
            postingState.isPosting = false;
            postingState.isMainThread = false;
        }
    }
}

PostSingleEvent look at what has been done:

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
    Class<?> eventClass = event.getClass();
    boolean subscriptionFound = false;
         // look up whether the parent class, default true, can be configured by EventBusBuilder
    if (eventInheritance) {
                 // Find all registered events
        List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
        int countTypes = eventTypes.size();
        for (int h = 0; h < countTypes; h++) {
            Class<?> clazz = eventTypes.get(h);
            subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
        }
    } else {
        subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
    }
    if (!subscriptionFound) {
        if (logNoSubscriberMessages) {
            logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
        }
        if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                eventClass != SubscriberExceptionEvent.class) {
            post(new NoSubscriberEvent(this, event));
        }
    }
}

Be processed events by postSingleEventForEventType method:

private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
    CopyOnWriteArrayList<Subscription> subscriptions;
    synchronized (this) {
                 // Remove the subscription set of objects corresponding to the event
        subscriptions = subscriptionsByEventType.get(eventClass);
    }
    if (subscriptions != null && !subscriptions.isEmpty()) {
                 // event event and the corresponding Subscription (subscription object) is passed to postingState, to process the event by calling the method postToSubscription
        for (Subscription subscription : subscriptions) {
            postingState.event = event;
            postingState.subscription = subscription;
            boolean aborted = false;
            try {
                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;
}

Look postToSubscription done:

private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
    switch (subscription.subscriberMethod.threadMode) {
        case POSTING:
            invokeSubscriber(subscription, event);
            break;
        case MAIN:
            if (isMainThread) {
                invokeSubscriber(subscription, event);
            } else {
                mainThreadPoster.enqueue(subscription, event);
            }
            break;
        case MAIN_ORDERED:
            if (mainThreadPoster != null) {
                mainThreadPoster.enqueue(subscription, event);
            } else {
                // temporary: technically not correct as poster not decoupled from subscriber
                invokeSubscriber(subscription, event);
            }
            break;
        case BACKGROUND:
            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);
    }
}

According thread mode subscription method, we were treated, if it is Main, a thread if the submission is the main thread, through reflection run directly subscribe to approach, if not the main thread needs to mainThreadPoster to add our subscription event to the main thread queue. mainThreadPoster inherited from Handler, the subscription method to switch to the main thread by Handler.
At this point, the event transmission end.

4. Subscribers to unregister

Registration is canceled after acquiring Eventbus object, call unregieter method.

/** Unregisters the given subscriber from all event classes. */
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());
    }
}

/** Unregisters the given subscriber from all event classes. */
public synchronized void unregister(Object 
 typesBySubscriber is a map collection, used in subscription time. Found subscribedTypes by a set of first subscriber, the subscriber is then removed from the corresponding eventTpye in typesBySubscriber. Traversal subscribedTypes and call unsubscribeByEventType method:
/** Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber. */
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--;
            }
        }
    }
}

Subscriptions to remove removed from subscriptions, complete unsubscribe.

References:
1.https://www.jianshu.com/p/e28e1692d0c7
2.https://github.com/greenrobot/EventBus
3. "Android Advanced Light" (Liu Wangshu) Chapter VII

Intelligent Recommendation

Android development framework series EventBus event bus

Introduction GitHub:https://github.com/greenrobot/EventBus Let's talk about what the EventBus thread bus does, the environment, its advantages and disadvantages. What are you doing? In a word, simple ...

Android development framework EventBus Series event bus

Android development framework EventBus Series event bus Introduction GitHub:https://github.com/greenrobot/EventBus EventBus to talk threads bus is doing, environment, advantages, disadvantages. Doing?...

Android event bus EventBus entry and use

What is the event bus? It is an implementation of the publish-subscribe model. It is a centralized event processing mechanism that allows different components to communicate with each other without in...

Android Event Bus EventBus 3.X.X

In order to simplify and more high-quality communication between Activity, Fragment, Thread and Service, etc., while solving the high coupling between components and still continue to communicate effi...

EventBus framework in android (subscription event bus)

EventBus is a publish/subscribe event bus optimized for Android. The main function is to replace Intent, Handler, BroadCast are passed between Fragment, Activity, Service, and threads Message. The adv...

More Recommendation

Android publish and subscribe event bus EventBus

Overview What is EventBus? Introduction to the internal roles of EventBus Configuration Actual case Overview In the previous traditional Android development, we tended to implement Intent, Handler, Br...

Basic Use of Android Event Bus Eventbus 3.0

I. Overview Android components, communication between threads, can be implemented with Handler, BroadcastReceiver, callback, etc., but the implementation of these methods is a cumbersome. Eventbus can...

VUE's EventBus-Event bus bus

In Vue, the data transmission between components is generally passed on the parent -child component, but in the actual application of the project, there is also no relationship of components that need...

Vue.js event bus (EventBus)

The core concept of many modern JavaScript frameworks and libraries is the ability to encapsulate data and UI in modular, reusable components. This allows developers to avoid writing a lot of duplicat...

Vue event bus (EventBus)

Reference article: The core concept of many modern JavaScript frameworks and libraries is the ability to encapsulate data and UI in modular, reusable components. This allows developers to avoid writin...

Copyright  DMCA © 2018-2026 - All Rights Reserved - www.programmersought.com  User Notice

Top