深入剖析 Android 开源库 EventBus 的源码详解

news2024/9/22 11:42:30

文章目录

  • 前言
  • 一、EventBus 简介
    • EventBus 三要素
    • EventBus 线程模型
  • 二、EventBus 使用
    • 1.添加依赖
    • 2.EventBus 基本使用
      • 2.1 定义事件类
      • 2.2 注册 EventBus
      • 2.3 EventBus 发起通知
  • 三、EventBus 源码详解
    • 1.Subscribe 注解
    • 2.注册事件订阅方法
      • 2.1 EventBus 实例
      • 2.2 EventBus 注册
        • 2.2.1 SubscriberMethodFinder#findSubscriberMethods()
          • 2.2.1.1 SubscriberMethodFinder#findUsingInfo()
          • 2.2.1.2 SubscriberMethodFinder#findUsingReflectionInSingleClass()
          • 2.2.1.3 SubscriberMethodFinder#FindState#checkAdd()
        • 2.2.2 EventBus#subscribe()
    • 3.EventBus 取消注册
      • 3.1 EventBus#unregister()
      • 3.2 EventBus#unsubscribeByEventType()
    • 4.EventBus 发布、处理事件
      • 4.1 EventBus#post()
      • 4.2 EventBus#postSingleEvent()
      • 4.3 EventBus#postSingleEventForEventType()
      • 4.4 EventBus#postToSubscription()
        • 4.4.1 EventBus#invokeSubscriber()
        • 4.4.2 HandlerPoster#enqueue()
        • 4.4.3 EventBus#invokeSubscriber()
        • 4.4.4 BackgroundPoster#enqueue()
        • 4.4.5 AsyncPoster#enqueue()
      • **EventBus** 发布事件(包括粘性事件)及处理流程
    • 5.EventBus 粘性事件
      • 5.1 EventBus#postSticky()
      • 5.2 EventBus#subscribe()
      • 5.3 EventBus#checkPostStickyEventToSubscription()
    • 6.EventBus 之 Subscriber Index
      • 6.1 EventBusIndex
      • 6.2 EventBusBuilder#addIndex()
      • 6.2 EventBusBuilder#installDefaultEventBus()
      • 6.3 SubscriberMethodFinder#findUsingInfo()
        • 6.3.1 SubscriberMethodFinder#getSubscriberInfo()
        • 6.3.2 SimpleSubscriberInfo#getSubscriberMethods()
      • 小结
  • 总结
  • 参考


前言

Android 项目开发的时候,经常会遇到组件与组件之间、组件与后台线程之间的通信, 比如:子线程中进行数据请求,请求数据成功后,通过 HandlerRxJava 等来通知 UI 主线程进行更新操作;两个 Fragment 之间可以通过 Listener 进行通信,简单的通信通过上述的技术手段也是可以满足需求的,但随着项目的不断迭代更新,程序越来越庞大时,就会要写很多的代码,从而导致代码严重的耦合问题。为了优化该问题,EventBus 事件总线应运而生。


一、EventBus 简介

EventBus 事件总线,是一款由 GreenRobot 开源的在 Android 开发中使用的发布/订阅事件总线框架,基于观察者模式,将事件的接收者和发送者分开解耦。用来替代广播 BroadCastReceiverstartActivityForResultHandler异步回调等来实现各组件间、组件与后台线程间的通信。

EventBus 优点:

  • 简化组件之间的通讯方式;
  • 对通信双方进行解藕;
  • 通过 ThreadMode 灵活切换工作线程;
  • 速度快、性能好、库比较小、不占内存;

首先看一下官方给出的 EventBus 原理图:
EventBus 原理图
Publisher 使用 post() 函数发出一个 Event 事件,SubscriberonEvent() 函数中接收事件、进行后续的处理。

EventBus 三要素

使用 EventBus 时的三个重要参与者:EventPublisherSubscriber

  • Event:事件,它可以是任意类型;
  • Publisher:事件的发布者,可以在任意线程里发布事件,一般情况下,使用 EventBus.getDefault() 方法就可以得到一个 EventBus 对象,然后再调用其 post(Object) 方法即可;
  • Subscriber:事件订阅者,在 EventBus3.0 之前我们必须定义以 onEvent 开头的那几个方法,分别是:onEventonEventMainThreadonEventBackgroundThreadonEventAsync,而在 3.0 之后事件处理的方法名可以随意取,不过需要加上注解 @subscribe(),并且要指定线程模型,默认是 POSTING

EventBus 线程模型

ThreadMode 线程模式,通过 threadMode 设置 onReceiveMsg() 方法将在哪个线程环境下被调用。其中 threadMode 属性有如下几个可选值:

  • ThreadMode.POSTING:默认的线程模式,订阅者的订阅方法将在发布事件的同一线程中被调用,避免了线程切换,效率高;但是可能发布事件的线程是主线程,所以需要避免在订阅方法中处理耗时操作;
  • ThreadMode.MAIN:订阅者的订阅方法将在 UI 线程被调用,如在 UI 主线程发布事件,则直接在主线程处理事件;如果在子线程发送事件,则先将事件入队列,然后通过 Handler 切换到主线程,依次处理事件;
  • ThreadMode.MAIN_ORDERED:无论在那个线程发送事件,都先将事件入队列,然后通过 Handler 切换到主线程,依次处理事件(注意:该模式下可以确保 post() 的调用是非阻塞的);
  • ThreadMode.BACKGROUND:表示订阅者的订阅方法在后台线程。如果发布事件的线程是 UI 主线程,那么将开启一个后台线程执行订阅方法;如果发布事件的线程是在后台线程,那么事件处理函数就使用该线程;
  • ThreadMode.ASYNC:表示无论发布事件的线程是哪一个,订阅者的订阅方法始终会新建一个子线程来执行。所以这种情况下可以做耗时操作,但是需要避免在同一时间进行大量的异步订阅,控制并发线程的数量。

说了这么多,最后再来看一下 EventBus 有何缺点:

  • 使用的时需定义很多 Event 类;
  • 需要自己注册和反注册,如果忘了反注册就会导致内存泄漏;
  • Event 在注册的时候会通过反射去遍历注册对象的方法,并在其中找出带有 @subscriber 标签的方法,性能不高(可以通过编译时解析注解,优化运行时反射带来的性能损耗)。

二、EventBus 使用

1.添加依赖

app 或底层 base 库中的 builde.gradle 文件中导入依赖库:

imlementation ‘org.greenrobot:eventbus:3.2.0

2.EventBus 基本使用

通过 EventBus 的三个重要参与者:EventSubscriberPublisher 来一步步学习 EventBus 的基本使用,

2.1 定义事件类

public class EventMessage<T> {
    private int code;	// 事件类型
    private T data;		// 数据

    public EventMessage(int code, T data){
        this.code=code;
        this.data=data;
    }
    public int getCode() {
        return code;
    }
    public void setCode(int code) {
        this.code = code;
    }
    public T getData() {
        return data;
    }
    public void setData(T data) {
        this.data = data;
    }

    @Override
    public String toString() {
        return "EventMessage{" + "code=" + code + ", data=" + data + '}';
    }
}

2.2 注册 EventBus

