当前位置:网站首页>Interpretation of eventbus source code

Interpretation of eventbus source code

2022-06-23 17:39:00 Pen head

EventBus It's an open source library , It uses publishing / The subscriber pattern is used to decouple the project , Achieve the communication effect between multiple components . The general process is Registered subscribers -> producer POST event -> Subscriber consumption

One 、Register

There are mainly 2 A note ,findSubscriberMethods Function and subscribe function

public void register(Object subscriber) {
    if (AndroidDependenciesDetector.isAndroidSDKAvailable() && !AndroidDependenciesDetector.areAndroidComponentsAvailable()) {
        // Crash if the user (developer) has not imported the Android compatibility library.
        throw new RuntimeException("It looks like you are using EventBus on Android, " +
                "make sure to add the \"eventbus\" Android library to your dependencies.");
    }
    //-------------------------------------------------------1
    Class<?> subscriberClass = subscriber.getClass();
    //-------------------------------------------------------2
    List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
    synchronized (this) {
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            //------------------------------------------------3
            subscribe(subscriber, subscriberMethod);
        }
    }
}

1. Get the current subscriber

2. Get the current class that is @Subscriber The method of annotation is Subscription method , Details please see findSubscriberMethods()

3. Traverse all subscription methods , The subscription event is stored in subscriptionsByEventType、 The subscription type is stored in typesBySubscriber in , View details subscribe()

One 、findSubscriberMethods

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
    //------------------------------------------------------1
    List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
    if (subscriberMethods != null) {
        return subscriberMethods;
    }

   //---------------------------------------------------------2
    if (ignoreGeneratedIndex) {
        subscriberMethods = findUsingReflection(subscriberClass);
    } else {
    //--------------------------------------------------------3
        subscriberMethods = findUsingInfo(subscriberClass);
    }
    if (subscriberMethods.isEmpty()) {
        throw new EventBusException("Subscriber " + subscriberClass
                + " and its super classes have no public methods with the @Subscribe annotation");
    } else {
    //--------------------------------------------------------4
        METHOD_CACHE.put(subscriberClass, subscriberMethods);
        return subscriberMethods;
    }
}

1. Get subscription method from cache

2.ignoreGeneratedIndex Default false, If true, Query subscription method through reflection , For details, see findUsingReflectionInSingleClass()

4. Add the subscription method to METHOD_CACHE In cache .

private void findUsingReflectionInSingleClass(FindState findState) {
    Method[] methods;
    try {
        // ----------------------------------------------1
        methods = findState.clazz.getDeclaredMethods();
    } catch (Throwable th) {
        // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
        try {
            methods = findState.clazz.getMethods();
        } catch (LinkageError error) { // super class of NoClassDefFoundError to be a bit more broad...
            String msg = "Could not inspect methods of " + findState.clazz.getName();
            if (ignoreGeneratedIndex) {
                msg += ". Please consider using EventBus annotation processor to avoid reflection.";
            } else {
                msg += ". Please make this class visible to EventBus annotation processor to avoid reflection.";
            }
            throw new EventBusException(msg, error);
        }
        findState.skipSuperClasses = true;
    }
    //---------------------------------------------------2
    for (Method method : methods) {
    //---------------------------------------------------3
        int modifiers = method.getModifiers();
        //-----------------------------------------------4
        if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
        //-----------------------------------------------5
            Class<?>[] parameterTypes = method.getParameterTypes();
            if (parameterTypes.length == 1) {
                //---------------------------------------6
                Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                if (subscribeAnnotation != null) {
                    Class<?> eventType = parameterTypes[0];
                    if (findState.checkAdd(method, eventType)) {
                        ThreadMode threadMode = subscribeAnnotation.threadMode();
                        //-------------------------------7
                        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");
        }
    }
}

1. Find all methods for registering objects

3. Get the modifier of the method , namely public、private etc.

4. Just look at the modifier public Methods

5. Return the parameter type of the method

6. View only the @subscribe Method of annotation , Subscription method .

7. Add the subscription method to FindState Collection

Two 、subscribe

above findSubscriberMethods Find all subscription methods , Next, you need to deal with these subscription methods , For example, the subscription method cannot be duplicated 、 According to the type of subscription, it is key Store the subscription events to according to priority subscriptionsByEventType in 、 The subscription type is stored in typesBySubscriber in , Later in unregister Use in .

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
    //-------------------------------------------------------1
    Class<?> eventType = subscriberMethod.eventType;
    //-------------------------------------------------------2
    Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
    //-------------------------------------------------------3
    CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
    //-------------------------------------------------------4
    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);
        }
    }

    //--------------------------------------------------------5
    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;
        }
    }

    //-------------------------------------------------------6
    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);
        }
    }
}

1. Get events EventType, For example String Or custom objects

2. Construct a subscription event according to the subscriber and subscription method

