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

文章目录

  • 前言
  • 一、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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

无人机之电动系统篇

无人机的动能系统为无人机提供了动力&#xff0c;使无人机能够进行飞行活动。电动系统是无人机动力系统的其中一种。电力系统是将化学能转化为电能&#xff0c;再转化为机械能&#xff0c;为无人机飞行提供动力的系统。电力系统有电池、电调、电机和螺旋桨四个部分组成。 电池…

论文阅读【时间序列】TimeMixer (ICLR2024)

【时间序列】TimeMixer (ICLR2024) 原文链接&#xff1a;TIMEMIXER: DECOMPOSABLE MULTISCALE MIXING FOR TIME SERIES FORECASTING 代码仓库&#xff1a;https://github.com/kwuking/TimeMixer 符号定义 符号含义P用于预测的历史序列长度&#xff08;seq_len&#xff09;F预测…

第七天 SpringBoot与SpringCloud微服务项目交付

Spring Cloud微服务项目交付 微服务扫盲篇 微服务并没有一个官方的定义&#xff0c;想要直接描述微服务比较困难&#xff0c;我们可以通过对比传统WEB应用&#xff0c;来理解什么是微服务。 单体应用架构 如下是传统打车软件架构图&#xff1a; 这种单体应用比较适合于小项…

LVS+Keepalive高可用

1、keepalive 调度器的高可用 vip地址主备之间的切换&#xff0c;主在工作时&#xff0c;vip地址只在主上&#xff0c;vip漂移到备服务器。 在主备的优先级不变的情况下&#xff0c;主恢复工作&#xff0c;vip会飘回到住服务器 1、配优先级 2、配置vip和真实服务器 3、主…

基于hive数据库的泰坦尼克号幸存者数据分析

进入 ./beeline -u jdbc:hive2://node2:10000 -n root -p 查询 SHOW TABLES; 删除 DROP TABLE IF EXISTS tidanic; 上传数据 hdfs dfs -put train.csv /user/hive/warehouse/mytrain.db/tidanic 《泰坦尼克号幸存者数据分析》 1、原始数据介绍 泰坦尼克号是当时世界上…

PyTorch人脸识别

新书速览|PyTorch深度学习与企业级项目实战-CSDN博客 一套基本的人脸识别系统主要包含三部分&#xff1a;检测器、识别器和分类器&#xff0c;流程架构如图11-3所示&#xff1a; 图11-5 检测器负责检测图片中的人脸&#xff0c;再将检测出来的人脸感兴趣区域&#xff08;Reg…

音视频入门基础:H.264专题(13)——FFmpeg源码中通过SPS属性获取视频色彩格式的实现

一、引言 通过FFmpeg命令可以获取到H.264裸流文件的色彩格式&#xff08;又译作色度采样结构、像素格式&#xff09;&#xff1a; 在vlc中也可以获取到色彩格式&#xff08;vlc底层也使用了FFmpeg进行解码&#xff09;&#xff1a; 这个色彩格式就是之前的文章《音视频入门基础…

2024年初级注册安全工程师职业资格考试首次开考!

​2024年初级注册安全工程师考试首次开考&#xff08;注&#xff1a;该考试由各省人事考试局组织考试&#xff09;。目前未取得中级注册安全工程师证书的各位同学&#xff0c;可以关注该考试&#xff0c;毕竟初级考证相对较容易&#xff0c;先去考一个。 目前初安开考地区汇总…

【Diffusion学习】【生成式AI】Stable Diffusion、DALL-E、Imagen 背後共同的套路

文章目录 图片生成Framework 需要3个组件&#xff1a;相关论文【Stable Diffusion&#xff0c;DALL-E&#xff0c;Imagen】 具体介绍三个组件1. Text encoder介绍【结论&#xff1a;文字的encoder重要&#xff0c;Diffusion的模型不是很重要&#xff01;】评估指标&#xff1a;…

大数据面试SQL题-笔记01【运算符、条件查询、语法顺序、表连接】

大数据面试SQL题复习思路一网打尽&#xff01;(文档见评论区)_哔哩哔哩_bilibiliHive SQL 大厂必考常用窗口函数及相关面试题 大数据面试SQL题-笔记01【运算符、条件查询、语法顺序、表连接】大数据面试SQL题-笔记02【...】 目录 01、力扣网-sql题 1、高频SQL50题&#xff08…

