EventBus源码解析

本文详细解析了EventBus的源码,包括`EventBus.getDefault().register(Object subscriber)`的订阅过程,`EventBus.getDefault().post(Object event)`的事件发布,以及`EventBus.getDefault().unregister(Object subscriber)`的解绑操作。讲解了EventBus如何通过注解找到事件接收方法,注册时创建订阅者列表,发布事件时按线程模式执行,以及解绑时清理订阅者信息。EventBus本质上是一个维护事件处理方法映射的单例类。

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

本文参考了 Dotry(https://www.jianshu.com/p/6da5abfec1da)的博客,如有侵权请通知删除。

EventBus出来已经好几年了,但是一直想自己看看都没有时间,最近看了好久别人的博客写的EventBus的源码解析,但是我自身还没有查看源码去了解其中的原理,所以今天我就去下载我项目中用到的org.greenrobot:eventbus:3.1.1官方下载源码去看看,它其中实现的整个流程和原理。

先给出EventBus的
官方网址http://greenrobot.org/eventbus/
项目地址https://github.com/greenrobot/EventBus#add-eventbus-to-your-project
官方API文档(3.0.0版本的):http://greenrobot.org/files/eventbus/javadoc/3.0/
具体什么概念,五种线程执行类型什么的,就不CV了,我就只针对注册和解绑,调用以及接收四个方法进行剖析。

一、EventBus.getDefault().register(Object subscriber)以及通过注解来获取类中的事件接收方法,并且进行绑定
首先看看EventBus.getDefault()。
官方下载的release版本代码是这样的
在这里插入图片描述
我用gradle插件下载下来的Ctrl点进去是这样的
在这里插入图片描述
都是双重校检锁,防止多个线程同时首次访问,首个进去创建了新对象了出来之后,后面的紧跟着进去继续创建对象,导致浪费内存甚至发生其他问题。
唯一的区别就是一个给定义了对象,另一个是直接获取EventBus类中的静态构造对象。
我挺纳闷的是为什么我从gradle上配置的EventBus版本3.1.1下来的代码不是release版本的。。。
接着就是初始化构造一个传一个参数
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
再接着就是用构造器模式,将EventBusBuilder的属性和对象赋给EventBus中的对象,完成EventBus的初始化。

其次接下来就是register(Object subscriber)方法了,代码中已经加入了备注
在这里插入图片描述

// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
    //获取方法中的参数类型类
    Class<?> eventType = subscriberMethod.eventType;
    //放入上下文和方法的存储对象
    Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
    //因为同一个EventBus可能被多个Activity注册,所以先获取一下Subscription,看看是否之前已经存在了此对象
    CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
    if (subscriptions == null) {//如果不存在,则存入
        subscriptions = new CopyOnWriteArrayList<>();
        //存入方式是eventType是处理事件的参数类型(key),subscriptions是上下文和方法的对象(value)
        subscriptionsByEventType.put(eventType, subscriptions);
    } else {
        //如果获取的Subscription已经包含了刚才刚刚注册的Subscription,那么就抛出异常信息
        if (subscriptions.contains(newSubscription)) {
            throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
                    + eventType);
        }
    }
    //拿到Subscription的集合大小
    int size = subscriptions.size();
    //遍历
    for (int i = 0; i <= size; i++) {
        //如果说最后的Subscription对象还没被存入,但是已经被注册了,那就将它存入集合当中
        // 此处应用于粘性事件,当仅仅发送了事件,但是还没有注册的时候,就会在注册后进行接受处理。
        if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
            subscriptions.add(i, newSubscription);
            break;
        }
    }

    //创建获取该上下文内的事件处理数
    List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
    //如果为空
    if (subscribedEvents == null) {
        //并put进去subscribedEvents中
        subscribedEvents = new ArrayList<>();
        typesBySubscriber.put(subscriber, subscribedEvents);
    }
    //并且放入subscribedEvents中
    subscribedEvents.add(eventType);

    //方法数不为0则进入
    if (subscriberMethod.sticky) {
        //默认为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>).
            //下面的就是逐个进行参数判断来进行不同线程的操作和不同参数进行不同onEvent方法的处理
            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);
        }
    }
}

划重点,必须加锁执行,否则里面的集合对象就乱完了。

在这里插入图片描述

private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
        switch (subscription.subscriberMethod.threadMode) {
            case POSTING:
                invokeSubscriber(subscription, event);
                break;
            case MAIN:
                if (isMainThread) {
                    invokeSubscriber(subscription, event);
                } else {
                    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);
        }
    }

到这里大概就结束了,剩下的就是设置subscription.subscriberMethod.threadMode的不同类型下不同的处理方式,对象为null的创建对象,不为null的直接调用,大概这个样子。

二、EventBus.getDefault().post(Object event);
在这里插入图片描述
下面这个是上面调用的方法,这里就是调用事件处理方法的入口是遍历寻找获取对应的接收事件方法,并且该处理方法所在的上下文是否注册绑定了EventBus,方法是否有注解,是否有注解异常,如果都是的话就会再次post一个NoSubscriberEvent的event。
在这里插入图片描述
上个方法中有一个subscriptionFound对象,它就是寻找对应处理方法的返回值。

在这里插入图片描述
到这里就结束了整个post过程的操作。最后再贴一下事件最终处理的方法。

private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
        switch (subscription.subscriberMethod.threadMode) {
            case POSTING:
                invokeSubscriber(subscription, event);
                break;
            case MAIN:
                if (isMainThread) {
                    invokeSubscriber(subscription, event);
                } else {
                    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);
        }
    }

三、EventBus.getDefault().unregister(Object subscriber)

在这里插入图片描述
到这里就马上结束了,坚持一下下
在这里插入图片描述
不多说,到这里我觉得前面的注册理解的话,后面这个反注册就是小儿科。

最最最后,很尴尬,起初是想从四方面来说的,注册,反注册,发送事件,接收事件。但是后来慢慢的看了源码后发现,这只能是从三个方面进行探索,因为当EventBus.getDefault().register(Object subscriber)的时候,EventBus会自动地查找到该类中的带有Subscribe注解的接收方法,来进行绑定。所以还不能单独拿出来说,只能和注册放一起说。

总结
简单来说就是,EventBus它就是一个单例类,里面放了一个Map,map的键是发送和接收参数类型,值是上下文和接收事件方法的bean。当注册的时候就会给map中添加一个entry,当反注册的时候就会remove那个Class的entry。post执行的时候会根据参数类型在map中寻找对应的entry,再进入对应的方法,根据注解来执行对应的线程和类型。完事儿~

最近看了一句话分享一下~

与其临渊羡鱼,不如退而结网。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值