EventBus 源码分析 - 注解 + 反射

本文详细解析了EventBus的源码,包括getDefault()方法的DCL单例模式,register()方法中通过反射查找@Subscribe注解的方法,以及post()方法如何依据eventType执行订阅方法。EventBus利用注解和反射实现订阅发布机制,同时涉及线程模式、优先级和粘性事件的处理。此外,还介绍了unregister()方法清理相关数据。整个过程展示了EventBus设计的巧妙之处。

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

EventBus 源码解析

随着 LiveDataKotlin Flow 的出现,EventBus 已经慢慢过时了。不过 EventBus 源码的设计思想以及实现原理还是值得我们去学习的。

getDefault() 方法

EventBus().getDefault().register(this)

首先 EventBus 的创建用到了 DCL 单例模式,源码如下:

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


register() 方法第一部分

示例:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
       
        EventBus.getDefault().register(this);
    }

    /**
     * threadMode 执行的线程方式
     * priority 执行的优先级
     * sticky 粘性事件
     */
    @Subscribe(threadMode = ThreadMode.MAIN,priority = 50,sticky = true)
    public void test1(String msg){
        // 如果有一个地方用 EventBus 发送一个 String 对象,那么这个方法就会被执行
        Log.e("TAG","msg1 = "+msg);
    }
    
    @Subscribe(threadMode = ThreadMode.MAIN,priority = 100,sticky = true)
    public void test2(String msg){
        // 如果有一个地方用 EventBus 发送一个 String 对象,那么这个方法就会被执行
        Log.e("TAG","msg2 = "+msg);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        
        EventBus.getDefault().unregister(this);
    }
}

EventBus().getDefault().post("text")

register() 方法源码:

public void register(Object subscriber) {
    
    // 获取 class 对象
    Class<?> subscriberClass = subscriber.getClass();
    
    // 遍历这个 class 的所有方法,将含有 @Subscribe 注解的方法找出来
    List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
    
    synchronized (this) {
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            subscribe(subscriber, subscriberMethod);
        }
    }
}

这里的 subscriber 就是我们的 MainActivity,第一行代码很简单,就是获取 MainActivityclass 对象。

第二行代码的意思:遍历这个 class 的所有方法,将含有 @Subscribe 注解的方法找出来,并将它们封装成 List<SubscriberMethod>SubscriberMethod 的成员变量如下所示:

image.png

我们来看下 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;
    }
}

这里的 subscriberClass 就是我们的 MainActivityclass 对象。

ignoreGeneratedIndex 这个属性表示是否忽略注解成器生成的 EventBusIndex.class 文件, 我们只要知道 ignoreGeneratedIndex 默认是 false 就行了。

所以会走 findUsingInfo(subscriberClass) 方法,我们来看下源码:

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

这里我直接说结论,因为我们没有使用编译时注解生成 EventBusIndex.class 文件,所以 findState.subscriberInfo 为空,所以这里会走 findUsingReflectionInSingleClass(findState) 方法,最终就是通过反射去找。我们来看下 findUsingReflectionInSingleClass 源码:

private void findUsingReflectionInSingleClass(FindState findState) {
    Method[] methods;
    try {
        methods = findState.clazz.getDeclaredMethods();
    } catch (Throwable th) {
       ......
        }
    }
    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()));
                    }
                }
            } 
        } 
    }
}

第一行代码 findState.clazz.getDeclaredMethods(); 就是通过反射获取订阅类的所有方法。

然后遍历 methods,判断方法的修饰符是不是 public,然后获取方法参数的 class,并判断是不是只有一个参数,并且还判断是否被 @Subscribe 修饰。method.getParameterTypes() 就是获取方法的参数类型。String msg 获取到的就是 String.class,如下:

image.png

遍历完 methods 之后,就会将所获得的属性添加到 subscriberMethods 中,如下:

findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode, subscribeAnnotation.priority(), subscribeAnnotation.sticky()));

最后通过 findStatesubscriberMethods 返回回去。也就是 register() 方法中的List<SubscriberMethod>

第一部分小结

public void register(Object subscriber) {
    
    // 获取 class 对象
    Class<?> subscriberClass = subscriber.getClass();
    
    // 遍历这个 class 的所有方法,将含有 @Subscribe 注解的方法找出来
    List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
    
    synchronized (this) {
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            subscribe(subscriber, subscriberMethod);
        }
    }
}

