EventBus源码解析一

本文详细剖析了EventBus的注册过程,包括如何通过反射或预生成的索引查找订阅者方法,以及如何根据事件类型和优先级进行订阅。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在这一篇文章中,我们会介绍在调用 EventBus.getDefault().register(this); 之后都发生了什么
首先介绍一下各种名词的含义:

  • subscriber 类的成员函数有用 @Subscribe() 注解修饰的都能称为subscriber(订阅者)
  • Subscription 封装subscribersubscriberMethod的实体类
    然后介绍一下EventBus中比较关键的几个Map类型的属性:
    private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;//Key为Event(事件实体),Value为封装好的Subscription(订阅者)集合
    private final Map<Object, List<Class<?>>> typesBySubscriber;//Key为Subscriber(订阅者),Value为订阅者感兴趣的Event(事件)集合
    private final Map<Class<?>, Object> stickyEvents;//置顶事件集合,Key为Event实体类,Value为具体Event对象

register过程

//EventBus.class
    /**
     * Registers the given subscriber to receive events. Subscribers must call {@link #unregister(Object)} once they
     * are no longer interested in receiving events.
     * 
     * Subscribers have event handling methods that must be annotated by {@link Subscribe}.
     * The {@link Subscribe} annotation also allows configuration like {@link
     * ThreadMode} and priority.
     */
    public void register(Object subscriber) {
        Class subscriberClass = subscriber.getClass();
        List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        synchronized (this) {
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);
            }
        }
    }

调用register()方法需要传入订阅者;这个订阅者可以为调用者本身,也可以为其他订阅者
register() 方法内部会调用 SubscriberMethodFinder.findSubscriberMethods(Class subscriberClass) 方法来获取订阅者内部可以接收订阅事件的方法,方法实现如下:

//SubscriberMethodFinder.class
List findSubscriberMethods(Class subscriberClass) {
        List 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;
        }
}

注意到作者使用了缓存机制METHOD_CACHE,变量声明如下:private static final Map, List> METHOD_CACHE = new ConcurrentHashMap<>();可见作者还考虑了多线程的情况,使用这个缓存机制可以减少重复获取订阅者处理事件的方法,提高运行效率
关于ignoreGeneratedIndex就不得不提一下在 EventBus高级使用姿势 中提到的EventBus 3.X新特性Subscriber Index,如果看过上篇文章,那么理解这个变量的含义就不困难了,ignoreGeneratedIndex的默认值为false,也就是默认采用生成的index获取订阅者的方法。
这里我们首先分析 findUsingReflection(Class subscriberClass) 方法,看名字就能猜出来是采用反射方法获取订阅者方法,具体实现上源码:

    private List findUsingReflection(Class subscriberClass) {
        FindState findState = prepareFindState();
        findState.initForSubscriber(subscriberClass);
        while (findState.clazz != null) {
            findUsingReflectionInSingleClass(findState);
            findState.moveToSuperclass();
        }
        return getMethodsAndRelease(findState);
    }

很简短的一段代码,在代码中我们遇到了新的朋友FindState,这个类是SubscriberMethodFinder的内部类,具体内容后面再讲,注意关键代码是while循环,首先是用反射方法获取订阅者方法,然后转移至父类,获取父类事件处理函数,一直到java或Android提供的系统类,完成对订阅者方法的遍历,找出所有的订阅方法
现在看一下FindState类中的属性

        final List subscriberMethods = new ArrayList<>();
        final Map anyMethodByEventType = new HashMap<>();
        final Map subscriberClassByMethodKey = new HashMap<>();
        final StringBuilder methodKeyBuilder = new StringBuilder(128);

        Class subscriberClass;
        Class clazz;
        boolean skipSuperClasses;
        SubscriberInfo subscriberInfo;

这里主要说明一下subscriberClassclazz,其他见名思意(作者这点做的很不错),subscriberClass代表的是订阅者本身,也就是register函数的参数,自始至终不会变;clazz会在遍历方法过程中变化,比如目前在遍历父类方法,clazz变量就指向父类
回到findSubscriberMethods()函数,除了findUsingReflection()方法还有findUsingInfo()方法,现在我们分析一下findUsingInfo()方法,先上函数实现:

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

这里使用了FindState中的属性SubscriberInfo,注意这是一个接口,具体实现在Subscriber Index过程中实现的,第一次调用getSubscriberInfo()方法会从build过程中生成的index中取得subscriberInfo,然后每调用一次getSubscriberInfo()方法都会去的父类的subscriberInfo,这点需要注意一下,感兴趣的可以看subscriberInfo()代码,可以发现如果subscriberInfo为空的话,会采用反射方法获取订阅者方法,这和我们前一篇文章中介绍的特性一致
这里需要特别说明一下findState设计的巧妙之处:在SubscriberMethodFinder内部有一个属性FIND_STATE_POOL,这是一个大小为4的FindState数组:

  1. 在prepareFindState()阶段,会从FIND_STATE_POOL中取出不为空的FindState对象,然后将对应位置置空,返回FindState对象,这样可以避免多线程读写冲突以及读写不一致情况发生
  2. 在getMethodsAndRelease()阶段,会遍历FIND_STATE_POOL,找到为空的位置将使用完的FindState对象放进去,可以在下一次的prepareFindState()阶段复用,这样可以减少实例化对象所产生的额外消耗

回到register()方法中,既然我们拿到了订阅者内部的订阅函数,下一步应该是注册这些函数到EventBus中,别慌,我们可以发现会对得到的subscriberMethods列表循环调用subscribe()方法,那么这个方法具体是做什么的呢,我猜应该是订阅函数,嗯看名字就知道,下面我们来分析分析:

    // Must be called in synchronized block
    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        Class eventType = subscriberMethod.eventType;
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
        CopyOnWriteArrayList subscriptions = subscriptionsByEventType.get(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);
    //置顶事件,在注册EventBus的时候就会发送事件到订阅者的时间处理函数中
        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).
                Set<Map.Entry<Class<?>, Object>> = stickyEvents.entrySet();
                for (Map.Entry, 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);
            }
        }
    }

首先是根据事件类型取得同类型事件的订阅者集合,然后根据事件处理方法的优先级确定插入位置,然后根据订阅者类型插入订阅者事件集合,最后就是处理置顶之间,在源码中可以发现:置顶事件的发送是在注册EventBus阶段

至此,注册EventBus过程分析完了,如有不对的地方,欢迎在评论中指正

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值