源码解析之EventBus注册流程

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/pihailailou/article/details/81842960

一,写在前面

EventBus的使用还是比较简单的,对于框架的使用,不仅仅是知其然,更要知其所以然,还可以学到框架里一些优秀的设计理念。本篇文章将从源码角度解析EventBus的注册流程,对框架的使用可参考文章EventBus的使用


二,EventBus构造方法

在使用EventBus订阅事件时,一般会调用EventBus.getDefault()获取EventBus的实例,它返回一个系统默认的EventBus实例。

EventBus$getDefault()源码如下:

    static volatile EventBus defaultInstance;    

    public static EventBus getDefault() {
        if (defaultInstance == null) {
            synchronized (EventBus.class) {
                if (defaultInstance == null) {
                    defaultInstance = new EventBus();
                }
            }
        }
        return defaultInstance;
    }

    public EventBus() {
        this(DEFAULT_BUILDER);
    }

    EventBus(EventBusBuilder builder) {
        logger = builder.getLogger();

        //三个Map集合存储了订阅类,事件类型,订阅方法相关的数据,后面的注册流程会用到
        subscriptionsByEventType = new HashMap<>();
        typesBySubscriber = new HashMap<>();
        stickyEvents = new ConcurrentHashMap<>();

        
        mainThreadSupport = builder.getMainThreadSupport();
        
        //三个重要的Poster,在发送事件的流程中会用到
        mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
        backgroundPoster = new BackgroundPoster(this);
        asyncPoster = new AsyncPoster(this);

        //builder.subscriberInfoIndexes为null,indexCount为0
        indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;

        //参数分别为null, false, false
        subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
                builder.strictMethodVerification, builder.ignoreGeneratedIndex);

        //true
        logSubscriberExceptions = builder.logSubscriberExceptions;
        //true
        logNoSubscriberMessages = builder.logNoSubscriberMessages;
        //true
        sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
        //true
        sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
        //false
        throwSubscriberException = builder.throwSubscriberException;
        //true
        eventInheritance = builder.eventInheritance;
        //线程池对象
        executorService = builder.executorService;
    }

1-12行:采用单例设计模式的双重检查,对开发者提供一个单例的EventBus对象;

18行:借助类EventBusBuilder构造EventBus的初始化数据,具体解释见上述代码片段;


三,register方法

查看EventBus$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);
            }
        }
    }

2行:获取订阅类A的Class实例;

3行:遍历订阅类A及其超类中的订阅方法,subscriberMethodFinder是SubscriberMethodFinder类型的变量,初始化在EventBus的构造方法中完成;

查看SubscriberMethodFinder$findSubscriberMethods方法,源码如下:

    private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>();

    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是一个Map集合,key是订阅类的Class实例,value是存储了SubscriberMethod对象的List集合。SubscriberMethod存储了订阅方法相关的一些数据,后面会讲到。

5行:由于第一次遍历该订阅类的订阅方法,subscriberMethods == null;

9行:由EventBus的构造方法可知,ignoreGeneratedIndex为false,执行else逻辑;

12行:findUsingInfo方法,用于遍历该订阅类中所有的订阅方法,并返回一个List集合,下面会具体分析;

15行:若订阅类及其超类中没有@Subscribe注解的方法,抛出异常给开发者;

19行:遍历完订阅方法后,将数据存储在METHOD_CACHE集合中。若再次遍历订阅方法,直接在缓存METHOD_CACHE中取出数据即可。

查看SubscriberMethodFinder$findUsingInfo方法,源码如下:

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

2行:调用prepareFindState方法,里面维护了一个对象池FIND_STATE_POOL来获取FindState对象;

prepareFindState方法源码:

    private static final FindState[] FIND_STATE_POOL = new FindState[POOL_SIZE];    
    
    private FindState prepareFindState() {
        synchronized (FIND_STATE_POOL) {
            for (int i = 0; i < POOL_SIZE; i++) {
                FindState state = FIND_STATE_POOL[i];
                if (state != null) {
                    FIND_STATE_POOL[i] = null;
                    return state;
                }
            }
        }
        return new FindState();
    }

1行:创建一个FindState的对象数组,作为对象池;

6行:如果对象池中存在FindState对象,则直接使用对象池中的FindState对象,见第9行;

8行:如果对象池中的对象已被使用,需要设置为null;

回到findUsingInfo方法,为方便查看,重新粘贴其代码如下:

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

3行:初始化类FindState中数据;

4行:findState.clazz == subscriberClass,不为null,执行while循环;

5行:由FindState构造方法可知:subscriberInfo为null;由EventBus的构造方法可知:subscriberInfoIndexes为null(暂不考虑索引加速)。于是,getSubscriberInfo方法返回null,执行else逻辑;

14行:调用findUsingReflectionInSingleClass方法,遍历订阅方法,下面会具体分析

16行:调用moveToSuperclass方法,遍历订阅类的超类的订阅方法;

