csdn blog: http://blog.csdn.net/hjjdehao
Before looking at the source code, we need to master the main knowledge points: collection, reflection, annotation.
Framework is basically written with the knowledge of these three aspects, not the best to master, otherwise you will be dizzy when you look.
1. Source Code Resolution of Registration
The flow chart of the registration process (from the network) is as follows:
When we use EventBus, the first thing we do is to register the event object. Generally speaking, to receive the message, we need to register the information so that the message can be notified to you. So how does EventBus register information? How do you receive events?
If we look at the code with questions, the effect will be better.
Registered Subscriber Code:
//Register the current object to indicate that it receives events EventBus.getDefault().register(this);
Registration source code:
public void register(Object subscriber) { //Get the Class type of the subscriber Class<?> subscriberClass = subscriber.getClass(); //Get the subscriber's subscription method based on the subscriber's Class type List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass); synchronized (this) { for (SubscriberMethod subscriberMethod : subscriberMethods) { subscribe(subscriber, subscriberMethod); } } }
List< SubscriberMethod > subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
This code indicates that all subscribers'subscription methods are retrieved by the subscriber's Class type. So how does it get all the subscription methods?
Let's look at the source code for the findSubscriberMethods method again.
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) { //Find out if there is a subscription method from the cache first List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass); if (subscriberMethods != null) { return subscriberMethods; } //Ignore the MyEventBusIndex class generated by the annotator if (ignoreGeneratedIndex) { //Using Reflection to Get Subscription Method Information in Subscription Class subscriberMethods = findUsingReflection(subscriberClass); } else { //Get the subscription method information of the subscription class from the MyEventBusIndex class generated by the annotator 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; } }
The source code above searches for subscription method information in two ways, as follows:
if (ignoreGeneratedIndex) { //Using Reflection to Get Subscription Method Information in Subscription Class subscriberMethods = findUsingReflection(subscriberClass); } else { //Get the subscription method information of the subscription class from the MyEventBusIndex class generated by the annotator subscriberMethods = findUsingInfo(subscriberClass); }
ignoreGeneratedIndex judges whether your build configuration file configures the annotated processor information as follows:
To say that the difference between the two ways is nothing more than performance and speed. Relatively speaking, annotation processing is better than reflection. Look at the figure below.
The general logic of this method is as follows:
First, the subscription method information is retrieved from the cache, and if not, the subscription method information is retrieved by reflection or annotation.
1. Use Annotation Processor
findUsingInfo(Class< ? > subscriberClass)
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) { FindState findState = prepareFindState(); findState.initForSubscriber(subscriberClass); while (findState.clazz != null) { findState.subscriberInfo = getSubscriberInfo(findState); if (findState.subscriberInfo != null) { SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods(); for (SubscriberMethod subscriberMethod : array) { if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) { findState.subscriberMethods.add(subscriberMethod); } } } else { findUsingReflectionInSingleClass(findState); } findState.moveToSuperclass(); } return getMethodsAndRelease(findState); }
EventBus provides an EventBus Annotation Processor annotation processor to read and parse @Subscribe() annotations at compile time.
Processing the information contained therein and then generating java classes to hold all subscribers'information about subscriptions is much better than using reflection to get those subscribers at run time.
Information is faster.
EventBus 3.0 uses compile-time annotations, not run-time annotations. It can be seen from the table that the performance of callback method table is greatly improved by indexing.
2. Subscription Method Information Obtained by Reflection
Find UsingReflection (subscriberClass) source code
private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) { FindState findState = prepareFindState(); findState.initForSubscriber(subscriberClass); while (findState.clazz != null) { //Obtaining Subscription Method Information by Launching findUsingReflectionInSingleClass(findState); //Find Subscription Method Information for the Parent Class findState.moveToSuperclass(); } return getMethodsAndRelease(findState); }
FindState is an entity class that stores information about subscribers and subscription methods, as well as verifying subscription methods. So much has been written that ultimately findUsingReflectionInSingleClass(findState) is called to get information about the subscription method.
Look at the code:
private void findUsingReflectionInSingleClass(FindState findState) { Method[] methods; try { // This is faster than getMethods, especially when subscribers are fat classes like Activities //Get all subscription methods for subscribers methods = findState.clazz.getDeclaredMethods(); } catch (Throwable th) { methods = findState.clazz.getMethods(); findState.skipSuperClasses = true; } for (Method method : methods) { //Get modified permissions for subscription methods int modifiers = method.getModifiers(); //If it's a public-decorated method and it's not static or abstract, it throws an exception if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) { //Get the parameter type array of the method Class<?>[] parameterTypes = method.getParameterTypes(); //Method must have at least one parameter or throw an exception if (parameterTypes.length == 1) { //Getting Annotation Objects Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class); if (subscribeAnnotation != null) { //Getting parameter type information of event object Class<?> eventType = parameterTypes[0]; //Check if a subscription method has been added if (findState.checkAdd(method, eventType)) { ThreadMode threadMode = subscribeAnnotation.threadMode(); 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"); } } }
Start subscribing to event source code:
There are subscribers and subscription methods. Next is subscribing as a parameter.
// Must be called in synchronized block private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) { //Get the event type Class<?> eventType = subscriberMethod.eventType; //Instance of a Subscriber Information Subscription newSubscription = new Subscription(subscriber, subscriberMethod); //Getting Subscriber Objects Based on Event Types 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(); //Adding Subscriber Objects Based on Priority for (int i = 0; i <= size; i++) { if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) { subscriptions.add(i, newSubscription); break; } } //Getting Subscriber Events Based on Subscriber Objects List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber); if (subscribedEvents == null) { subscribedEvents = new ArrayList<>(); //Save Subscriber Objects and Subscriber Events typesBySubscriber.put(subscriber, subscribedEvents); } subscribedEvents.add(eventType); if (subscriberMethod.sticky) { if (eventInheritance) { 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); } } }
There is only one code for registration, but a lot of work has been done to go deep into it.
In fact, so much work has been done to store the relevant information of subscribers (subscribers, subscription methods, event objects, etc.) and pave the way for the distribution of events in the future.
Subscription logic:
1. First, register () is called to register the subscription object. 2. Get all the subscription methods of the subscription object according to the subscription object. 3. subscriptions ByEventType. put (eventType, subscriptions), according to the event type of all subscriptions of the subscriber, the subscribers are stored in each map collection with event type as key and subscriptions as values. 4. TypeesBySubscriber. put (subscriber, subscribed Events), and then add subscription events to the map collection with subscribers as key s and subscribers as values.
II. Analysis of Event Distribution
Event Distribution Flow Diagram (Source Network)
Registered subscribers are distributed for subsequent events so as to know who to send the event to, and event reception is the subscriber's subscription method.
Event distribution code:
EventBus.getDefault().post(new Event());
Look at the source code of the post method:
A parameter is the event object to be sent
public void post(Object event) { //Gets the status of the current thread PostingThreadState postingState = currentPostingThreadState.get(); //Get the event queue for the current thread List<Object> eventQueue = postingState.eventQueue; eventQueue.add(event); //Determine whether the current thread is in the send state if (!postingState.isPosting) { //Set it as the main thread postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper(); postingState.isPosting = true; if (postingState.canceled) { throw new EventBusException("Internal error. Abort state was not reset"); } //Continuously looping events out of the queue try { while (!eventQueue.isEmpty()) { postSingleEvent(eventQueue.remove(0), postingState); } } finally { postingState.isPosting = false; postingState.isMainThread = false; } } }
PostingThreadState is the static internal class of EventBust, which stores the event queue and thread state of the current thread.
final static class PostingThreadState { final List<Object> eventQueue = new ArrayList<Object>();//Event queue for current thread boolean isPosting;//Is there an event being distributed? boolean isMainThread;//Is the post thread the main thread? Subscription subscription;//Subscriber Object event;//Subscription events boolean canceled;//Whether to cancel }
Parametric 1: Event object
Parametric 2: Send status
PosSingleEvent (eventQueue. remove (0), postingState) executes source code
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error { //Get the Class type of the event object Class<?> eventClass = event.getClass(); boolean subscriptionFound = false; //Determine whether event objects have parent objects if (eventInheritance) { //Find all event types of event objects and their parent types 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) { Log.d(TAG, "No subscribers registered for event " + eventClass); } if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class && eventClass != SubscriberExceptionEvent.class) { post(new NoSubscriberEvent(this, event)); } } }
postSingleEventForEventType(event, postingState, eventClass)
Looking at so many sources, I found that this method is the culprit. The method of ultimately launching events.
Then look at his source code:
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) { CopyOnWriteArrayList<Subscription> subscriptions; synchronized (this) { //Obtain all subscribers by event object type subscriptions = subscriptionsByEventType.get(eventClass); } // if (subscriptions != null && !subscriptions.isEmpty()) { //Travel through all subscribers 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; }
postToSubscription(subscription, event, postingState.isMainThread);
Start processing subscriber information
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 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); } }
invokeSubscriber(subscription, event)
Parametric 1: Subscribers
Parametric 2: Event object
Finally, it is implemented through reflection calls.
void invokeSubscriber(Subscription subscription, Object event) { try { subscription.subscriberMethod.method.invoke(subscription.subscriber, event); } catch (InvocationTargetException e) { handleSubscriberException(subscription, event, e.getCause()); } catch (IllegalAccessException e) { throw new IllegalStateException("Unexpected exception", e); } }
Event distribution logic:
1. Get the event queue. 2. Loop out the events in the queue. 3. Get the subscriber set, then traverse, and finally invoke the subscriber's subscription method through reflection.
III. Resolution of Cancellation of Registration
public synchronized void unregister(Object subscriber) { //Obtaining all subscriptions of a subscriber based on the event type of the subscriber List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber); if (subscribedTypes != null) { for (Class<?> eventType : subscribedTypes) { unsubscribeByEventType(subscriber, eventType); } typesBySubscriber.remove(subscriber); } else { Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass()); } }
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) { //Get all subscribers of event type based on event type 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--; } } } }
Cancellation logic:
1. First, all subscription events are retrieved according to the subscriber. 2. Traversing the subscription event set. 3. Get the subscriber set that subscribes to the event based on the subscription event. 4. Traverse the subscriber set, and then compare the incoming subscribers to determine whether they are the same subscriber. If so, remove it, and vice versa.
Reference resources: http://www.cnblogs.com/all88/archive/2016/03/30/5338412.html