Android必知必會EventBus源碼分析之注冊

EventBus使用很簡單,了解它的源碼的實(shí)現(xiàn)也是應(yīng)該的,不但學(xué)習(xí)設(shè)計(jì)者的思想,也可以提高自己的能力。

注冊

EventBus.getDefault().register(this);

 public void register(Object subscriber) {
        Class<?> subscriberClass = subscriber.getClass();
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        synchronized (this) {
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);
            }
        }
    }

可見,EventBus使用注解和反射機(jī)制。查看findSubscriberMethods,查找當(dāng)前注冊類是否有訂閱方法。

整個查找訂閱方法就過程開始了


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

        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;
        }
    }
  1. 通過METHOD_CACHE獲取是否有緩存該類的所有方法,有緩存則直接返回一個List<SubscriberMethod>。METHOD_CACHE是一個ConcurrentHashMap,以訂閱類的Class為key,保存所有的訂閱方法List<SubscriberMethod>為value。
  2. 通過跟蹤代碼,ignoreGeneratedIndex默認(rèn)值為false,所有這里執(zhí)行findUsingInfo方法。
 private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
       //finaState保留訂閱類的相關(guān)信息
        FindState findState = prepareFindState();
        //findState的初始化
        findState.initForSubscriber(subscriberClass);
        //由于上一行的初始化,clazz!=null
        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);
    }

配合代碼中的注釋,F(xiàn)indState類主要保留注冊類的相關(guān)信息,在prepareFindStateinitForSubscriber,初始化后,findSate.clazz并不為空,findSate指向當(dāng)前的訂閱類的class。查看getSubscriberInfo
,此時返回值是null。在前面初始化中,findState.subscriberInfo的值是null,為此查看findUsingReflectionInSingleClass。

 private void findUsingReflectionInSingleClass(FindState findState) {
        Method[] methods;
        try {
            // This is faster than getMethods, especially when subscribers are fat classes like Activities
            methods = findState.clazz.getDeclaredMethods();
        } catch (Throwable th) {
            // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
            methods = findState.clazz.getMethods();
            findState.skipSuperClasses = true;
        }
        for (Method method : methods) {
            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();
                            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. 這里通過反射機(jī)制查看訂閱類是否有訂閱方法。
  2. 符合訂閱方法規(guī)則。
  • 訂閱方法必須public修飾,不能是靜態(tài)方法抽象方法。否則會拋出錯誤。
  • 參數(shù)只有一個的。
  • 使用Subscribe注解的
  1. 在符合第二個要求后,通過checkAdd方法檢測訂閱方法是否已存在。如果不存在則封裝成SubscriberMethod保存在findState.subscriberMethods

在這里,我們查看一下FindState類的初始化和checkAdd方法。

        final List<SubscriberMethod> subscriberMethods = new ArrayList<>();
        final Map<Class, Object> anyMethodByEventType = new HashMap<>();
        final Map<String, Class> subscriberClassByMethodKey = new HashMap<>();
        final StringBuilder methodKeyBuilder = new StringBuilder(128);

        Class<?> subscriberClass;
        Class<?> clazz;
        boolean skipSuperClasses;
        SubscriberInfo subscriberInfo;

        void initForSubscriber(Class<?> subscriberClass) {
            this.subscriberClass = clazz = subscriberClass;
            skipSuperClasses = false;
            subscriberInfo = null;
        }

        boolean checkAdd(Method method, Class<?> eventType) {
            // 2 level check: 1st level with event type only (fast), 2nd level with complete signature when required.
            // Usually a subscriber doesn't have methods listening to the same event type.
            Object existing = anyMethodByEventType.put(eventType, method);
            if (existing == null) {
                //該類型參數(shù)未添加
                return true;
            } else {
                if (existing instanceof Method) {
                    //存在相同類型參數(shù),可能方法名不同,或者父類被子類重寫了。
                    if (!checkAddWithMethodSignature((Method) existing, eventType)) {
                        // Paranoia check
                        throw new IllegalStateException();
                    }
                    // Put any non-Method object to "consume" the existing Method
                    anyMethodByEventType.put(eventType, this);
                }
                return checkAddWithMethodSignature(method, eventType);
            }
        }

        private boolean checkAddWithMethodSignature(Method method, Class<?> eventType) {
            methodKeyBuilder.setLength(0);
            methodKeyBuilder.append(method.getName());
            methodKeyBuilder.append('>').append(eventType.getName());

            String methodKey = methodKeyBuilder.toString();
            Class<?> methodClass = method.getDeclaringClass();
            Class<?> methodClassOld = subscriberClassByMethodKey.put(methodKey, methodClass);
           //檢測方法名是否相同
          //檢測methodClassOld是否是methodClass的父類或者接口
           if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) {
                // Only add if not already found in a sub class
                return true;
            } else {
                // Revert the put, old class is further down the class hierarchy
                subscriberClassByMethodKey.put(methodKey, methodClassOld);
                return false;
            }
        }
  • subscriberMethods用于保存所有訂閱方法。anyMethodByEventType以事件類型EventType為key,保存方法Method。subscriberClassByMethodKeymethod.getName()>eventType.getName()為key,保存methodClass。
  • checkAdd的第一步是檢查該事件類型的參數(shù)的方法是否已經(jīng)存在,如果不存在,直接返回true添加,添加該訂閱方法。第二步是,但存在該類型方法時,可能存在方法名不同或者方法名相同父類被子類重寫。此時調(diào)用checkAddWithMethodSignature方法進(jìn)行檢查。
  • checkAddWithMethodSignature方法中,當(dāng)methodClassOld==null,表示方法名不同,那么直接添加訂閱方法。methodClassOld.isAssignableFrom(methodClass)表示methodClassOld是否methodClass的父類或者父接口。如果是的化,也直接添加。

這里的雙重檢查,判斷該訂閱方法是否已存在:

  1. 與已有的訂閱方法參數(shù)類型是否相同,不相同,則直接添加。相同則下一步。
  2. 檢測方法名是否相同,不相同則添加。相同則檢查已有的訂閱方法的類是否是訂閱類的父類或父接口。
    主要是查找所有的訂閱方法,并封裝為SubscriberMethod

回到查到訂閱方法的開始。


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

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

當(dāng)注冊類后但沒有訂閱方法后,會報異常
把查到到的訂閱方法放到METHOD_CACHE,以便下次查找訂閱方法直接返回。

到這里整個查找訂閱方法結(jié)束了

接著回到最開始的方法register

 public void register(Object subscriber) {
        Class<?> subscriberClass = subscriber.getClass();
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        synchronized (this) {
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);
            }
        }
    }