public class EventBusActivity extends AppCompatActivity {
 
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }
    
    @Override
    protected void onStart() {
        super.onStart();
        // 注册 EventBus
        EventBus.getDefault().register(this);
    }
 
    // 接收事件、线程模式为 ThreadMode.POSTING
    @Subscribe(threadMode = ThreadMode.POSTING, priority = 1)
    public void onReceiveMsg(EventMessage message){
        Log.e("EventBus_Subscriber", "onReceiveMsg_POSTING: " + message.toString());
    }
 
    // 接收事件、线程模式为 ThreadMode.MAIN
    @Subscribe(threadMode = ThreadMode.MAIN, priority = 1)
    public void onReceiveMsg1(EventMessage message){
        Log.e("EventBus_Subscriber", "onReceiveMsg_MAIN: " + message.toString());
    }
 
    // 接收事件、线程模式为 ThreadMode.MAIN_ORDERED
    @Subscribe(threadMode = ThreadMode.MAIN_ORDERED, priority = 1)
    public void onReceiveMsg2(EventMessage message){
        Log.e("EventBus_Subscriber", "onReceiveMsg_MAIN_ORDERED: " + message.toString());
    }
 
    // 接收事件、线程模式为 ThreadMode.BACKGROUND
    @Subscribe(threadMode = ThreadMode.BACKGROUND, priority = 1)
    public void onReceiveMsg3(EventMessage message){
        Log.e("EventBus_Subscriber", "onReceiveMsg_BACKGROUND: " + message.toString());
    }
 
    // 接收事件、线程模式为 ThreadMode.ASYNC、同时设置 sticky 为 true,表示粘性事件
    @Subscribe(threadMode = ThreadMode.ASYNC, sticky = true, priority = 1)
    public void onReceiveMsg4(EventMessage message){
        Log.e("EventBus_Subscriber", "onReceiveMsg__ASYNC: " + message.toString());
    }
 
    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 解除注册的 EventBus
        EventBus.getDefault().unregister(this);
    }
}

2.3 EventBus 发起通知

通过 EventBus#post(eventMessage) 方法或者 EventBus#postSticky(eventMessage) 方法来发起事件:

@OnClick(R2.id.send_common_event)
public void clickCommon(){
    EventMessage message = new EventMessage(1, "发送普通事件");
    EventBus.getDefault().post(message);
}
 
@OnClick(R2.id.send_sticky_event)
public void clickSticky(){
    EventMessage message = new EventMessage(1, "发送黏性事件");
    EventBus.getDefault().postSticky(message);
}

至此,通过 EventBus 的 post() 方法发起的事情,在 EventBusActivity 中就可以收到并做后续的处理。postSticky() 方法最终也是调用的 EventBus 的 post() 方法,后续通过分析源码来进行剖析其具体过程。


三、EventBus 源码详解

EventBus 的实现原理主要包括如下几个方面的内容:

  • Subscribe 注解
  • 注册事件订阅方法
  • 取消注册
  • 发布、处理事件
  • 粘性事件
  • Subscriber Index

1.Subscribe 注解

EventBus3.0 开始用 Subscribe 注解配置事件订阅方法,其共有三个参数(可选):threadModeboolean stickyint priority。 完整的写法如下:

@Subscribe(threadMode = ThreadMode.MAIN, sticky = true, priority = 1)
public void onReceiveMsg(EventMessage message) {
    Log.e(TAG, "onReceiveMsg: " + message.toString());
}
  • threadMode:用来设置 onReceiveMsg() 方法将在哪个线程环境下被调用,共有五种模式,参考上面的简介;
  • sticky:一个 Boolean 类型的变量,默认值为 false,即不开启黏性 sticky 特性。其作用是订阅者可以先不进行注册,如果 post() 事件已经发出,再注册订阅者,同样可以接收到事件,并进行处理;
  • priority:优先级,是一个 int 类型的变量,默认值为 0。值越大,优先级越高,越优先接收到事件。注意:只有在 post() 事件和事件接收处理处于同一个线程环境的时候,才有意义。

具体看下 Subscribe 注解的实现:

@Documented 
@Retention(RetentionPolicy.RUNTIME) 
@Target({ElementType.METHOD}) 
public @interface Subscribe { 
    // 指定事件订阅方法的线程模式,即在哪个线程执行事件订阅方法处理事件,默认为 POSTING 
    ThreadMode threadMode() default ThreadMode.POSTING; 
    // 是否支持粘性事件,默认为 false 
    boolean sticky() default false; 
    // 指定事件订阅方法的优先级,默认为0,如果多个事件订阅方法可以接收相同事件的,则优先级高的先接收到事件 
    int priority() default 0; 
} 

在使用 Subscribe 注解时可以根据需求指定 threadModestickypriority 三个属性值。

2.注册事件订阅方法

EventBus 注册订阅事件流程
结合 EventBus 注册事件流程图,便于更好的理解源码执行流程,首先 EventBus 注册事件的方式如下:

EventBus.getDefault().register(this);

EventBus#getDefault() 是一个单例方法,保证当前只有一个 EventBus 实例对象:

2.1 EventBus 实例

public class EventBus {
    static volatile EventBus defaultInstance;
    // 默认的 EventBusBuilder 实例对象
    private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
    
    // 通过 double check 双重校验获取 EventBus 单例对象
    public static EventBus getDefault() {
        if (defaultInstance == null) {
            synchronized (EventBus.class) {
                if (defaultInstance == null) {
                    defaultInstance = new EventBus();
                }
            }
        }
        return defaultInstance;
    }
    
    // 创建一个新的EventBus实例;每个实例都是一个单独的作用域,事件在其中传递。
    // 要使用中央总线,请考虑{@link#getDefault()}
    public EventBus() {
    	// 继续调用 EventBus 的另一个有参构造函数,传入默认的 EventBusBuilder 来完成它相关属性的初始化
        this(DEFAULT_BUILDER);
    }

    EventBus(EventBusBuilder builder) {
        subscriptionsByEventType = new HashMap<>();
        typesBySubscriber = new HashMap<>();
        stickyEvents = new ConcurrentHashMap<>();
        mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
        backgroundPoster = new BackgroundPoster(this);
        asyncPoster = new AsyncPoster(this);
        indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
        subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
                builder.strictMethodVerification, builder.ignoreGeneratedIndex);
        logSubscriberExceptions = builder.logSubscriberExceptions;
        logNoSubscriberMessages = builder.logNoSubscriberMessages;
        sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
        sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
        throwSubscriberException = builder.throwSubscriberException;
        eventInheritance = builder.eventInheritance;
        executorService = builder.executorService;
    }
}

如果 defaultInstancenull,则新建一个 EventBus 实例对象赋值给 defaultInstance。最终通过调用 EventBus 的另一个有参构造函数,传入默认的 EventBusBuilder 来完成其相关属性的初始化。

可以通过 Builder 模式来支持用 EventBusBuilderEventBus 进行一些属性的配置,例如用如下方式注册事件:

EventBus.builder()
        .eventInheritance(false)
        .logSubscriberExceptions(false)
        .build()
        .register(this);

2.2 EventBus 注册

public class EventBus {
    private final SubscriberMethodFinder subscriberMethodFinder;
	
	// 注册订阅者 subscriber 以接收事件,订阅者一旦对接收事件不再感兴趣,就必须调用{@link #unregister(Object)}
    public void register(Object subscriber) {
    	// 获取当前要注册类的 Class 对象 
        Class<?> subscriberClass = subscriber.getClass();
        // 根据 Class 查找当前类中订阅了事件的方法集合,即使用了 Subscribe 注解、有 public 修饰符、一个参数的方法 
        // SubscriberMethod类主要封装了符合条件方法的相关信息:Method对象、线程模式、事件类型、优先级、是否是粘性事等 
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        synchronized (this) {
        	// 循环遍历订阅了事件的方法集合,以完成注册
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);
            }
        }
    }
}

EventBus#register() 方法的执行流程如下:

  • 通过 SubscriberMethodFinder#findSubscriberMethods() 方法,根据当前要注册类的 Class 对象查找当前类中订阅了事件的方法集合 List,即找到使用了 Subscribe 注解、有 public 修饰符、一个参数的方法,其中 SubscriberMethod 类主要封装了符合条件方法的相关信息:Method 对象、线程模式、事件类型、优先级、是否是粘性事等;
  • 循环遍历订阅了事件的方法集合,通过 EventBus#subscribe() 方法完成注册。
2.2.1 SubscriberMethodFinder#findSubscriberMethods()
class SubscriberMethodFinder {
    // METHOD_CACHE是一个ConcurrentHashMap,保存了subscriberClass和对应SubscriberMethod的集合,以提高注册效率,避免重复查找
    private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>();
    
    List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
    	// 获取 subscriberClass 类对应的 SubscriberMethod 的集合
        List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
        if (subscriberMethods != null) { // 获取到且不为 null 则直接返回找到的 SubscriberMethod 的集合
            return subscriberMethods;
        }
	
