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:
onEvent-- comment ThreadMode.POSTING;
onEventMainThread-- comment ThreadMode.MAIN
onEventAsync-- comment ThreadMode.BACKGROUND
onEventBackgroundThread-- comment ThreadMode.ASYNC
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.
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.
/** 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
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);
}
}
}
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.
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.
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.
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
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 Introduction GitHub:https://github.com/greenrobot/EventBus EventBus to talk threads bus is doing, environment, advantages, disadvantages. Doing?...
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...
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 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...
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...
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...
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...
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...
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...