基于Java的斗地主游戏案例开发(做牌、洗牌、发牌、看牌

package Game;import java.util.ArrayList; import java.util.Collections;public class PokerGame01 {//牌盒//♥3 ♣3static ArrayList<String> list new ArrayList<>();//静态代码块//特点&#xff1a;随着类的加载而在加载的&#xff0c;而且只执行一次。stat…

【C语言】深入解析选择排序

文章目录 什么是选择排序&#xff1f;选择排序的基本实现代码解释选择排序的优化选择排序的性能分析选择排序的实际应用结论 在C语言编程中&#xff0c;选择排序是一种简单且直观的排序算法。尽管它在处理大型数据集时效率不高&#xff0c;但由于其实现简单&#xff0c;常常用于…

2024-07-15 Unity插件 Odin Inspector4 —— Collection Attributes

文章目录 1 说明2 集合相关特性2.1 DictionaryDrawerSettings2.2 ListDrawerSettings2.3 TableColumnWidth2.4 TableList2.5 TableMatrix 1 说明 ​ 本文介绍 Odin Inspector 插件中集合&#xff08;Dictionary、List&#xff09;相关特性的使用方法。 2 集合相关特性 2.1 D…

直播美颜工具开发教学:视频美颜SDK集成详解

本篇文章&#xff0c;笔者将详细介绍如何在直播应用中集成视频美颜SDK&#xff0c;让你的直播画面焕然一新。 一、什么是视频美颜SDK&#xff1f; 视频美颜SDK是一种软件开发工具包&#xff0c;提供了视频处理和图像增强功能。通过集成视频美颜SDK&#xff0c;开发者可以轻松…

十九、【文本编辑器(五)】排版功能

目录 一、搭建框架 二、实现段落对齐 三、实现文本排序 一、搭建框架 (1) 在imgprocessor.h文件中添加private变量&#xff1a; QLabel *listLabel; //排序设置项QComboBox *listComboBox;QActionGroup *actGrp;QAction *leftAction;QAction *…

Win11鼠标卡顿 - 解决方案

问题 使用Win11系统使&#xff0c;鼠标点击任务栏的控制中心&#xff08;如下图&#xff09;时&#xff0c;鼠标会有3秒左右的卡顿&#xff0c;同时整个显示屏幕也有一定程度的卡顿。 问题原因 排除鼠标问题&#xff1a;更换过不同类型的鼠标&#xff0c;以及不同的连接方式…

昇思25天学习打卡营第22天|应用实践之DCGAN生成漫画头像

基本介绍 今日要实践的模型是DCGAN&#xff0c;用于生成漫画头像&#xff0c;生成头像原理可参考GAN图像生成。使用的动漫头像数据集共有70,171张动漫头像图片&#xff0c;图片大小均为96*96。本文会先简单介绍DCGAN模型&#xff0c;然后展示自己的运行结果&#xff0c;不作代码…

新增支持GIS地图、数据模型引擎升级、增强数据分析处理能力

为了帮助企业提升数据分析处理能力&#xff0c;Smartbi重点围绕产品易用性、用户体验、操作便捷性进行了更新迭代&#xff0c;同时重磅更新了体验中心。用更加匹配项目及业务需求的Smartbi&#xff0c;帮助企业真正发挥数据的价值&#xff0c;赋能决策经营与管理。 Smartbi用户…

端到端拥塞控制的本质

昨天整理了一篇 bbr 的微分方程组建模(参见 bbr 建模)&#xff0c;算是 bbr 算法终极意义上的一个总结&#xff0c;最后也顺带了对 aimd 的描述&#xff0c;算是我最近比较满意的一篇分享了。那么接下来的问题&#xff0c;脱离出具体算法&#xff0c;上升到宏观层面&#xff0c…

百度“文心•跨模态大模型”又有新动态,支持内容分析时输出自定义标签库

大模型真正的价值在于应用。 一、基本概念 AI大模型具有强大的表征学习能力&#xff0c;能够在海量数据中提取有用的特征&#xff0c;为各种复杂任务提供解决方案。例如GPT-4o、BERT等模型的出现&#xff0c;不仅展示了大规模参数和复杂计算结构的优势&#xff0c;还在自然语…