		// ignoreGenereatedIndex 是用于标记是否忽略由 Builder 传入的 SubscriberInfoIndex
		// 由于使用了默认的 EventBusBuilder,则 ignoreGeneratedIndex 属性默认为 false
        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;
        }
    }
}

SubscriberMethodFinder#findSubscriberMethods() 方法的执行流程如下:

  • 先从缓存集合 Map<Class<?>, List> METHOD_CACHE 中查找、获取 subscriberClass 类对应的 SubscriberMethod 的集合,如果找到则直接返回;
  • 如果查找不到,则根据条件判断进行下一步的查找过程,由于使用了默认的 EventBusBuilder,因此 ignoreGeneratedIndex 属性默认为 false,即忽略注解生成器,所以调用 SubscriberMethodFinder#findUsingInfo() 方法进行查找,最后将查找到的订阅事件的方法集合缓存到 METHOD_CACHE 中。
2.2.1.1 SubscriberMethodFinder#findUsingInfo()
class SubscriberMethodFinder {
    private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
    	// 通过 SubscriberMethodFinder#prepareFindState() 方法从 FindState 池中获取到非空的 FindState 并返回
        FindState findState = prepareFindState();
        findState.initForSubscriber(subscriberClass); // 初始化 FindState
        // 初始状态下 findState.clazz 就是 subscriberClass 
        while (findState.clazz != null) {
        	// 由于在 FindState.initForSubscriber() 方法初始化时 subscriberInfo 赋值为 null
        	// 且没有通过 EventBusBuilder 向 List<SubscriberInfoIndex> subscriberInfoIndexes 集合中添加 SubscriberInfoIndex
            findState.subscriberInfo = getSubscriberInfo(findState);
            if (findState.subscriberInfo != null) { // 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.clazz 为 subscriberClass 的父类 Class,即需要遍历父类
            findState.moveToSuperclass();
        }
        // 查找到的方法保存在了 FindState 实例的 subscriberMethods 集合中
        // 使用 FindState.subscriberMethods 构建一个新的 List<SubscriberMethod>,然后释放掉 FindState
        return getMethodsAndRelease(findState);
    }
}

通过注释可知在 SubscriberMethodFinder#findUsingInfo() 方法会在当前要注册的类以及其父类中查找订阅事件的方法,FindState 类是 SubscriberMethodFinder 的内部类,用来辅助查找订阅事件的方法,通过条件判断可知,接下来将通过 SubscriberMethodFinder#findUsingReflectionInSingleClass() 方法通过反射查找订阅事件的方法。

SubscriberMethodFinder 类中维护了一个 FindState 池,是一个默认大小为 4 的数组,通过 SubscriberMethodFinder#prepareFindState() 方法遍历该数组找到非 nullFindState 进行返回。而在 SubscriberMethodFinder#getMethodsAndRelease(findState) 方法中则是将搜寻的结果取出后,对 FindState 进行 recycle,之后再将其放回 FindState 池中。

2.2.1.2 SubscriberMethodFinder#findUsingReflectionInSingleClass()
class SubscriberMethodFinder {
	private void findUsingReflectionInSingleClass(FindState findState) {
        Method[] methods;
        try {
        	// 获取订阅事件的 Method 列表,使用 getDeclaredMethods() 方法其实是比 getMethods() 方法的效率更高的
        	// 尤其是对于较复杂庞大的类,如 Activity 类
            methods = findState.clazz.getDeclaredMethods();
        } catch (Throwable th) {
            // 但有时会导致 NoClassDefFoundError,此时采取备用方案,使用 getMethods() 进行获取
            methods = findState.clazz.getMethods();
            findState.skipSuperClasses = true;
        }
        // 循环遍历当前注册类的 methods 数组,筛选出符合条件的:public、non-static、non-abstract 的
        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) { // 检查其参数个数是否符合 1 的要求
                    Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                    if (subscribeAnnotation != null) { // 如果当前方法使用了 Subscribe 注解 
                        Class<?> eventType = parameterTypes[0]; // 得到该参数的类型
                        // FindState.checkAdd()方法用来判断 FindState 的 anyMethodByEventType map 是否
                        // 已经添加过以当前 eventType 为 key 的键值对,没添加过则返回 true 
                        if (findState.checkAdd(method, eventType)) {
                        	// 得到 Subscribe 注解的 threadMode 属性值,即线程模式
                            ThreadMode threadMode = subscribeAnnotation.threadMode();
                            // 创建一个 SubscriberMethod 对象,并添加到 subscriberMethods 集合 
                            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");
            }
        }
    }
}

SubscriberMethodFinder#findUsingReflectionInSingleClass() 方法的执行流程如下:

  1. 获取订阅事件的 Method 列表,注意:使用 getDeclaredMethods() 方法其实是比 getMethods() 方法的效率更高的,但有时会导致 NoClassDefFoundError,此时采取备用方案,再使用 getMethods() 进行获取;
  2. 循环遍历当前注册类的 methods 数组,筛选出符合条件:publicnon-staticnon-abstract 的,然后获取符合条件的方法的所有参数类型,如果参数个数符合 1 的要求且使用了 Subscribe 注解,则通过 FindState.checkAdd() 方法来判断 FindState 的 Map<Class, Object> anyMethodByEventType 集合中是否已经添加过以当前参数的类型 eventTypekey 的键值对,如没添加过则返回 true,随后创建一个 SubscriberMethod 对象,并添加到 FindState 的 List<SubscriberMethod> subscriberMethods 集合中。
2.2.1.3 SubscriberMethodFinder#FindState#checkAdd()
class SubscriberMethodFinder {
    static class FindState {
        final Map<Class, Object> anyMethodByEventType = new HashMap<>();
        
        boolean checkAdd(Method method, Class<?> eventType) {
            // 2级检查:第一级通过事件类型(较快速),第二级检查需具有完整签名。通常,订阅者不会监听相同事件类型的方法
            Object existing = anyMethodByEventType.put(eventType, method);
            if (existing == null) { // existing 为 null,说明之前的集合,没有当前要加入的订阅方法
                return true; // 直接返回 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);
            }
        }
    }
}

FindState#checkAdd() 方法中,将订阅事件的方法 Method 以方法的参数类型为 key 保存到 HashMap<Class, Object> anyMethodByEventType 集合中;同时还会调用 FindState#checkAddWithMethodSignature() 方法将方法以方法的签名(形式为:方法名>Event 类型名)为 key 保存到 HashMap<String, Class> subscriberClassByMethodKey 集合中。

2.2.2 EventBus#subscribe()

在 EventBus#register() 方法中通过 SubscriberMethodFinder#findSubscriberMethods() 方法,查找到当前要注册类及其父类中订阅了事件的方法集合 List subscriberMethods,随后循环遍历该方法集合,再通过 EventBus#subscribe() 方法完成注册:

public class EventBus {
	// 保存以订阅方法的参数类型 eventType 为 key,Subscription 对象集合为 value 的键值对的集合 HashMap
    private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
    // 保存以当前要注册类的对象为 key,注册类中订阅事件的方法的参数类型的集合为 value 的键值对的集合 HashMap
    private final Map<Object, List<Class<?>>> typesBySubscriber;

	// 必须在同步块中调用,EventBus 为方法的订阅过程进行了加锁,保证了线程安全
    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
        Class<?> eventType = subscriberMethod.eventType; // 得到当前订阅了事件的方法的参数类型
        // Subscription 类保存了要注册的类对象以及当前订阅的 subscriberMethod 
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
        // 查找集合 subscriptionsByEventType 中是否存在以当前 eventType 为 key 的值 
        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        if (subscriptions == null) { 
        	// 如果不存在,则创建一个 subscriptions 集合,并以当前订阅方法的参数类型为 key 保存到 subscriptionsByEventType
            subscriptions = new CopyOnWriteArrayList<>();
            subscriptionsByEventType.put(eventType, subscriptions);
        } else {
            if (subscriptions.contains(newSubscription)) {
                throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
                        + eventType);
            }
        }
		// 将新创建的 newSubscription 对象按照优先级 priority 的顺序添加到 subscriptions 中 
        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,并以当前要注册类的对象为 key 保存到 typesBySubscriber
            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);
            }
        }
    }
}