3. according to EventType Find all existing subscription Events ,

5. Traverse all of subscriber identical EventType Subscription events , Here we judge the present subscriberMethod Whether the priority of the subscription method is greater than that in the collection subscriberMethod The priority of the , If it is , hold newSubscription Insert in , It also shows that subscription in priority The big one is ahead , In this way, when the event is distributed, it will get .

6. Save all subscription method content types in the current subscriber to typesBySubscriber in , Later in unregister Will use , View details UnRegister

Two 、Post

public void post(Object event) {
    //---------------------------------------------------------1
    PostingThreadState postingState = currentPostingThreadState.get();
    List<Object> eventQueue = postingState.eventQueue;
    eventQueue.add(event);
    //----------------------------------------------------------2
    if (!postingState.isPosting) {
        postingState.isMainThread = isMainThread();
        postingState.isPosting = true;
        if (postingState.canceled) {
            throw new EventBusException("Internal error. Abort state was not reset");
        }
        try {
            while (!eventQueue.isEmpty()) {
            //------------------------------------------------2
                postSingleEvent(eventQueue.remove(0), postingState);
            }
        } finally {
            postingState.isPosting = false;
            postingState.isMainThread = false;
        }
    }
}

1.PostingThreadState Save event queue and thread state information

2. This is used here. ThreadLocal Technical solution , In different threads ,post Don't interfere with each other , If in the same thread , The subscription method is slow ,eventQueue There is always data , Then don't take this step , Just store the subscription directly in the queue . In different threads eventQueue, Do not affect each other .

2. Traverse post Single subscription , View details postSingleEvent()

One 、postSingleEvent

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
    Class<?> eventClass = event.getClass();
    boolean subscriptionFound = false;
    //------------------------------------------------------1
    if (eventInheritance) {
        List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
        int countTypes = eventTypes.size();
        for (int h = 0; h < countTypes; h++) {
            Class<?> clazz = eventTypes.get(h);
            //----------------------------------------------2
            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));
        }
    }
}

1.eventInheritance The default is true, That is, get the current Event Of Class Of all the parent classes of the object Class A collection of objects , Priority is given to reading from the cache , View details lookupAllEventTypes()

2.postSingleEventForEventType View details postSingleEventForEventType Method

3. Can't find subscription Errors will be thrown when subscribing to events No subscribers registered for event perhaps sendNoSubscriberEvent If it is not empty, send NoSubscriberEvent event .

One 、lookupAllEventTypes

Traverse the subscription and its parent class , Store in eventTypesCache in .

private static List<Class<?>> lookupAllEventTypes(Class<?> eventClass) {
    synchronized (eventTypesCache) {
        List<Class<?>> eventTypes = eventTypesCache.get(eventClass);
        if (eventTypes == null) {
            eventTypes = new ArrayList<>();
            Class<?> clazz = eventClass;
            while (clazz != null) {
                eventTypes.add(clazz);
                addInterfaces(eventTypes, clazz.getInterfaces());
                clazz = clazz.getSuperclass();
            }
            eventTypesCache.put(eventClass, eventTypes);
        }
        return eventTypes;
    }
}

Two 、postSingleEventForEventType

Traverse subscription Events , Store subscription events in threadLocal in , At the same time, distribute the event

private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
    CopyOnWriteArrayList<Subscription> subscriptions;
    synchronized (this) {
        subscriptions = subscriptionsByEventType.get(eventClass);
    }
    if (subscriptions != null && !subscriptions.isEmpty()) {
        for (Subscription subscription : subscriptions) {
            postingState.event = event;
            postingState.subscription = subscription;
            boolean aborted;
            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;
}
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
      //---------------------------------------------1
    switch (subscription.subscriberMethod.threadMode) {
        case POSTING:
            //----------------------------------------2
            invokeSubscriber(subscription, event);
            break;
        case MAIN:
            if (isMainThread) {
                invokeSubscriber(subscription, event);
            } else {
            //-----------------------------------------3
                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);
    }
}

1. Take out the thread mode of the subscription method , After that, we can process... According to the thread mode

2. Internally, reflection calls are used directly ,poster.enqueue The internal is also a combination of reflection calls handler Handle

3、 ... and 、UnRegister

public synchronized void unregister(Object subscriber) {
    List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
    if (subscribedTypes != null) {
        for (Class<?> eventType : subscribedTypes) {
            // Yes  subscriptionsByEventType  Removed the  subscriber  All subscription information for 
            unsubscribeByEventType(subscriber, eventType);
        }
        // Removed the registered object and all its corresponding  Event  Event list 
        typesBySubscriber.remove(subscriber);
    } else {
        logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
    }
}
/** 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--;
            }
        }
    }
}
原网站

版权声明
本文为[Pen head]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/01/202201061942102411.html