EventBus详解

EventBus详解

Android EventBus 的原理基于观察者模式,它允许不同组件之间的通信,而无需这些组件之间有直接的依赖关系。以下是一个典型的 EventBus 库(如 GreenRobot 的 EventBus)的工作原理:

核心组件:

  1. 事件(Event)
    • 任何对象都可以作为事件,通常是一个简单的 POJO(Plain Old Java Object)或者包含数据的类。
  2. 发布者(Poster)
    • 发送事件的组件或对象。
  3. 订阅者(Subscriber)
    • 接收并处理事件的组件或对象。
  4. EventBus 实例
    • 负责管理事件的注册、发布和分发。

工作流程:

  1. 注册
    • 订阅者通过调用 EventBus.register() 方法注册自己。
    • EventBus 使用反射机制查找订阅者类中所有使用 @Subscribe 注解的方法。
    • EventBus 维护一个订阅者列表,列表中包含订阅者和它们订阅的事件类型。
  2. 发布事件
    • 发布者通过调用 EventBus.post() 方法发布一个事件。
    • EventBus 接收到事件后,查找订阅了这个事件类型的所有订阅者。
  3. 事件分发
    • EventBus 根据事件类型,从订阅者列表中找到对应的方法,并将事件作为参数传递给这些方法。
    • 事件分发的线程模式可以是:
      • POSTING:在发布事件的线程中直接调用订阅者方法。
      • MAIN:在主线程(UI线程)中调用订阅者方法,适用于更新UI。
      • BACKGROUND:如果发布事件是在主线程,则在后台线程中调用订阅者方法;如果发布事件是在后台线程,则直接在该线程中调用。
      • ASYNC:无论事件在哪个线程发布,都在一个单独的线程中调用订阅者方法。
  4. 注销
    • 订阅者不再需要接收事件时,应该通过调用 EventBus.unregister() 方法注销自己。
    • EventBus 从订阅者列表中移除该订阅者,防止内存泄漏。

原理:

  • 反射:EventBus 使用 Java 反射 API 来在运行时查找订阅者的订阅方法。
  • 注解@Subscribe 注解用于标记订阅者方法,这些方法会在事件发布时被调用。
  • 线程管理:EventBus 内部使用了线程池和 Handler 来管理不同线程模式下的方法调用。
  • 事件优先级:订阅者可以设置优先级,EventBus 会根据优先级顺序调用订阅者方法。
  • 事件粘性:某些事件可以被标记为“粘性的”,这意味着它们在发布后可以被新注册的订阅者接收。

原理深入(三张HashMap表):

  • 订阅者和订阅事件
EventBus.getDefault().register(this);

@Subscribe(threadMode = ThreadMode.MAIN)
    public void onEvent1(Event1 event) {
}
@Subscribe(threadMode = ThreadMode.MAIN)
    public void onEvent2(Event2 event) {
}

如上代码,注册监听的是activity,称为subscriber,在activity中监听了Event1和Event2两个事件,当其他地方执行post#Event1事件的时候,为什么我们能接收到呢?

private final Map<Object, List<Class<?>>> typesBySubscriber;

因为一个Subscribe对应多个Event,Subsribe就是通过register方法注册的对象,比如activity或Fragment,EventBus会在对象register时,使用反射机制,遍历对象的方法,将带有@Subscribe标签并且合法的方法加入到typesBySubscriber。typesBySubscriber是HashMap形式,key是注册的对象本身,由于一个注册的对象中可能有多个监听事件,所以value是用list保存的Event。当然,在反注册的时候,会通过Subsribe来查找到其中所有额event进行反注册。

  • 订阅事件和订阅者
    因为一个Event可能会有被多个subsribe订阅,所以有当执行post(Event)的时候会查找到所有订阅了Event事件的subscribe并调用其中的Event方法。
Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType
  • Sticky粘性事件是怎么发送的
    为什么先执行postSticky,后执行register还是可以监听到粘性Event事件呢?看下代码吧
public void postSticky(Object event) {
        synchronized (stickyEvents) {
            stickyEvents.put(event.getClass(), event);
        }
        // Should be posted after it is putted, in case the subscriber wants to remove immediately
        post(event);
    }

是不是一目了然了,原来执行postSticky的时候会将event.getclass和event保存起来

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

代码就不解释了

优点与缺点:

  • 优点
    • 简化组件间的通信。
    • 减少代码耦合。
    • 易于实现复杂的事件传递逻辑。
  • 缺点
    • 反射可能导致性能问题。
    • 事件流难以追踪,特别是在大型应用中。
    • 过度使用可能导致代码结构混乱。
      EventBus 在小型或中等规模的应用中非常有用,但在大型应用中,过度依赖 EventBus 可能会导致维护困难。此外,Android Architecture Components(如 LiveData 和 ViewModel)提供了更现代的组件间通信方式,值得考虑。
  • 10
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值