EventBus#subscribe() 方法中,新建 Subscription 实例对象保存要注册的类对象以及当前类中订阅的 subscriberMethod,将新建的 Subscription 实例对象保存到 Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType 集合中,同时将当前订阅事件方法的参数类型添加到 Map<Object, List<Class<?>>> typesBySubscriber 集合中。流程至此,EventBus 注册的核心流程的源码已经分析完。

3.EventBus 取消注册

在这里插入图片描述
结合 EventBus 取消注册流程图,再来分析源码,首先 EventBus 取消注册的方式如下:

EventBus.getDefault().unregister(this);

3.1 EventBus#unregister()

public class EventBus {
    // 保存以当前要注册类的对象为 key,注册类中订阅事件的方法的参数类型的集合为 value 的键值对的集合 HashMap
    private final Map<Object, List<Class<?>>> typesBySubscriber;
    
    // 取消给定订阅者所注册订阅的所有事件
    public synchronized void unregister(Object subscriber) {
   	    // 获取当前注册类对象对应的订阅事件方法的参数类型的集合 
        List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
        if (subscribedTypes != null) {
        	// 遍历订阅事件方法的参数类型集合,释放之前保存的当前注册类中的 Subscription
            for (Class<?> eventType : subscribedTypes) {
                unsubscribeByEventType(subscriber, eventType);
            }
            // 集合中删除以 subscriber 为 key 的键值对
            typesBySubscriber.remove(subscriber);
        } else {
            Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
        }
    }
}

EventBus#unregister() 方法,首先获取当前注册类对象对应的订阅事件方法的参数类型的集合,随后遍历订阅事件方法的参数类型集合,调用 EventBus#unsubscribeByEventType() 方法释放之前保存的当前注册类中的 Subscription。最后从 Map<Object, List<Class<?>>> typesBySubscriber 集合中删除以当前 subscriberkey 的键值对。

3.2 EventBus#unsubscribeByEventType()

public class EventBus {
	// 保存以订阅方法的参数类型 eventType 为 key,Subscription 对象集合为 value 的键值对的集合 HashMap
    private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
    
    // 只更新 subscriptionsByEventType 集合,不更新 typebysubscriber 集合,调用者必须更新 typebysubscriber 集合
    private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
    	// 获取当前订阅方法的参数类型所对应的 Subscription 集合 
        List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        if (subscriptions != null) {
            int size = subscriptions.size();
            for (int i = 0; i < size; i++) { // 遍历 Subscription 集合 
                Subscription subscription = subscriptions.get(i);
                // 如果当前 subscription 对象对应的注册类对象和要取消注册的注册类对象相同
                // 则从 Subscription 集合中删除当前 subscription 对象 
                if (subscription.subscriber == subscriber) {
                    subscription.active = false;
                    subscriptions.remove(i);
                    i--;
                    size--;
                }
            }
        }
    }
}

EventBus#unsubscribeByEventType() 方法中,获取当前订阅方法的参数类型所对应的 Subscription 集合,遍历 Subscription 集合,如果当前 subscription 对象所对应的注册类对象和要取消注册的注册类对象相同,则从 Subscription 集合中删除。流程至此,EventBus 取消注册的源码已经分析完。

4.EventBus 发布、处理事件

EventBus 发布事件的方式如下:

EventBus.getDefault().post(new EventMessage(1, "事件 1"));

4.1 EventBus#post()

public class EventBus {
	// currentPostingThreadState 是一个 PostingThreadState 类型的 ThreadLocal 
    // PostingThreadState 类保存了事件队列和线程模式等信息 
    private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
        @Override
        protected PostingThreadState initialValue() {
            return new PostingThreadState();
        }
    };    

    // 将给定的事件 event 发布到事件总线 EventBus
    public void post(Object event) {
    	// 获取 ThreadLocal 中保存的 PostingThreadState 实例对象
        PostingThreadState postingState = currentPostingThreadState.get();
        List<Object> eventQueue = postingState.eventQueue;
        eventQueue.add(event); // 将要发送的事件添加到事件队列

        if (!postingState.isPosting) { // isPosting 默认为 false 
            postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper(); // 是否为主线程
            postingState.isPosting = true; // isPosting 置为 true,使得事件 post 的过程中 当前线程的其他 post 事件无法被响应
            if (postingState.canceled) {
                throw new EventBusException("Internal error. Abort state was not reset");
            }
            try {
                while (!eventQueue.isEmpty()) { // 遍历事件队列 
                	// EventBus.postSingleEvent() 发送单个事件 
                    // eventQueue.remove(0),从事件队列移除事件 
                    postSingleEvent(eventQueue.remove(0), postingState);
                }
            } finally {
                postingState.isPosting = false; // 当 post 过程结束后,再将 isPosting 置为 false
                postingState.isMainThread = false;
            }
        }
    }
}

EventBus#post() 方法,获取 ThreadLocal 中保存的 PostingThreadState 实例对象,将要发送的事件添加到事件队列 PostingThreadState.eventQueue 中,随后遍历事件队列,调用 EventBus#postSingleEvent() 方法发送单个事件。

4.2 EventBus#postSingleEvent()

public class EventBus { 
    private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
        Class<?> eventClass = event.getClass();
        boolean subscriptionFound = false;
        if (eventInheritance) { // eventInheritance默认为true,表示是否向上查找事件的父类
        	// 查找当前事件类型 Class 及其父类、接口等保存到集合 Map<Class<?>, List<Class<?>>> eventTypesCach
            List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
            int countTypes = eventTypes.size();
            for (int h = 0; h < countTypes; h++) { // 遍历 eventTypesCach 集合,继续处理事件 
                Class<?> clazz = eventTypes.get(h);
                subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
            }
        } else {
            subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
        }
        if (!subscriptionFound) {
            if (logNoSubscriberMessages) {
                Log.d(TAG, "No subscribers registered for event " + eventClass);
            }
            if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                    eventClass != SubscriberExceptionEvent.class) {
                post(new NoSubscriberEvent(this, event));
            }
        }
    }
}

EventBus#postSingleEvent() 方法中,根据 eventInheritance 属性,决定是否向上遍历事件的父类型,将获取到的当前事件类型 Class 及其父类、接口等保存到 Map<Class<?>, List<Class<?>>> eventTypesCach 集合中,然后遍历刚获取的集合,对集合中的每一个 Class 调用 EventBus#postSingleEventForEventType() 方法进一步处理。

4.3 EventBus#postSingleEventForEventType()

public class EventBus { 
	// 保存以订阅方法的参数类型 eventType 为 key,Subscription 对象集合为 value 的键值对的集合 HashMap
    private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
    