subscriberMethodFinder.findSubscriberMethods() 方法就是通过反射去解析注册者(MainActivity)的所有方法。并且找出被 @Subscribe 注解修饰的方法,然后通过 Annotation 解析所有需要的参数(包括 threadModeprioritysticky、参数的类型 eventTypemethod)。最后把这些参数封装成 SubscriberMethod 添加到集合返回。

findSubscriberMethods 方法完成后图解.png

register() 方法第二部分

接下来我们来分析 subscribe 方法:

synchronized (this) {
    for (SubscriberMethod subscriberMethod : subscriberMethods) {
        subscribe(subscriber, subscriberMethod);
    }
}

我们先看一下 subscribe() 方法的参数,subscriber 参数就是我们的 MainActivitysubscriberMethod 参数是我们上面对方法的封装。

我们来看一下 subscribe() 方法的源码:

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
    // eventType 是方法参数的 class,也就是 String.class
    Class<?> eventType = subscriberMethod.eventType;
    
    // 又封装了一个 Subscription 对象 subscriber : MainActivity, subscriberMethod : 解析好的方法
    Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
    
    CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
    
    // 第一次拿为空,put
    if (subscriptions == null) {
        subscriptions = new CopyOnWriteArrayList<>();
        subscriptionsByEventType.put(eventType, subscriptions);
    } else {
        ......
    }

    // 优先级排序 
    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);

    // 粘性事件的处理
    ```
}

这里又封装了一个 Subscription 对象,成员变量就是:

  • subscriber 参数就是我们的 MainActivity
  • subscriberMethod 参数是我们上面对方法的封装

接下来用 subscriptionsByEventTypeSubscription 对象存起来,subscriptionsByEventType 是个 Map,如下:

private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;

  • key : Class<?>(参数类型,也就是 Strin.class)
  • value : CopyOnWriteArrayList<Subscription>(线程安全的 ArrayList,里面是 Subscription)

subscriptionsByEventType.get(eventType) 第一次拿肯定为空,所以会调用 subscriptionsByEventType.put(eventType, subscriptions)。到这里,subscriber 方法就分析好了。

第二部分小结

解析所有的 subscriberMethodeventType,然后将它们封装成 Map<Class<?>, CopyOnWriteArrayList<Subscription>> 类型的 subscriptionsByEventTypekeyeventTypevalueSubscription 的列表,Subscription 包含两个属性 subscribersubscriberMethod

subscribe 完成后图解.png

post() 方法

其实根据上面那个图,我们很容易猜出 post() 方法肯定会遍历 subscriptionsByEventType,找到一样的 eventType,然后去执行 CopyOnWriteArrayList 列表里面的 Subscription 对象里面的 subscriberMethod 对象里面的 method

我们来看下 post() 方法源码:(经过一系列调用)

private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {

    CopyOnWriteArrayList<Subscription> subscriptions;
    
    synchronized (this) {
        subscriptions = subscriptionsByEventType.get(eventClass);
    }
    
    if (subscriptions != null && !subscriptions.isEmpty()) {
        for (Subscription subscription : subscriptions) {
            postingState.event = event;
            postingState.subscription = subscription;
            boolean aborted;
            try {
                postToSubscription(subscription, event, postingState.isMainThread);
                aborted = postingState.canceled;
            } finally {
                postingState.event = null;
                postingState.subscription = null;
                postingState.canceled = false;
            }
            if (aborted) {
                break;
            }
        }
        return true;
    }
    return false;
}

这里的 event 就是 post("text") 里面的 text,所以 eventClass 就是 String.class,然后执行 subscriptions = subscriptionsByEventType.get(eventClass),就会将我们上面图片中的 value 取出来。然后遍历 subscriptions,执行 postToSubscription(subscription, event, postingState.isMainThread) 方法,我们看下这个方法的源码:

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

我们的 threadMode 有四种形式:

  • POSTING:同一个线程,在哪个线程发送,就在哪个线程执行。
  • MAIN:在主线中执行。
  • BACKGROUND:子线程,如果发送事件是主线程,那么久调用线程池切换线程来执行方法。
  • ASYNC:异步线程,无论发布时间是主线程还是子线程,都有通过线程池切换线程执行。

所以我们看到源码中,如果是 POSTING 方式就直接调用 invokeSubscriber(subscription, event) 执行方法。其他方式就可能需要调用线程池切换线程来执行订阅方法。

invokeSubscriber 源码如下:

void invokeSubscriber(Subscription subscription, Object event) {
    try {
      //  subscriber 是 MainActivity,event 是 post 发送的 "text"
      subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
    } catch (InvocationTargetException e) {
        handleSubscriberException(subscription, event, e.getCause());
    } catch (IllegalAccessException e) {
        throw new IllegalStateException("Unexpected exception", e);
    }
}

post() 方法小结

post() 方法会将 post(Object event) 里的 eventclass 对象作为 key,去 subscriptionsByEventType 这个 map 里面取出 keyeventclass 相同的 subscriptions,然后遍历 subscriptions去执行订阅方法。

unRegister()

unRegister() 方法就是将上面所产生的那些 map 清空

总结

EventBus 执行订阅方法的原理主要是通过注解反射。细节上用的的知识还是非常多的,比如 DCL 单例模式Builder 设计模式享元设计模式FindState 的对象池设计)、线程池HandlerThreadLocal 等。

并且还引入了注解处理器,使用这种方式可以不需要用到反射来查找订阅方法,优化了运行时性能。不过本文的源码分析分析的还是利用反射来查找订阅方法。

通过阅读 EventBus 源码,可以看出作者很多地方的精彩设计,很值得我们学习。本文只是分析了核心的 EventBus 源码,很多细节上的源码就直接跳过去了。

文末

我总结了一些Android核心知识点,以及一些最新的大厂面试题、知识脑图和视频资料解析。

需要的小伙伴直接点击文末小卡片免费领取哦,以后的路也希望我们能一起走下去。(谢谢大家一直以来的支持,需要的自己领取)

Android学习PDF+架构视频+面试文档+源码笔记

部分资料一览:

  • 330页PDF Android学习核心笔记(内含8大板块)

  • Android学习的系统对应视频

  • Android进阶的系统对应学习资料

  • Android BAT大厂面试题(有解析)

领取地址:

在这里插入图片描述

<think>嗯,用户想了解EventBus源码中使用的设计模式。首先,我需要回忆一下EventBus的基本功能和结构。EventBus是一个用于AndroidJava的事件发布-订阅框架,主要用于组件间的通信,解耦发布者和订阅者。根据之前的知识,设计模式方面,观察者模式应该是核心,但可能还有其他模式。 用户提到了参考的引用内容,其中引用[3]提到EventBus是观察者模式的优雅实现,而引用[4]讨论了同步处理sticky事件时使用了synchronized关键字,可能涉及线程相关的模式。另外,源码解析的引用可能提到其他结构,比如建造者模式或者单例模式? 首先,观察者模式是肯定有的。EventBus中,订阅者注册自己关注的事件,当事件发布时,所有相关的订阅者会被通知。这符合观察者模式的定义。但具体实现可能有差异,比如使用注解处理器或者反射来动态注册订阅方法。 然后,可能用到了发布-订阅模式,不过发布-订阅和观察者模式有时会被视为同一模式的不同变体。需要确认两者是否有区别。发布-订阅通常有一个中间的事件总线,而观察者是直接联系主题和观察者,所以EventBus更接近发布-订阅模式,但通常还是称为观察者模式。 接下来,引用[4]提到sticky事件的处理,这里用了synchronized来保证线程安全。这可能涉及到线程池或者生产者-消费者模式,因为事件可能在后台线程处理,而订阅者可能在主线程或其他线程执行。不过生产者-消费者通常用队列实现缓冲,而EventBus是否使用这样的结构呢?需要回忆源码结构。EventBus的post方法可能将事件放入队列,然后分发给订阅者,这可能涉及生产者-消费者模式。 另外,建造者模式可能在创建EventBus实例时使用,比如通过EventBus.builder()来配置参数,这样用户可以通过不同的配置创建不同的实例,符合建造者模式的应用场景。例如,设置是否启用事件继承、是否发送NoSubscriberEvent等选项。 还有可能用到了单例模式,比如默认的EventBus实例通过EventBus.getDefault()获取,这通常是一个单例。但用户也可以创建自己的实例,所以这里的单例模式可能仅用于默认情况。 责任链模式可能在事件分发过程中使用吗?例如,事件的处理可能有多个步骤,或者不同订阅者处理事件的不同部分。不过EventBus通常是直接将事件分发给所有订阅者,所以可能不太符合责任链模式。或者可能在事件传递时,比如子事件传递给父类事件的处理,但可能用到了其他机制,如事件继承。 装饰者模式是否被使用?比如对订阅者的方法进行包装,添加额外的功能,如线程切换。例如,使用@Subscribe(threadMode = ThreadMode.MAIN)来指定方法在主线程执行,这里可能在内部装饰了订阅者的方法调用,以适应不同的线程模式。 策略模式可能用于不同的线程模式处理,比如根据不同的ThreadMode选择不同的执行策略,如立即执行、后台线程、主线程等。这符合策略模式的定义,将算法族封装,使得它们可以互相替换。 总结起来,主要的设计模式可能包括: 1. 观察者模式(发布-订阅模式) 2. 建造者模式(用于配置EventBus实例) 3. 单例模式(默认实例) 4. 策略模式(处理不同线程模式) 5. 装饰者模式(包装订阅方法) 6. 生产者-消费者模式(处理异步事件) 需要结合源码具体分析。例如,EventBusBuilder类使用建造者模式,允许链式调用配置方法。ThreadMode的不同实现可能使用策略模式,每个模式对应不同的执行策略。订阅者的注册和事件分发过程使用观察者模式。异步事件处理可能使用线程池,属于生产者-消费者模式。 另外,可能还有模板方法模式,比如在事件处理流程中定义骨架,具体步骤由子类实现,但不确定是否有这种情况。 需要确保回答中引用的设计模式在EventBus源码中确实存在,并且给出具体的例子。比如,EventBus.getDefault()是单例,EventBusBuilder是建造者模式,ThreadMode是策略模式,sticky事件处理中的synchronized属于线程安全设计,可能涉及同步模式,但不属于经典的设计模式。 可能还需要考虑其他模式,比如外观模式,如果EventBus提供了一个简化的接口来访问复杂的子系统,但可能不明显。 综上所述,主要的设计模式是观察者模式、建造者模式、策略模式和单例模式。需要具体说明每个模式在源码中的体现,并引用提供的参考资料中的内容来支持。</think>### EventBus源码中的设计模式分析 EventBus源码中主要运用了以下几种设计模式,以下是具体分析: --- #### 1. **观察者模式(发布-订阅模式)** **核心实现**: EventBus通过`register()`方法注册订阅者,订阅者通过`@Subscribe`注解标记事件处理方法。当调用`post(event)`时,所有匹配的订阅方法会被调用。 **源码体现**: - 订阅者注册时,通过反射注解处理器解析`@Subscribe`方法,建立**事件类型-订阅方法**的映射表[^3]。 - 事件分发时,遍历映射表并调用订阅方法,实现观察者模式的核心逻辑[^4]。 --- #### 2. **建造者模式(Builder Pattern)** **用途**:灵活配置EventBus实例。 **源码体现**: - `EventBusBuilder`类提供链式调用方法(如`eventInheritance()`、`throwSubscriberException()`),允许用户自定义EventBus行为。 - 示例: ```java EventBus eventBus = EventBus.builder() .eventInheritance(false) .logSubscriberExceptions(true) .build(); ``` --- #### 3. **策略模式(Strategy Pattern)** **用途**:支持不同线程模式下的事件处理策略。 **源码体现**: - `ThreadMode`枚举定义了四种策略(`POSTING`、`MAIN`、`BACKGROUND`、`ASYNC`),根据订阅方法注解中的`threadMode`值,选择对应的执行策略。 - 例如,`MAIN`模式通过`MainThreadSupport`将事件分发到主线程队列。 --- #### 4. **单例模式(Singleton Pattern)** **用途**:提供默认的全局EventBus实例。 **源码体现**: - 通过`EventBus.getDefault()`获取单例实例,内部使用双重检查锁保证线程安全: ```java public static EventBus getDefault() { if (defaultInstance == null) { synchronized (EventBus.class) { if (defaultInstance == null) { defaultInstance = new EventBus(); } } } return defaultInstance; } ``` --- #### 5. **生产者-消费者模式(异步事件处理)** **用途**:异步事件的分发与处理。 **源码体现**: - 当使用`ThreadMode.ASYNC`时,事件会被提交到线程池队列,由后台线程消费并执行订阅方法。 --- #### 6. **装饰者模式(线程切换逻辑)** **用途**:包装订阅方法以实现线程切换。 **源码体现**: - 在`HandlerPoster`和`BackgroundPoster`类中,通过封装`Runnable`任务,将事件分发到主线程或后台线程。 --- ### 关键设计细节 - **线程安全**:对`stickyEvents`等共享资源使用`synchronized`关键字,避免多线程竞争。 - **事件继承**:支持父类事件触发子类订阅方法(需配置`eventInheritance`参数)[^1]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值