EventBus3.0源码解析-02

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

EventBus3.0源码解析-01这篇文章简单的介绍了EventBus的使用流程,我们知道EventBus有三个主要入口方法,分别为

  • EventBus.getDefault().register(Object);
    
  • EventBus.getDefault().unregister(Object);
    
  • EventBus.getDefault().post(Object);
    

本文将通过register入口方法及其逻辑,分析EventBus源码。

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

register函数主要实现了两个功能,1、将订阅类中所有订阅方法收集;2、按一定规则保存收集的信息。

订阅方法收集

订阅方法收集主要由SubscriberMethodFinder类的findSubscriberMethods函数实现,我们看下其具体实现:

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、如果参数中的subscriberClass类之前注册过,那么直接从METHOD_CACHE缓存中获取信息返回。

2、根据ignoreGeneratedIndex判断是否要根据注解处理器在编译期生成的索引文件来寻找订阅方法信息,该方式是从EventBus3.0中引入的,目的是为了提高EventBus的整体效率。如果ignoreGeneratedIndex为true,则使用反射来寻找订阅方法信息,否则,先根据生成的索引文件来寻找订阅方法信息,索引文件中找不到,再使用反射寻找。

3、如果找到的订阅方法信息非空,则将这些信息缓存在METHOD_CACHE中。

第1、3步没有什么好讲的内容,主要的内容在第2步。由于根据索引文件寻找订阅方法信息的方式还涉及注解处理器,稍微复杂,所以会单独开一篇文章讲这块。本文主要介绍通过反射来寻找订阅方法信息的方式。

我们来看一下findUsingReflection的具体实现:

private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
    FindState findState = prepareFindState();
    findState.initForSubscriber(subscriberClass);
    // 遍历订阅类以及其父类
    while (findState.clazz != null) {
        findUsingReflectionInSingleClass(findState);
        findState.moveToSuperclass();
    }
    return getMethodsAndRelease(findState);
}
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();
}

prepareFindState()方法会尝试从缓存中寻找是否有现存的FindState对象,如果有,则直接使用缓存的FindState对象,否则,直接new一个FindState对象。

void initForSubscriber(Class<?> subscriberClass) {
	// 保存订阅类信息
    this.subscriberClass = clazz = subscriberClass;
    // 是否忽略父类
    skipSuperClasses = false;
    subscriberInfo = null;
}

initForSubscriber方法就是简单的初始化了一些参数。

接下来我们看下核心方法findUsingReflectionInSingleClass的实现:

private void findUsingReflectionInSingleClass(FindState findState) {
    Method[] methods;
    try {
        // getDeclaredMethods()方法的效率要优于getMethods()
        methods = findState.clazz.getDeclaredMethods();
    } catch (Throwable th) {
        methods = findState.clazz.getMethods();
        findState.skipSuperClasses = true;
    }
    for (Method method : methods) {
        // 获取方法的修饰符, 比如public, static等修饰符
        int modifiers = method.getModifiers();
        if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) 			{
        	// 获取方法的参数信息
            Class<?>[] parameterTypes = method.getParameterTypes();
            // 方法只有一个参数
            if (parameterTypes.length == 1) {
                // 获取方法的@Subscribe注解信息
                Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                // 如果方法有@Subscribe注解信息
                if (subscribeAnnotation != null) {
                    Class<?> eventType = parameterTypes[0];
                    // 检查当前method是否是有效的订阅方法
                    if (findState.checkAdd(method, eventType)) {
                        // 如果当前method是有效的订阅方法,则把该订阅方法收集起来
                        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");
        }
    }
}

findUsingReflectionInSingleClass方法加了详细的注解,大多数逻辑都能直接看代码注释弄明白。这里主要要介绍的是findState.checkAdd方法,checkAdd的实现如下:

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

考虑到同一个类里面通常只会有一个方法监听同一类事件,为了效率,所以EventBus先根据事件类型去匹配同一个类中是否已经有添加过的方法监听同一类事件,如果没有,则直接认为该方法为有效订阅方法,如果之前已经添加过方法监听同一类事件,则通过checkAddWithMethodSignature来判断该方法是否为有效订阅方法。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);
    // 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;
    }
}

根据一定规则计算出方法的唯一特征名称,如果具有该特征名称的方法之前还没有添加过,则认为当前方法为有效订阅方法。否则,如果之前添加过的方法所在的类是当前方法所在类的父类或者接口,考虑到子类重写,认为当前方法为有效订阅方法。否则认为当前方法为非有效订阅方法。

通过上面的多次调用findUsingReflectionInSingleClass方法,我们已经把有效的订阅信息收集到了findState的subscriberMethods变量中。

保存所有订阅信息

我们再回到findUsingReflection方法,接着会调用getMethodsAndRelease(findState)方法,我们看看它的实现:

private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {
    List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods);
    findState.recycle();
    synchronized (FIND_STATE_POOL) {
        for (int i = 0; i < POOL_SIZE; i++) {
            if (FIND_STATE_POOL[i] == null) {
                FIND_STATE_POOL[i] = findState;
                break;
            }
        }
    }
    return subscriberMethods;
}

它其实做的事情就是把findState放入到缓存中,然后返回刚刚收集到的有效订阅信息。

现在我们回到register方法,我们收集到了有效的订阅信息,接着会循环调用subscribe(subscriber, subscriberMethod)方法,我们看看这个方法的实现:

// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
    Class<?> eventType = subscriberMethod.eventType;
    Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
    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);
        }
    }
}

这个函数的代码虽然很长,但是其只做了三件事情,1、将订阅信息以事件类型为key保存在subscriptionsByEventType变量中(方便post方法调用的时候,以事件为key寻找订阅信息);2、将事件类型以订阅类为key保存在typesBySubscriber变量中(方便unregister方法调用的时候,以订阅类为key寻找事件类型,然后根据subscriptionsByEventType变量找到订阅信息);3、处理sticky事件逻辑。关于第3点,考虑到本文篇幅,我将另外开一篇文章进行分析。

总结

所以通过调用register方法,最终订阅方法信息会被最终保存在subscriptionsByEventType变量和typesBySubscriber变量中。

如果你喜欢,感谢给个赞。如果有什么问题,欢迎评论。

猜你喜欢

转载自blog.csdn.net/xiayong1/article/details/86144091
今日推荐