	private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
        CopyOnWriteArrayList<Subscription> subscriptions;
        synchronized (this) {
        	// 获取事件类型对应的 Subscription 集合
            subscriptions = subscriptionsByEventType.get(eventClass);
        }
        // 如果已有订阅者订阅了对应类型的事件
        if (subscriptions != null && !subscriptions.isEmpty()) {
            for (Subscription subscription : subscriptions) {
                postingState.event = event; // PostingThreadState 记录事件
                postingState.subscription = subscription; // PostingThreadState 记录对应的 subscription
                boolean aborted = false;
                try {
                	// EventBus.postToSubscription() 方法对事件进行处理 
                    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;
    }
}

EventBus#postSingleEventForEventType() 方法中,首先同步加锁获取事件类型对应的 Subscription 集合,如果获得的集合不为 null,表示已有订阅者订阅了对应类型的事件,则遍历 Subscription 集合,为每一个 Subscription 调用 EventBus#postToSubscription() 方法来处理事件。

4.4 EventBus#postToSubscription()

public class EventBus { 
    private final HandlerPoster mainThreadPoster;
    private final BackgroundPoster backgroundPoster;
    private final AsyncPoster asyncPoster;
    
    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 {
                	// 如果是在子线程发送事件,则将事件入队列,通过 Handler 切换到主线程执行处理事件 
                    // mainThreadPoster 不为空
                    mainThreadPoster.enqueue(subscription, event);
                }
                break;
            case MAIN_ORDERED: // 无论在那个线程发送事件,都先将事件入队列,然后通过 Handler 切换到主线程,依次处理事件
                if (mainThreadPoster != null) { 
                	// mainThreadPoster 不为空,将事件入队列,然后通过 Handler 切换到主线程,依次处理事件
                    mainThreadPoster.enqueue(subscription, event); 
                } else { 
                	// 否者直接在主线程通过反射处理事件
                    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#postToSubscription() 方法,根据订阅事件方法的线程模式、以及发送事件的线程来判断如何处理事件,处理方式主要有两种:

  1. 在相应线程直接通过 EventBus#invokeSubscriber() 方法,通过反射来执行订阅事件的方法,此时发送出去的事件就被订阅者接收并做相应处理;
  2. 通过不同的 Poster 将事件入队,然后采用队列的方式做进一步处理。
4.4.1 EventBus#invokeSubscriber()
public class EventBus { 
    void invokeSubscriber(Subscription subscription, Object event) {
        try { // 反射调用来执行订阅事件的方法
            subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
        } catch (InvocationTargetException e) {
            handleSubscriberException(subscription, event, e.getCause());
        } catch (IllegalAccessException e) {
            throw new IllegalStateException("Unexpected exception", e);
        }
    }
}

EventBus#invokeSubscriber() 方法中,由 Subscription 实例保存的事件订阅方法,通过反射来执行订阅者的事件订阅方法,此时发布的事件就被订阅者接收并做相应处理。

4.4.2 HandlerPoster#enqueue()
final class HandlerPoster extends Handler {

    private final PendingPostQueue queue;
    private final int maxMillisInsideHandleMessage;
    private final EventBus eventBus;
    private boolean handlerActive;

    HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) {
        super(looper);
        this.eventBus = eventBus;
        this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage;
        queue = new PendingPostQueue();
    }

    void enqueue(Subscription subscription, Object event) {
    	// 通过 PendingPost.obtainPendingPost() 方法从 pendingPostPool 缓存中获取 PendingPost 并赋值
    	// 如果缓存中没有,则由 subscription 和 event 新建一个 PendingPost 对象
        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
        synchronized (this) {
            queue.enqueue(pendingPost); // 通过 PendingPostQueue.enqueue() 入队
            if (!handlerActive) {
                handlerActive = true;
                // 发送开始处理事件的消息,handleMessage() 方法将被执行,完成从子线程到主线程的切换
                if (!sendMessage(obtainMessage())) {
                    throw new EventBusException("Could not send handler message");
                }
            }
        }
    }

    @Override
    public void handleMessage(Message msg) {
        boolean rescheduled = false;
        try {
            long started = SystemClock.uptimeMillis();
            while (true) { // 死循环遍历队列
                PendingPost pendingPost = queue.poll(); // 出队获取 PendingPost
                if (pendingPost == null) {
                    synchronized (this) {
                        // 再检查一次,这次是同步的
                        pendingPost = queue.poll();
                        if (pendingPost == null) {
                            handlerActive = false; // 经过两次获取仍然为 null 则直接返回并置 handlerActive 为 false
                            return;
                        }
                    }
                }
                // 通过 EventBus.invokeSubscriber() 方法进一步处理 pendingPost
                eventBus.invokeSubscriber(pendingPost);
                long timeInMethod = SystemClock.uptimeMillis() - started;
                if (timeInMethod >= maxMillisInsideHandleMessage) {
                    if (!sendMessage(obtainMessage())) {
                        throw new EventBusException("Could not send handler message");
                    }
                    rescheduled = true;
                    return;
                }
            }
        } finally {
            handlerActive = rescheduled;
        }
    }
}

HandlerPoster#enqueue() 方法,首先通过 PendingPost.obtainPendingPost() 方法从 pendingPostPool 缓存中获取 PendingPost 并赋值传入的 subscriptionevent 对象,如果缓存中获取不到,则由 subscriptionevent 新建一个 PendingPost 对象,并将 PendingPost 添加到 PendingPostQueue 队列中,随后通过 Handler 切换到主线程,在 Handler#handleMessage() 方法中将 PendingPost 对象循环出队列,交给 EventBus#invokeSubscriber() 方法进行处理。

4.4.3 EventBus#invokeSubscriber()
public class EventBus {
    void invokeSubscriber(PendingPost pendingPost) {
        Object event = pendingPost.event;
        Subscription subscription = pendingPost.subscription;
        PendingPost.releasePendingPost(pendingPost); // 释放 PendingPost 引用的资源
        if (subscription.active) {
        	// 最终,通过反射来执行订阅事件的方法
            invokeSubscriber(subscription, event);
        }
    }
}

EventBus#invokeSubscriber() 方法,主要就是从 PendingPost 中取出之前保存的 eventsubscription,然后通过反射来执行订阅事件的方法,又回到了第一种处理方式。所以 HandlerPoster#enqueue(subscription, event) 方法的核心就是先将将事件入队列,然后通过 Handler 从子线程切换到主线程中去处理事件。

4.4.4 BackgroundPoster#enqueue()
final class BackgroundPoster implements Runnable {
    private final PendingPostQueue queue;
    private final EventBus eventBus;
    private volatile boolean executorRunning;

    BackgroundPoster(EventBus eventBus) {
        this.eventBus = eventBus;
        queue = new PendingPostQueue();
    }

    public void enqueue(Subscription subscription, Object event) {
    	// 通过 PendingPost.obtainPendingPost() 方法从 pendingPostPool 缓存中获取 PendingPost 并赋值
    	// 如果缓存中没有,则由 subscription 和 event 新建一个 PendingPost 对象
        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
        synchronized (this) {
            queue.enqueue(pendingPost); // 通过 PendingPostQueue.enqueue() 入队
            if (!executorRunning) {
                executorRunning = true;
                // 通过线程池来执行当前 BackgroundPoster 的 run() 方法来进一步处理事件
                eventBus.getExecutorService().execute(this);
            }
        }
    }

    @Override
    public void run() {
        try {
            try {
                while (true) { // 死循环遍历队列
                    PendingPost pendingPost = queue.poll(1000); // 出队获取 PendingPost
                    if (pendingPost == null) {
                        synchronized (this) {
                            // 再检查一次,这次是同步的
                            pendingPost = queue.poll();
                            if (pendingPost == null) {
                            	// 经过两次获取仍然为 null 则直接返回并置 handlerActive 为 false
                                executorRunning = false;
                                return;
                            }
                        }
                    }
                    // 通过 EventBus.invokeSubscriber() 方法进一步处理 pendingPost
                    eventBus.invokeSubscriber(pendingPost);
                }
            } catch (InterruptedException e) {
                Log.w("Event", Thread.currentThread().getName() + " was interruppted", e);
            }
        } finally {
            executorRunning = false;
        }
    }
}

BackgroundPoster#enqueue() 方法跟 HandlerPoster#enqueue() 方法的功能差不多,核心也是先将事件加入到 PendingPostQueue 队列,然后再出队列。不同之处是 BackgroundPoster 是通过线程池来执行其 run() 方法,最后交给 EventBus#invokeSubscriber() 方法进行处理。

4.4.5 AsyncPoster#enqueue()
class AsyncPoster implements Runnable {
    private final PendingPostQueue queue;
    private final EventBus eventBus;

    AsyncPoster(EventBus eventBus) {
        this.eventBus = eventBus;
        queue = new PendingPostQueue();
    }

    public void enqueue(Subscription subscription, Object event) {
        // 通过 PendingPost.obtainPendingPost() 方法从 pendingPostPool 缓存中获取 PendingPost 并赋值
    	// 如果缓存中没有,则由 subscription 和 event 新建一个 PendingPost 对象
        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
        queue.enqueue(pendingPost); // 通过 PendingPostQueue.enqueue() 入队
        // 通过线程池来执行当前 AsyncPoster 的 run() 方法来进一步处理事件
        eventBus.getExecutorService().execute(this);
    }

    @Override
    public void run() {
        PendingPost pendingPost = queue.poll();
        if(pendingPost == null) {
            throw new IllegalStateException("No pending post available");
        }
        // 通过 EventBus.invokeSubscriber() 方法进一步处理 pendingPost
        eventBus.invokeSubscriber(pendingPost);
    }
}

AsyncPoster#enqueue() 方法跟 HandlerPoster#enqueue() 方法差不多,核心也是先将事件加入到 PendingPostQueue 队列,然后再出队列,随后通过线程池来执行其 run() 方法,最后交给 EventBus#invokeSubscriber() 方法进行处理。流程至此,EventBus 发布事件与处理事件的源码已经分析完。

EventBus 发布事件(包括粘性事件)及处理流程

EventBus 发布事件及处理流程

5.EventBus 粘性事件

一般情况,使用 EventBus 都是准备好订阅事件的方法,然后注册事件,最后在发布事件,即要先有事件的接收者(订阅者)。但粘性事件却恰恰相反,可以先发布事件,后续再准备订阅事件的方法、以及注册事件。

发布粘性事件通过如下方式:

EventBus.getDefault().postSticky(new EventMessage(2, "粘性事件"));

5.1 EventBus#postSticky()

public class EventBus {
	// 发布粘性事件时,保存事件的类型和对应事件本身
    private final Map<Class<?>, Object> stickyEvents;

    // 将给定的事件发布到 EventBus 并保持该事件(因为它具有粘性)。
    // 相同事件类型、最近的粘性事件保存在内存中,供订阅者使用 Subscribe#sticky() 访问
    public void postSticky(Object event) {
        synchronized (stickyEvents) {
        	// 将发布的事件和事件类型保存到 stickyEvents
            stickyEvents.put(event.getClass(), event);
        }
        // 保存后再调用 EventBus#post(event) 方法,以防订阅者想立即删除
        post(event);
    }
}

EventBus#postSticky() 方法执行流程如下:

  1. 将事件类型和对应事件保存到 Map<Class<?>, Object> stickyEvents 集合中,等待后续使用;
  2. 通过 EventBus#post(event) 方法继续发布事件。因此,如果在发布粘性事件前,已经有了对应类型事件的订阅者,即使它是非粘性的,依然可以接收到发布的粘性事件。

通过 EventBus#post(event) 方法发布粘性事件,流程在前面已经分析过,在前面分析 EventBus#subscribe() 方法时,关于粘性事件的处理过程还没分析,下面一起来剖析一下这段代码。

5.2 EventBus#subscribe()

public class EventBus {
    // 发布粘性事件时,保存事件的类型和对应事件本身
    private final Map<Class<?>, Object> stickyEvents;
    private final boolean eventInheritance; // 默认为 true,表示是否向上查找事件的父类

	// 必须在同步块中调用,EventBus 为方法的订阅过程进行了加锁,保证了线程安全
    private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
		......
		// 如果当前订阅事件方法的 Subscribe 注解的 sticky 属性为 true,即该方法可接受粘性事件
        if (subscriberMethod.sticky) {
            if (eventInheritance) { // 默认为 true,表示向上查找事件的父类
                // 须考虑 eventType 所有子类的现有粘性事件。注意:对于粘性事件较多的情况,遍历所有事件效率不高
                // 因此要更改数据结构使得查找更加有效(如:存储父类的子类集合:Class -> List<Class>)
                Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
                for (Map.Entry<Class<?>, Object> entry : entries) {
                    Class<?> candidateEventType = entry.getKey();
                    // 如果 candidateEventType 是 eventType 的子类
                    if (eventType.isAssignableFrom(candidateEventType)) {
                        Object stickyEvent = entry.getValue(); // 获得对应的事件
                        // 通过 EventBus.checkPostStickyEventToSubscription() 方法处理粘性事件
                        checkPostStickyEventToSubscription(newSubscription, stickyEvent);
                    }
                }
            } else {
                Object stickyEvent = stickyEvents.get(eventType);
                // 通过 EventBus.checkPostStickyEventToSubscription() 方法处理粘性事件
                checkPostStickyEventToSubscription(newSubscription, stickyEvent);
            }
        }
    }
}

EventBus#subscribe() 方法中,如果当前订阅事件方法的 Subscribe 注解的 sticky 属性为 true,即该方法可接受粘性事件。遍历 EventBus#postSticky() 方法保存到 Map<Class<?>, Object> stickyEvents 集合中的粘性事件,如果 stickyEvents 中取出事件的事件类型与当前订阅方法接收的事件类型相同或者是其子类,则取出 stickyEvents 中对应事件类型的具体事件,然后通过 EventBus.checkPostStickyEventToSubscription() 方法处理粘性事件。

5.3 EventBus#checkPostStickyEventToSubscription()

public class EventBus {
    // 发布粘性事件时,保存事件的类型和对应事件本身
    private final Map<Class<?>, Object> stickyEvents;
    private final boolean eventInheritance; // 默认为 true,表示是否向上查找事件的父类

	private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
        if (stickyEvent != null) {
            // 如果订阅者试图中止当前事件,则将失败(在发布状态下事件无法追踪)
            postToSubscription(newSubscription, stickyEvent, Looper.getMainLooper() == Looper.myLooper());
        }
    }
}

EventBus#checkPostStickyEventToSubscription() 方法,在判空操作通过后,通过 EventBus#postToSubscription() 方法完成粘性事件的处理,处理流程前面已经分析过,不再重复分析。流程至此,EventBus 粘性事件的发布与处理流程已经分析完。

6.EventBus 之 Subscriber Index

通过上面几节的分析可知,EventBus 在项目运行时默认是通过反射来查找订阅事件的方法信息,如果项目中存在大量的订阅事件的方法,通过反射必然会对项目运行时的性能产生影响。EventBus 也考虑到了这个问题,因此除了默认的反射之外,还提供了在项目编译时通过注解处理器(APT 全称:Annotation Processing Tool)查找订阅事件方法信息的方式,在编译期生成一个辅助的索引类 Subscriber Index 来保存这些信息。

要在项目编译时查找订阅事件的方法信息,首先要在 app 的 build.gradle 中加入如下配置:

android {
    defaultConfig {
        javaCompileOptions {
            annotationProcessorOptions {
                // 根据项目实际情况,指定辅助索引类的名称和包名
                arguments = [ eventBusIndex : 'com.xxx.xxx.EventBusIndex' ]
            }
        }
    }
}
dependencies {
    compile 'org.greenrobot:eventbus:3.2.0'
    // 引入注解处理器
    annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.2.0'
}

然后在项目的 Application 中添加如下配置,以生成一个默认的 EventBus 单例:

public class AndroidApplication extends Application implements Application.ActivityLifecycleCallbacks {
    @Override
    public void onCreate() {
        super.onCreate();
        registerActivityLifecycleCallbacks(this);
        EventBus.builder().addIndex(new EventBusIndex()).installDefaultEventBus();
    }
}

用法还是和上面的例子一样,只是在项目编译时会生成对应的 EventBusIndex 类(这里是在项目的底层 baselibrary 进行配置的,进行了封装,如果是简单使用,放到 app 模块即可):
在这里插入图片描述

6.1 EventBusIndex

// 这个类是由 EventBus 生成的,不要编辑
public class EventBusIndex implements SubscriberInfoIndex {
	// 保存当前注册类的 Class 类型和其中事件订阅方法的信息
    private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;