moveToSuperclass方法源码如下(解释见代码注释,很重要):

    void moveToSuperclass() {
            //由构造方法可知:skipSuperClasses为false
            if (skipSuperClasses) {
                clazz = null;
            } else {
                //clazz重新赋值,设置为订阅类的超类
                clazz = clazz.getSuperclass();
                String clazzName = clazz.getName();
                /** Skip system classes, this just degrades performance. */
                //如果当前订阅类的超类是Java,Android提供的类,clazz = null
                if (clazzName.startsWith("java.") || clazzName.startsWith("javax.") || clazzName.startsWith("android.")) {
                    //返回null,findUsingInfo方法跳出while循环
                    clazz = null;
                }
            }
        }

第18行:遍历完订阅类及其超类的订阅方法后,调用getMethodsAndRelease方法,将使用完的FindState对象添加进对象池FIND_STATE_POOL中重复利用。

值得一提的是,EventBus使用对象池来提供FindState对象,重复利用对象池中的对象,很好的节约了内存。

getMethodsAndRelease方法源码:

    private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {
        //创建一个新的List集合,用于存储subscriberMethods中数据
        List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods);
        //清理使用过的FindState对象中的数据,见第18行
        findState.recycle();
        synchronized (FIND_STATE_POOL) {
            for (int i = 0; i < POOL_SIZE; i++) {
                if (FIND_STATE_POOL[i] == null) {
                    //将FindState对象添加进对象池中
                    FIND_STATE_POOL[i] = findState;
                    break;
                }
            }
        }
        return subscriberMethods;
    }

    void recycle() {
        subscriberMethods.clear();
        anyMethodByEventType.clear();
        subscriberClassByMethodKey.clear();
        methodKeyBuilder.setLength(0);
        subscriberClass = null;
        clazz = null;
        skipSuperClasses = false;
        subscriberInfo = null;
    }

下面重点关注findUsingReflectionInSingleClass方法,该方法完成订阅方法的遍历;

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

5行:findState.clazz表示订阅类的Class实例,getDeclaredMethods方法获取该类中所有的方法,包括private,default,protected,public修饰的方法,但不包括继承的方法。

8行:抛出异常,则调用getMethods方法,获取自身以及继承的public方法;

9行:修改skipSuperClasses为true,则前面的moveToSuperclass方法执行if逻辑,findUsingInfo方法跳出循环。因为getMethods方法已经遍历所有的超类的方法,便不再重复遍历超类的订阅方法,所以跳出循环;

13行:如果方法由public修饰,且不由static,abstract修饰,If语句为true。否则抛出异常,执行第30行;

15行:如果方法只有一个参数;

17行:如果方法被@Subscribe注解修饰;

18行,获取事件类型的class实例;

19行,对订阅方法进行过滤,目的:如果子类和其超类中存在一样的订阅方法,只保留子类的。(后面会重点分析checkAdd方法)

20行,获取注解@Subscribe的threadMode属性的值;

21行,将方法,事件类型,注解的属性封装在SubscriberMethod类中,并添加进subscriberMethods集合中;

查看checkAdd方法源码:

    final Map<Class, Object> anyMethodByEventType = new HashMap<>();    

    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) {
                return true;
            } else {
                if (existing instanceof Method) {
                    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);
            }
        }

1行:anyMethodByEventType是一个Map集合,key:事件类型的Class实例,value:Method对象;

6行:如果第一次遍历该事件类型的方法,existing为null,执行if逻辑,返回true。如果是第n(n>=2)次遍历该事件类型的方法,existing != null,执行else逻辑。(系统会遍历当前订阅类及其超类的订阅方法,checkAdd方法可能是在遍历超类,遍历超类就可能出现与子类一样的订阅方法,因此要处理这种情况)

需要注意,有两种情况会再次遍历到事件类型相同的方法,分别是:

  1. 事件类型相同,方法名不同(两个方法在同一个类中;或一个方法在子类,一个方法在超类中)
  2. 事件类型相同,方法名相同(两个方法分别在子类和超类中)

11行,调用checkAddWithMethodSignature方法,第一个参数:Map集合中上一个Method对象,即该事件类型对应的第一个订阅方法;第二个参数:事件类型的Class实例。

该方法的目的:在subscriberClassByMethodKey集合中,存储该事件类型对应的第一个订阅方法的相关数据,这样遍历该事件类型的其他订阅方法会同其进行比较,过滤掉超类中相同的订阅方法。

查看checkAddWithMethodSignature方法源码(解释见代码片段):

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

            //methodKey表示该事件类型对应的第一个订阅方法的方法名,参数类型的拼接字符串;
            String methodKey = methodKeyBuilder.toString();

            //第一个订阅方法所在类的Class实例
            Class<?> methodClass = method.getDeclaringClass();
            
            //第一次添加key为methodKey的数据,methodClassOld == null
            Class<?> methodClassOld = subscriberClassByMethodKey.put(methodKey, 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;
            }
        }

回到checkAdd方法

16行:前面第11行了谈到checkAddWithMethodSignature方法的目的,对于同一个订阅事件,只存储第一个订阅方法的相关数据。这里的this指的是FindState对象,会使得第10行的判断语句为false,将不再调用第11行的checkAddWithMethodSignature方法。