可見,通過subscribe方法將訂閱者和其所有的訂閱方法關(guān)聯(lián)在一起。

 private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        Class<?> eventType = subscriberMethod.eventType;
        //Subscription保存著訂閱者和訂閱方法的信息和狀態(tài)
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
        //獲取該事件類型的所有訂閱者和訂閱方法
        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        if (subscriptions == null) {
            //  如果事件類型為首次添加
            subscriptions = new CopyOnWriteArrayList<>();
            subscriptionsByEventType.put(eventType, subscriptions);
        } else {
            //如果事件類型非首次添加,并存在當(dāng)前訂閱信息,表示該訂閱者已經(jīng)添加該類型的訂閱方法,拋出異常。
            if (subscriptions.contains(newSubscription)) {
                throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
                        + eventType);
            }
        }

        //根據(jù)事件的優(yōu)先權(quán),順序添加到subscriptions中
        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);
            }
        }
    }
  • 主要將訂閱者和訂閱方法關(guān)聯(lián)起來,封裝成Subscription,并以事件類型為key保存到subscriptionsByEventType中。
  • 如果subscriptionsByEventType已有訂閱信息,會拋出異常。
  • 如果訂閱方法是粘性的,則會收到該類型的最后一條粘性廣播。

到這里,EventBus的注冊過程基本就結(jié)束了。

整個流程總結(jié):

1、 尋訂閱類是否有訂閱方法,是否符合要求
2、 將訂閱方法相關(guān)信息封裝成SubscriberMethod,并把所有訂閱方法存在List和緩存到METHOD_CACHE中。
3 、將訂閱者和訂閱方法關(guān)聯(lián)起來,封裝成Subscription。并保存在subscriptionsByEventType。
4、 如果是粘性事件,接收最后一條廣播。

通過注冊流程中,我們應(yīng)該也能猜想EventBus的工作原理了。當(dāng)我們post一個eventType時,去讀取subscriptionsByEventType中該類型的相關(guān)訂閱信息,然后達(dá)到調(diào)用我們訂閱方法。也就是說明為什么EventBus是屬于發(fā)布-訂閱模式。

分析下來,涉及到了單例模式,建造者模式,觀察者模式。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容