    static {
        SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();

        putIndex(new SimpleSubscriberInfo(com.xxx.xxx.baselibrary.mvp.view.BaseMVPActivity.class, true,
                new SubscriberMethodInfo[] {
            new SubscriberMethodInfo("onEvent", java.util.Map.class, ThreadMode.MAIN),
        }));

    }

    private static void putIndex(SubscriberInfo info) {
        SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);
    }

    @Override
    public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {
        SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);
        if (info != null) {
            return info;
        } else {
            return null;
        }
    }
}

EventBusIndex 是由 EventBus 生成的,不要编辑。其内部首先在静态代码块内新建 HashMap 保存当前注册类的 Class 类型和其中事件订阅方法的信息,然后通过 EventBusIndex#putIndex() 方法将新建的 SimpleSubscriberInfo 实例对象添加到新建的 HashMap 中保存。

接下来通过源码剖析使用 Subscriber IndexEventBus 的注册流程,由前面的使用代码可知,期首先创建一个 EventBusBuilder 实例对象,然后通过其 addIndex() 方法添加索引类的实例:

6.2 EventBusBuilder#addIndex()

public class EventBusBuilder {
    List<SubscriberInfoIndex> subscriberInfoIndexes;
    
    /** Adds an index generated by EventBus' annotation preprocessor. */
    // 添加一个由 EventBus 注解处理器生成的索引 SubscriberInfoIndex
    public EventBusBuilder addIndex(SubscriberInfoIndex index) {
        if(subscriberInfoIndexes == null) {
            subscriberInfoIndexes = new ArrayList<>();
        }
        subscriberInfoIndexes.add(index);
        return this;
    }
}