18行:从该事件类型对应的第n(n>=2)个订阅方法开始,将继续调用checkAddWithMethodSignature方法用于过滤不需要的订阅方法,第一个参数是当前订阅方法的Method对象,第二个参数是事件类型的Class实例。

重新查看checkAddWithMethodSignature方法源码:

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

6行:该事件类型对应的当前订阅方法的相关数据,由方法名和参数类型拼接的字符串;

7行:该事件类型对应的当前订阅方法所在类的Class实例;

8行:前面提到,有两种情况会再次遍历到事件类型相同的方法,分别是:

  1. 事件类型相同,方法名不同(两个方法在同一个类中;或一个方法在子类,一个方法在超类中)
  2. 事件类型相同,方法名相同(两个方法分别在子类和超类中)

如果是第1种情况,由于方法名不同,methodKey不同,methodClassOld == null;

如果是第2中情况,由于方法名相同,methodKey相同,methodClassOld返回上一个订阅方法所在类的Class实例;

9行:若是第一种情况,则执行第11行逻辑。若是第二种情况,由于methodClassOld是methodClass的子类,isAssignableFrom方法返回false,执行else逻辑;

14行,在subscriberClassByMethodKey集合中,移除超类中相同的订阅方法,重新保存子类中的订阅方法;

15行,唯一返回false的地方,目的就是过滤掉超类中相同的订阅方法,使checkAdd方法返回false。

小结:遍历订阅类及其超类的订阅方法,并过滤掉超类中重复的订阅方法。


回到register方法,查看源码:

    public void register(Object subscriber) {
        Class<?> subscriberClass = subscriber.getClass();
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        synchronized (this) {
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                //subscriberMethod封装Method,EventType,三个注解的属性值
                subscribe(subscriber, subscriberMethod);
            }
        }
    }

3行:findSubscriberMethods方法已做详细分析,SubscriberMethod类封装了订阅方法,事件类型,注解相关的数据。

7行,调用subscribe方法,第一个参数是订阅类的实例,第二个参数是SubscriberMethod对象。

查看subscribe方法源码:

    private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
    private final Map<Object, List<Class<?>>> typesBySubscriber;
    private final Map<Class<?>, Object> stickyEvents;
    
    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        //返回该subscriberMethod对象的事件类型
        Class<?> eventType = subscriberMethod.eventType;

        //提供一个Subscription封装:订阅类+SubscriberMethod
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);

        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        if (subscriptions == null) {
            subscriptions = new CopyOnWriteArrayList<>();

            //key:事件类型  value:list集合,存储着Subscription(包含:订阅类+SubscriberMethod)
            subscriptionsByEventType.put(eventType, subscriptions);
        } else {

            //第n次调用EventBus$register方法时,if条件里值为true(n>=2)
            if (subscriptions.contains(newSubscription)) {
                throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
                        + eventType);
            }
        }

        //按照subscriberMethod的priority由大到小的顺序,添加进List集合;
        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<>();

            //key:订阅类   value:list集合,存储着事件类型
            typesBySubscriber.put(subscriber, subscribedEvents);
        }
        subscribedEvents.add(eventType);

        //如果订阅方法上@Subscribe注解的sticky属性值为true
        if (subscriberMethod.sticky) {

            //由构造方法可知:eventInheritance为true
            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>).

                /*
                key: postSticky中事件类型的class实例, value:事件类型的实例
                stickyEvents:该Map集合数据的添加,交由EventBus$postSticky方法完成
                 */
                Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();

                for (Map.Entry<Class<?>, Object> entry : entries) {
                    //postSticky方法中的事件类型
                    Class<?> candidateEventType = entry.getKey();

                    //如果postSticky方法中的事件类型,是订阅方法中的事件类型的子类或者是一个类
                    if (eventType.isAssignableFrom(candidateEventType)) {
                        Object stickyEvent = entry.getValue();

                        /*
                        第一个参数:封装了订阅方法相关的信息
                        第二个参数:postSticky方法中的事件类型的实例
                         */
                        checkPostStickyEventToSubscription(newSubscription, stickyEvent);
                    }
                }
            } else {
                Object stickyEvent = stickyEvents.get(eventType);
                checkPostStickyEventToSubscription(newSubscription, stickyEvent);
            }
        }
    }

调用subscribe方法在三个Map集合中存储,或取出订阅方法相关的数据,代码片段已做相关解释。

当多次注册同一个订阅类时,执行第22行抛出异常。

当粘性事件被接受,执行第73行的checkPostStickyEventToSubscription方法回调订阅方法,由于不是本篇文章的重点,具体细节会在EventBus的发送事件流程中介绍,敬请关注~


四,小结

EventBus的注册流程包括如下内容:

  • 遍历订阅类及其超类的订阅方法,并过滤掉超类中重复的订阅方法,将数据存储在subscriberMethods集合中
  • 将订阅方法相关的信息存储在subscriptionsByEventType,typesBySubscriber集合中
  • 配合postSticky发送的粘性事件,回调订阅方法(先发送粘性事件,后注册订阅类)

猜你喜欢

转载自blog.csdn.net/pihailailou/article/details/81842960