EventBusBuilder#addIndex() 方法把生成的索引类的实例保存在 List<SubscriberInfoIndex> subscriberInfoIndexes 集合中。

6.2 EventBusBuilder#installDefaultEventBus()

public class EventBusBuilder {
    /**
     * 安装由 EventBus#getDefault() 方法返回的默认 EventBus(使用 EventBusBuilder 的值)
     * 必须在第一次使用默认 EventBus 之前完成一次
     *
     * 如果已经有一个默认的 EventBus 实例,抛出 EventBusException
     */
    public EventBus installDefaultEventBus() {
        synchronized (EventBus.class) {
            if (EventBus.defaultInstance != null) {
                throw new EventBusException("Default instance already exists." +
                        " It may be only set once before it's used the first time to ensure consistent behavior.");
            }
            EventBus.defaultInstance = build();
            return EventBus.defaultInstance;
        }
    }
    
    // 基于当前配置构建 EventBus 实例对象
    public EventBus build() {
    	// this 代表当前 EventBusBuilder 对象
        return new EventBus(this);
    }
}

EventBusBuilder#installDefaultEventBus() 方法就是使用当前 EventBusBuilder 实例对象构建一个 EventBus 实例,然后赋值给 EventBusdefaultInstance 成员变量,这样通过 EventBusBuilder 配置的 Subscriber Index 也就传递到了 EventBus 实例中。

6.3 SubscriberMethodFinder#findUsingInfo()

由于配置使用了 Subscriber Index 索引类,回头再看 SubscriberMethodFinder#findUsingInfo() 方法,执行流程将会有所不同:

class SubscriberMethodFinder {
    private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
    	// 通过 SubscriberMethodFinder#prepareFindState() 方法从 FindState 池中获取到非空的 FindState 并返回
        FindState findState = prepareFindState();
        findState.initForSubscriber(subscriberClass); // 初始化 FindState
        // 初始状态下 findState.clazz 就是 subscriberClass 
        while (findState.clazz != null) {
        	// 虽然 FindState.initForSubscriber() 方法初始化时 subscriberInfo 赋值为 null
        	// 但此时通过 EventBusBuilder 向 List<SubscriberInfoIndex> subscriberInfoIndexes 集合中添加了
        	// SubscriberInfoIndex 的实现类 EventBusIndex 实例对象,因此,getSubscriberInfo() 方法将返回
        	// EventBusIndex#getSubscriberInfo() 方法的返回值,这里将返回 SimpleSubscriberInfo 实例对象
        	// 该实例对象是在 EventBusIndex 初始化时创建的,将返回的该实例对象赋值给 FindState.subscriberInfo 
            findState.subscriberInfo = getSubscriberInfo(findState);
            if (findState.subscriberInfo != null) { // findState.subscriberInfo 此时不为 null
            	// 通过 SimpleSubscriberInfo.getSubscriberMethods() 方法获得当前注册类中所有订阅了事件的方法
                SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
                for (SubscriberMethod subscriberMethod : array) {
                	// FindState.checkAdd()方法用来判断 FindState 的 anyMethodByEventType map 是否
                    // 已经添加过以当前 eventType 为 key 的键值对,没添加过则返回 true 
                    if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
                    	// 将 SubscriberMethod 对象,添加到 subscriberMethods 集合 
                        findState.subscriberMethods.add(subscriberMethod);
                    }
                }
            } else { // 通过反射查找订阅事件的方法 
                findUsingReflectionInSingleClass(findState);
            }
            // 修改 findState.clazz 为 subscriberClass 的父类 Class,即需要遍历父类
            findState.moveToSuperclass();
        }
        // 查找到的方法保存在了 FindState 实例的 subscriberMethods 集合中
        // 使用 FindState.subscriberMethods 构建一个新的 List<SubscriberMethod>,然后释放掉 FindState
        return getMethodsAndRelease(findState);
    }
}

SubscriberMethodFinder#findUsingInfo() 方法中,由于配置使用了 Subscriber Index,此时 SubscriberMethodFinder#getSubscriberInfo() 方法的返回值不再为 null,具体来看一下该方法的执行流程。

6.3.1 SubscriberMethodFinder#getSubscriberInfo()
class SubscriberMethodFinder {
    private SubscriberInfo getSubscriberInfo(FindState findState) {
    	// FindState.initForSubscriber() 方法初始化时 subscriberInfo 赋值为 null,因此该条件不成立
        if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) {
            SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();
            if (findState.clazz == superclassInfo.getSubscriberClass()) {
                return superclassInfo;
            }
        }
        // 通过 EventBusBuilder 向 List<SubscriberInfoIndex> subscriberInfoIndexes 集合中添加了
        // SubscriberInfoIndex 的实现类 EventBusIndex 实例对象
        if (subscriberInfoIndexes != null) { // subscriberInfoIndexes 不为 null
            for (SubscriberInfoIndex index : subscriberInfoIndexes) { // 遍历索引类实例集合
            	// 根据注册类的 Class 类查找 SubscriberInfo
                SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
                if (info != null) {
                    return info;
                }
            }
        }
        return null;
    }
}

SubscriberMethodFinder#getSubscriberInfo() 方法,遍历 List<SubscriberInfoIndex> subscriberInfoIndexes 集合根据注册类的 Class 类查找 SubscriberInfo,此时将返回 EventBusIndex 初始化时创建的 SimpleSubscriberInfo 实例对象。

6.3.2 SimpleSubscriberInfo#getSubscriberMethods()
public class SimpleSubscriberInfo extends AbstractSubscriberInfo {
    @Override
    public synchronized SubscriberMethod[] getSubscriberMethods() {
        int length = methodInfos.length;
        SubscriberMethod[] methods = new SubscriberMethod[length];
        for (int i = 0; i < length; i++) {
            SubscriberMethodInfo info = methodInfos[i];
            methods[i] = createSubscriberMethod(info.methodName, info.eventType, info.threadMode,
                    info.priority, info.sticky);
        }
        return methods;
    }
}

SimpleSubscriberInfo#getSubscriberMethods() 方法的主要作用是获取当前注册类中所有订阅了事件的方法。

流程至此,EventBus 使用 Subscriber Index 的主要源码分析完毕,其它的和之前的注册订阅流程一样。

小结

Subscriber Index 的核心就是项目编译时使用注解处理器生成保存事件订阅方法信息的索引类,然后项目运行时将索引类实例设置到 EventBus 中,这样当注册 EventBus 时,从索引类取出当前注册类对应的事件订阅方法信息,以完成最终的注册,避免了运行时反射处理的过程,所以在性能上会有质的提高。项目中可以根据实际的需求决定是否使用 Subscriber Index


总结

  • EventBus 底层采用的是注解和反射的方式来获取订阅方法信息;
  • 整个 EventBus 可以看出,事件是被观察者,订阅者类是观察者,当事件出现或者发布变更的时候,会通过 EventBus 通知观察者,使得观察者的订阅方法能够被自动调用;

注意:项目中根据实际需求决定是否使用,如果滥用的话,各种逻辑交叉在一起,也可能会给后期的维护带来困难。

参考

greenrobot/EventBus:github.com/greenrobot/EventBus

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1928454.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

禹神:一小时快速上手Electron,前端Electron开发教程,笔记。一篇文章入门Electron

一、Electron是什么 简单的一句话&#xff0c;就是用htmlcssjsnodejs&#xff08;Native Api&#xff09;做兼容多个系统&#xff08;Windows、Linux、Mac&#xff09;的软件。 官网解释如下(有点像绕口令)&#xff1a; Electron是一个使用 JavaScript、HTML 和 CSS 构建桌面…

Qt实现MDI应用程序

本文记录Qt实现MDI应用程序的相关操作实现 目录 1.MDM模式下窗口的显示两种模式 1.1TabbedView 页签化显示 1.2 SubWindowView 子窗体显示 堆叠cascadeSubWindows 平铺tileSubWindows 2.MDM模式实现记录 2.1. 窗体继承自QMainWindow 2.2.增加组件MdiArea 2.3.定义统一…

react自定义校验报错问题修复 ProFormText

1、以下是tsx组件 自定义校验告警导致表单无法提交问题修复 修改如下&#xff1a;

Mac Dock栏多屏幕漂移固定的方式

记录一下 我目前的版本是 14.5 多个屏幕&#xff0c;Dock栏切换的方式&#xff1a; 把鼠标移动到屏幕的中间的下方区域&#xff0c;触到边边之后&#xff0c;继续往下移&#xff0c;就能把Dock栏固定到当前屏幕了。

flutter 手写 TabBar

前言&#xff1a; 这几天在使用 flutter TabBar 的时候 我们的设计给我提了一个需求&#xff1a; 如下 Tabbar 第一个元素 左对齐&#xff0c;试了下TabBar 的配置&#xff0c;无法实现这个需求&#xff0c;他的 配置是针对所有元素的。而且 这个 TabBar 下面的 滑块在移动的时…

全开源TikTok跨境商城源码/TikTok内嵌商城/前端uniapp+后端+搭建教程

多语言跨境电商外贸商城 TikTok内嵌商城&#xff0c;商家入驻一键铺货一键提货 全开源完美运营 海外版抖音TikTok商城系统源码&#xff0c;TikToK内嵌商城&#xff0c;跨境商城系统源码 接在tiktok里面的商城。tiktok内嵌&#xff0c;也可单独分开出来当独立站运营 二十一种…

论文翻译:通过云计算对联网多智能体系统进行预测控制

通过云计算对联网多智能体系统进行预测控制 文章目录 通过云计算对联网多智能体系统进行预测控制摘要前言通过云计算实现联网的多智能体控制系统网络化多智能体系统的云预测控制器设计云预测控制系统的稳定性和一致性分析例子结论 摘要 本文研究了基于云计算的网络化多智能体预…

ubuntu虚拟机安装ssh时报错 正在等待缓存锁

问题&#xff1a; 连接vm ubuntu虚拟机安装ssh时报错 正在等待缓存锁。 sudo apt install openssh-server 处理办法 sudo rm /var/lib/dpkg/lock-frontend sudo rm /var/cache/apt/archives/lock sudo rm /var/lib/dpkg/lock

ipynb转换为pdf、Markdown(.md)

Jupyter Notebook 文件&#xff08;.ipynb&#xff09;可以转换成多种数据格式&#xff0c;以适应不同的使用场景和需求。以下是几种常见的转换格式及其简洁描述&#xff1a; HTML: Jupyter Notebook可以直接导出为静态的网页&#xff08;HTML&#xff09;格式&#xff0c;这样…

WPF 手撸插件 一

1、本文主要使不适用第三方工具&#xff0c;纯手工的WPF主项目加载另一个WPF的项目&#xff0c;这里我们加载的是*.exe。 2、项目结构如下图。AbstractionLayer用于创建插件的接口。WPFIPluginDemo是主程序。WpfPlugin3是要加载的插件程序。 3、 AbstractionLayer中添加接口IP…

昇思25天学习打卡营第22天 | 基于MobileNetv2的垃圾分类函数式自动微分

基于MobileNetV2的垃圾分类 本文档详细介绍了使用MobileNetV2模型进行垃圾分类的全过程&#xff0c;包括数据准备、模型搭建、模型训练、评估和推理等步骤。MobileNetV2是一种轻量级卷积神经网络&#xff0c;专为移动端和嵌入式设备设计&#xff0c;具有高效、低耗的特点。通过…

昇思25天学习打卡营第21天|RNN实现情感分类

这节课学习的是RNN实现情感分类&#xff0c;情感分类是自然语言处理中的经典任务&#xff0c;是典型的分类问题。本节使用MindSpore实现一个基于RNN网络的情感分类模型。 比如&#xff1a; 输入: This film is terrible 正确标签: Negative 预测标签: Negative 输入: This film…

Matlab进阶绘图第63期—带标记线的三维填充折线图

三维填充折线图是在三维折线图的基础上&#xff0c;对其与XOY平面之间的部分进行颜色填充&#xff0c;从而能够更好地刻画细节变化。 而带标记线的三维填充折线图是在其基础上&#xff0c;添加X相同的一条或多条标记线&#xff0c;以用于进一步讨论分析。 由于Matlab中未收录…

Mongodb复合索引

学习mongodb&#xff0c;体会mongodb的每一个使用细节&#xff0c;欢迎阅读威赞的文章。这是威赞发布的第90篇mongodb技术文章&#xff0c;欢迎浏览本专栏威赞发布的其他文章。如果您认为我的文章对您有帮助或者解决您的问题&#xff0c;欢迎在文章下面点个赞&#xff0c;或者关…

Windows上LabVIEW编译生成可执行程序

LabVIEW项目浏览器(Project Explorer)中的"Build Specifications"就是用来配置项目发布方法的。在"Build Specifications"右键菜单中选取"New"&#xff0c;可以看到程序有几种不同的发布方法&#xff1a;Application(EXE)、Installer、.Net Inte…

C# 基于共享内存实现跨进程队列

C# 进程通信系列 第一章 共享内存 第二章 共享队列&#xff08;本章&#xff09; 文章目录 C# 进程通信系列前言一、实现原理1、用到的主要对象2、创建共享内存3、头部信息4、入队5、出队6、释放资源 二、完整代码三、使用示例1、传输byte[]数据2、传输字符串3、传输对象 总结…

HarmonyOS 屏幕适配设计

1. armonyOS 屏幕适配设计 1.1. 像素单位 &#xff08;1&#xff09;px (Pixels)   px代表屏幕上的像素点&#xff0c;是手机屏幕分辨率的单位&#xff0c;即屏幕物理像素单位。 &#xff08;2&#xff09;vp (Viewport Percentage)   vp是视口百分比单位&#xff0c;基于…

Java学习之SPI、JDBC、SpringFactoriesLoader、Dubbo

概述 SPI&#xff0c;Service Provider Interface&#xff0c;一种服务发现机制&#xff0c;指一些提供给你继承、扩展&#xff0c;完成自定义功能的类、接口或方法。 在SPI机制中&#xff0c;服务提供者为某个接口实现具体的类&#xff0c;而在运行时通过SPI机制&#xff0c…

Facebook未来展望:数字社交平台的进化之路

在信息技术日新月异的时代&#xff0c;社交媒体平台不仅是人们交流沟通的重要工具&#xff0c;更是推动社会进步和变革的重要力量。作为全球最大的社交媒体平台之一&#xff0c;Facebook在过去十多年里&#xff0c;不断创新和发展&#xff0c;改变了数十亿用户的社交方式。展望…

构建企业多层社会传播网络:以AI智能名片S2B2C商城小程序为例

摘要&#xff1a;在数字化转型的浪潮中&#xff0c;企业如何有效构建并优化其社会传播网络&#xff0c;已成为提升市场竞争力、深化用户关系及实现价值转化的关键。本文以AI智能名片S2B2C商城小程序为例&#xff0c;深入探讨如何通过一系列精细化的策略与技术创新&#xff0c;构…