EventBus使用和分析

1,076 阅读19分钟

参考文章:

EventBus: Events for Android - Open Source by greenrobot

Android主流三方库源码分析(九、深入理解EventBus源码)

EventBus设计之禅

EventBus源码解析

一篇讲明白EventBus

EventBus 3.0 源码分析

零 解决的问题

EventBus是一种用于Android的事件发布-订阅总线,由GreenRobot开发,Gihub地址是:EventBus。它简化了应用程序内各个组件之间进行通信的复杂度,尤其是Fragment、Activity或ViewModel之间进行通信的问题,可以避免由于使用广播通信而带来的诸多不便。

  • 广播:耗时、容易被捕获(不安全)。
  • 事件总线:更节省资源、更高效,能将信息传递给原生以外的各种对象。

一 简介

1. 观察者模式

EventBus是基于观察者模式扩展而来的,观察者模式又可称为发布 - 订阅模式,它定义了对象间的一种1对多的依赖关系,每当这个对象的状态改变时,其它的对象都会接收到通知并被自动更新。

观察者模式有以下角色:

  • 抽象被观察者:将所有已注册的观察者对象保存在一个集合中。
  • 具体被观察者:当内部状态发生变化时,将会通知所有已注册的观察者。
  • 抽象观察者:定义了一个更新接口,当被观察者状态改变时更新自己。
  • 具体被观察者:实现抽象观察者的更新接口。

EventBus的观察者模式和一般的观察者模式不同,它使用了扩展的观察者模式对事件进行订阅和分发,其实这里的扩展就是指的使用了EventBus来作为中介者,抽离了许多职责,如下是它的官方原理图:

image.png

2. 三种角色

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

3. 四种线程模型

  • POSTING:默认,表示事件处理函数的线程跟发布事件的线程在同一个线程。
  • MAIN:表示事件处理函数的线程在主线程(UI)线程,因此在这里不能进行耗时操作。
  • BACKGROUND:表示事件处理函数的线程在后台线程,因此不能进行UI操作。如果发布事件的线程是主线程(UI线程),那么事件处理函数将会开启一个后台线程,如果发布事件的线程是在后台线程,那么事件处理函数就使用该线程。
  • ASYNC:表示无论事件发布的线程是哪一个,事件处理函数始终会新建一个子线程运行,同样不能进行UI操作

4. 事件类型和优先级

  • 普通事件:指已有的事件订阅者能够收到事件发送者发送的事件,在事件发送之后注册的事件接收者将无法收到事件。发送普通事件可以调用EventBus.getDefault().post()方法进行发送。
  • 粘性事件:不管是在事件发送之前注册的事件接收者还是在事件发送之后注册的事件接收者都能够收到事件。这里对于普通事件的区别之处在于事件接收处需要定义事件接收类型,它可以通过@Subscribe(threadMode = xxx, sticky = true)的方式进行声明;在事件发送时需要调用EventBus.getDefault().postSticky()方法进行发送。否则事件类型默认为普通事件。
  • 优先级:在同一传递线程ThreadMode中,优先级较高的订阅者将在优先级较低的其他订阅者之前接收事件。默认优先级为0。注意:优先级不影响具有不同ThreadMode的订阅服务器之间的传递顺序!

二 使用

1. 引入依赖

implementation 'org.greenrobot:eventbus:3.1.1'

2. 定义事件

可以是任意类型

data class MsgEvent(val msg: String, val code: Int) 

3. 注册订阅者,编写响应方法,解除注册

在3.0版本中,通过@Subscribe注解,来确定运行的线程threadMode,是否接受粘性事件sticky,以及事件优先级priority,

override fun onCreate(savedInstanceState: Bundle?) { 

    super.onCreate(savedInstanceState) 

    setContentView(R.layout.activity_main) 

    EventBus.getDefault().register(this) 

}



@Subscribe(threadMode = ThreadMode.BACKGROUND, sticky = true, priority = 100)

fun onHandlerMsg(event: MsgEvent){ 

     Log.e(TAG, "MainActivity # onHandlerMsg ${event.msg} - ${event.code}") 

}



override fun onDestroy() {

    super.onDestroy() 

    EventBus.getDefault().unregister(this) 

}

4. 发布事件

EventBus.getDefault().post(MsgEvent("普通事件",1))

EventBus.getDefault().postSticky(MsgEvent("粘性事件"2))

5. 注意

每次在register之后,都必须进行一次unregister,因为register是强引用,它会让对象无法得到内存回收,导致内存泄露。所以必须在unregister方法中释放对象所占的内存。

EventBus2.x的版本与EventBus3.x的版本的区别:

  • EventBus2.x使用的是运行时注解,它采用了反射的方式对整个注册的类的所有方法进行扫描来完成注册,因而会对性能有一定影响。
  • EventBus3.x使用的是编译时注解,Java文件会编译成.class文件,再对class文件进行打包等一系列处理。在编译成.class文件时,EventBus会使用EventBusAnnotationProcessor注解处理器读取@Subscribe()注解并解析、处理其中的信息,然后生成Java类来保存所有订阅者的订阅信息。这样创建出了对文件或类的索引关系,并将其编入到apk中。
  • 从EventBus3.0开始使用了对象池缓存减少了创建对象的开销。

三 源码分析

调用层级 ← 对应 → 标题层级

几个变量和类

  • METHOD_CACHE:Map<Class<?>, List> 类型。键为注册类的 Class,值为该类中所有 EventBus 回调的方法链表(也就是被 @Subscribe 标记的方法们)。
  • Subscription 类(订阅信息):关注类中两个字段,一个是 Object 类型的 subscriber,该字段即为注册的对象(在 Android 中时常为 Activity);另一个是 SubscriberMethod 类型的 subscriberMethod,细节如下。
  • subscriberMethod:SubscriberMethod 类型(文中称订阅方法)。关注类中有个字段 eventType 是 Class<?> 类型,代表 Event 的类类型。
  • typesBySubscriber:Map<Object, List<Class<?>>> 类型。键为对象本身(注意:并非其 Class 对象),值为该对象中所有的 Event 的类型链表。该字段只用于仅用于判断某个对象是否注册过,在日常使用中几乎没什么作用。
  • subscribtionsByEventType:Map<Class<?>, CopyonWriteArrayList> 类型。键为 Event 类型,值为元素为 Subscription(订阅信息)链表。核心字段。

1.EventBus.getDefault()

从获取EventBus实例的方法getDefault()开始

public static EventBus getDefault() {

    if (defaultInstance == null) {

        synchronized (EventBus.class) {

            if (defaultInstance == null) {

                defaultInstance = new EventBus();

            }

        }

    }

    return defaultInstance;

}

在getDefault()中使用了双重校验并加锁的单例模式来创建EventBus实例。

查看EventBus的默认构造方法:

private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();

public EventBus() {

    this(DEFAULT_BUILDER);

}

在EventBus的默认构造方法中又调用了它的另一个有参构造方法,将一个类型为EventBusBuilder的DEFAULT_BUILDER对象传递进去了。这里的EventBusBuilder很明显是一个EventBus的构造器,以便于EventBus能够添加自定义的参数和安装自定义的默认EventBus实例。

看一下EventBusBuilder的构造方法:

public class EventBusBuilder {

    ...
    EventBusBuilder() {

    }
    ...
}

EventBusBuilder的构造方法中什么也没有做,那继续查看EventBus的有参构造方法:

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

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

private final Map<Class<?>, Object> stickyEvents;

EventBus(EventBusBuilder builder) { 

    //日志 
    logger = builder.getLogger(); 

    // 1
    //这个集合可以根据事件类型获取订阅者 
    //key:事件类型,value:订阅该事件的订阅者集合 
    subscriptionsByEventType = new HashMap<>(); 

    // 2
    //订阅者所订阅的事件集合 
    //key:订阅者,value:该订阅者订阅的事件集合 
    typesBySubscriber = new HashMap<>(); 

    // 3
    //粘性事件集合 
    //key:事件Class对象,value:事件对象 
    stickyEvents = new ConcurrentHashMap<>(); 

    // 4
    //Android主线程处理事件 
    mainThreadSupport = builder.getMainThreadSupport(); 
    mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null; 

    //Background事件发送者 
    backgroundPoster = new BackgroundPoster(this); 
    //异步事件发送者 
    asyncPoster = new AsyncPoster(this); 
    indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0; 

    // 5
    //订阅者订阅事件查找对象 
    subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes, builder.strictMethodVerification, builder.ignoreGeneratedIndex); 

    //从builder取中一些列订阅相关信息进行赋值
    logSubscriberExceptions = builder.logSubscriberExceptions; 
    logNoSubscriberMessages = builder.logNoSubscriberMessages; 
    sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent; 
    sendNoSubscriberEvent = builder.sendNoSubscriberEvent; 
    throwSubscriberException = builder.throwSubscriberException;  
    //是否支持事件继承
    eventInheritance = builder.eventInheritance;
    // 6
    // 默认的线程池对象 
    executorService = builder.executorService; 

}
  • 注释1处,创建了一个subscriptionsByEventType对象,它是一个类型为HashMap的subscriptionsByEventType对象,并且其key为 Event 类型,value为 Subscription链表。这里的Subscription是一个订阅信息对象,它里面保存了两个重要的字段,一个是类型为 Object 的 subscriber,该字段即为注册的对象(在 Android 中时通常是 Activity对象);另一个是 类型为SubscriberMethod 的 subscriberMethod,它就是被@Subscribe注解的订阅方法,里面保存了一个重要的字段eventType,它是 Class<?> 类型的,代表了 Event 的类型。

  • 注释2处,新建了一个类型为 Map 的typesBySubscriber对象,它的key为subscriber对象,value为subscriber对象中所有的 Event 类型链表,日常使用中仅用于判断某个对象是否注册过。

  • 注释3处,新建了一个类型为ConcurrentHashMap的stickyEvents对象,专用于粘性事件处理的一个字段,key为事件的Class对象,value为当前的事件。

  • 注释4处,新建了三个不同类型的事件发送器:

    • mainThreadPoster:主线程事件发送器,通过它的mainThreadPoster.enqueue(subscription, event)方法可以将订阅信息和对应的事件进行入队,然后通过 handler 去发送一个消息,在 handler 的 handleMessage 中去执行方法。
  • backgroundPoster:后台事件发送器,通过它的enqueue() 将方法加入到后台的一个队列,最后通过线程池去执行,它在 Executor的execute()方法 上添加了 synchronized关键字并设立了控制标记flag,保证任一时间只且仅能有一个任务会被线程池执行。
  • asyncPoster:实现逻辑类似于backgroundPoster,但是backgroundPoster保证任一时间只且仅能有一个任务会被线程池执行的特性,而asyncPoster则是异步运行,可以同时接收多个任务。
  • 注释5处,新建了一个subscriberMethodFinder对象,这是从EventBus中抽离出的订阅方法查询的一个对象。
  • 注释6处,从builder中取出了一个默认的线程池对象,它由Executors的newCachedThreadPool()方法创建,它是一个有则用、无则创建、无数量上限的线程池。

2. regist(this)

 public void register(Object subscriber) { 

     //首先获得订阅者的class对象 

     Class<?> subscriberClass = subscriber.getClass(); 

     // 1
     //通过subscriberMethodFinder来找到订阅者订阅了哪些事件.返回一个SubscriberMethod对象
     //的List,SubscriberMethod里包含了这个方法的Method对象,
     //以及将来响应订阅是在哪个线程的ThreadMode,以及订阅的事件类型eventType,
     //以及订阅的优先级priority,以及是否接收粘性sticky事件的boolean值. 
     List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass); 

     synchronized (this) { 

         for (SubscriberMethod subscriberMethod : subscriberMethods) { 
             // 2 订阅 
             subscribe(subscriber, subscriberMethod); 
         } 
     } 
}
  • 注释1处,根据当前注册类获取 subscriberMethods这个订阅方法列表 。
  • 注释2处,使用了增强for循环令subsciber对象对subscriberMethods 中每个 SubscriberMethod 进行订阅。

2.1 findSubscriberMethods(subscriberClass)

接着查看SubscriberMethodFinder的findSubscriberMethods()方法:

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {

    // 1 先从METHOD_CACHE取看是否有缓存,key:保存订阅类的类名,value:保存类中订阅的方法数据,
    List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);

    if (subscriberMethods != null) {
        return subscriberMethods;
    }

    // 2 是否忽略注解器生成的MyEventBusIndex类
    if (ignoreGeneratedIndex) {
        //利用反射来读取订阅类中的订阅方法信息
        subscriberMethods = findUsingReflection(subscriberClass);
    } else {
        //从注解器生成的MyEventBusIndex类中获得订阅类的订阅方法信息
        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缓存
        METHOD_CACHE.put(subscriberClass, subscriberMethods);
        return subscriberMethods;
    }
}
  • 注释1处,如果缓存中有subscriberClass对象对应的订阅方法列表,则直接返回。
  • 注释2处,ignoreGeneratedIndex字段, 它用来判断是否使用生成的 APT 代码去优化寻找接收事件的过程,如果开启了的话,那么将会通过 subscriberInfoIndexes 来快速得到接收事件方法的相关信息。如果我们在项目中接入了EventBus 的 APT,那么可以将 ignoreGeneratedIndex 字段设为 false 以提高性能。这里ignoreGeneratedIndex 默认为false,所以会执行findUsingInfo()方法,后面生成 subscriberMethods 成功的话会加入到缓存中,失败的话会抛出异常。

2.1.1 findUsingInfo()

接着查看SubscriberMethodFinder的findUsingInfo()方法:

private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {

    // 1 从数组中获取FindState对象
    //如果有直接返回,如果没有创建一个新的FindState对象
    FindState findState = prepareFindState();
    findState.initForSubscriber(subscriberClass);

    // 2 根据事件订阅者初始化findState
    while (findState.clazz != null) {
        //获取subscriberInfo,初始化为null
        findState.subscriberInfo = getSubscriberInfo(findState);
        if (findState.subscriberInfo != null) {
            SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
            for (SubscriberMethod subscriberMethod: array) {
                if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
                    findState.subscriberMethods.add(subscriberMethod);
                }
            }
        } else {
             // 3 通过反射的方式获取订阅者中的Method
             findUsingReflectionInSingleClass(findState);
        }
        findState.moveToSuperclass();
    }
    // 4
    return getMethodsAndRelease(findState);
}
2.1.1.1 prepareFindState() -> FindState

注释1处,调用了SubscriberMethodFinder的prepareFindState()方法创建了一个新的 FindState 类:

private static final FindState[] FIND_STATE_POOL = new FindState[POOL_SIZE];

private FindState prepareFindState() {

    // 1 从 FIND_STATE_POOL 即 FindState 池中取出可用的 FindState(这里的POOL_SIZE为4)
    synchronized(FIND_STATE_POOL) {
        for (int i = 0; i < POOL_SIZE; i++) {
            FindState state = FIND_STATE_POOL[i];
            if (state != null) {
                FIND_STATE_POOL[i] = null;
                return state;
            }
        }
    }
    // 2 直接新建 一个新的 FindState 对象。
    return new FindState();
}

FindState类:

static class FindState {
    ....
    void initForSubscriber(Class<?> subscriberClass) {
        this.subscriberClass = clazz = subscriberClass;
        skipSuperClasses = false;
        subscriberInfo = null;
    }
    ...
}

FindState 是 SubscriberMethodFinder 的内部类,主要做一个初始化、回收对象等工作。

2.1.1.2 getSubscriberInfo(findState)
private SubscriberInfo getSubscriberInfo(FindState findState) {
    if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) {
        SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();
        if (findState.clazz == superclassInfo.getSubscriberClass()) {
            return superclassInfo;
        }
    }
    if (subscriberInfoIndexes != null) {
        for (SubscriberInfoIndex index: subscriberInfoIndexes) {
            SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
            if (info != null) {
                return info;
            }
        }
    }
    return null;
}

在前面初始化的时候,findState的subscriberInfo和subscriberInfoIndexes 这两个字段为空,所以这里直接返回 null。

2.1.1.3 findUsingReflectionInSingleClass(findState)
private void findUsingReflectionInSingleClass(FindState findState) {
    Method[] methods;
    try {
        // 订阅者中所有声明的方法,放入数组中
        methods = findState.clazz.getDeclaredMethods();
    } catch (Throwable th) {
        // 获取订阅者中声明的public方法,设置跳过父类
        methods = findState.clazz.getMethods();
        findState.skipSuperClasses = true;
    }
    //遍历这些方法
    for (Method method: methods) {
        //获取方法的修饰符:public、private等等
        int modifiers = method.getModifiers();
        //订阅方法为public同时不是abstract、static
        if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
            //方法参数类型数组
            Class<?> [] parameterTypes = method.getParameterTypes();
            if (parameterTypes.length == 1) {
                //获取方法的注解
                Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                //如果有注解
                if (subscribeAnnotation != null) {
                    // 重点
                    Class<?> eventType = parameterTypes[0];
                    //将method和eventType放入到findState进行检查
                    if (findState.checkAdd(method, eventType)) {
                        //获取注解中的threadMode对象
                        ThreadMode threadMode = subscribeAnnotation.threadMode();
                        //新建一个SubscriberMethod对象,同时加入到findState中
                        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");
        }
    }
}
  • 过反射的方式获取订阅者类中的所有声明方法,然后在这些方法里面寻找以 @Subscribe作为注解的方法进行处理。
  • 再经过一轮检查,看看 findState.subscriberMethods是否存在,如果没有,将方法名,threadMode,优先级,是否为 sticky 方法等信息封装到 SubscriberMethod 对象中,最后添加到 subscriberMethods 列表中。
2.1.1.4 getMethodsAndRelease(findState)
private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {
    // 1 获取订阅者所有订阅方法集合 
    List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods);
    // 2 findState进行回收
    findState.recycle();
    // 3
    synchronized(FIND_STATE_POOL) {
        for (int i = 0; i < POOL_SIZE; i++) {
            if (FIND_STATE_POOL[i] == null) {
                FIND_STATE_POOL[i] = findState;
                break;
            }
        }
    }

    // 4 返回集合
    return subscriberMethods;

}

从findState中获取订阅者所有方法并释放

  • 注释1处,从findState中取出了保存的subscriberMethods。
  • 注释2处,将findState里的保存的所有对象进行回收。
  • 注释3处,把findState存储在 FindState 池中方便下一次使用,以提高性能。
  • 注释4处,返回subscriberMethods。

接着,在EventBus的 register() 方法的最后会调用 subscribe 方法:

2.2 subscribe(subscriber, subscriberMethod)

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
    // 获取事件类型
    Class<?> eventType = subscriberMethod.eventType;
    // 封装Subscription对象
    Subscription newSubscription = new Subscription(subscriber, subscriberMethod)

    // 1 通过事件类型获取该事件的订阅者集合
    CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
    // 如果没有订阅者订阅该事件
    if (subscriptions == null) {
         // 创建集合,存入subscriptionsByEventType集合中
        subscriptions = new CopyOnWriteArrayList <> ();
        subscriptionsByEventType.put(eventType, subscriptions);
    } else {//如果有订阅者已经订阅了该事件
        // 判断这些订阅者中是否有重复订阅的现象
        if (subscriptions.contains(newSubscription)) {
            throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event " + eventType);
        }
    }
    int size = subscriptions.size();

    // 2 遍历该事件的所有订阅者
    for (int i = 0; i <= size; i++) {
        // 按照优先级高低进行插入,如果优先级最低,插入到集合尾部
        if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
            subscriptions.add(i, newSubscription);
            break;
        }
    }

    // 3 获取该事件订阅者订阅的所有事件集合
    List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
    if (subscribedEvents == null) {
        subscribedEvents = new ArrayList<>();
        typesBySubscriber.put(subscriber, subscribedEvents);
    }

    // 获取该事件订阅者订阅的所有事件集合
    subscribedEvents.add(eventType);
    // 4 判断该事件是否是粘性事件
    if (subscriberMethod.sticky) {
        if (eventInheritance) { // 判断事件的继承性,默认是不可继承
            // 获取所有粘性事件并遍历,判断继承关系
            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);
        }
    }
}
  • 注释1处,会根据 subscriberMethod的eventType,在 subscriptionsByEventType 去查找一个 CopyOnWriteArrayList ,如果没有则创建一个新的 CopyOnWriteArrayList,然后将这个 CopyOnWriteArrayList 放入 subscriptionsByEventType 中。
  • 注释2处,添加 newSubscription对象,它是一个 Subscription 类,里面包含着 subscriber 和 subscriberMethod 等信息,并且这里有一个优先级的判断,说明它是按照优先级添加的。优先级越高,会插到在当前 List 靠前面的位置。
  • 注释3处,对typesBySubscriber 进行添加,这主要是在EventBus的isRegister()方法中去使用的,目的是用来判断这个 Subscriber对象 是否已被注册过。
  • 注释4处,会判断是否是 sticky事件。如果是sticky事件的话,会调用 checkPostStickyEventToSubscription() 方法。

2.2.1 checkPostStickyEventToSubscription(newSubscription, stickyEvent)

private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
    if (stickyEvent != null) {
        // 如果粘性事件不为空
        postToSubscription(newSubscription, stickyEvent, isMainThread());
    }
}

最终是调用了postToSubscription()这个方法来进行粘性事件的发送,对于粘性事件的处理,稍后再分析,接下来看看事件是如何post的。

3. post()方法

public void post(Object event) {
    // 1 获取当前线程的PostingThreadState,这是一个ThreadLocal对象
    PostingThreadState postingState = currentPostingThreadState.get();
    // 当前线程的事件集合
    List <Object> eventQueue = postingState.eventQueue;
    // 将要发送的事件加入到集合中
    eventQueue.add(event);
    // 2 查看是否正在发送事件
    if (!postingState.isPosting) {
    // 判断是否是主线程
        postingState.isMainThread = isMainThread();
        postingState.isPosting = true;
        if (postingState.canceled) {
            throw new EventBusException("Internal error. Abort state was not reset");
        }
        try {
            // 只要事件集合中还有事件,就一直发送
            while (!eventQueue.isEmpty()) {
                postSingleEvent(eventQueue.remove(0), postingState);
            }
        } finally {
            postingState.isPosting = false;
            postingState.isMainThread = false;
        }
    }
}
  • 注释1处,这里的currentPostingThreadState 是一个 ThreadLocal 类型的对象,里面存储了 PostingThreadState,而 PostingThreadState 中包含了一个 eventQueue 和其他一些标志位,相关的源码如下:
private final ThreadLocal <PostingThreadState> currentPostingThreadState = new ThreadLocal <PostingThreadState> () {

@Override

protected PostingThreadState initialValue() {
    return new PostingThreadState();
}
};


final static class PostingThreadState {
    final List <Object> eventQueue = new ArrayList<>();
    boolean isPosting;
    boolean isMainThread;
    Subscription subscription;
    Object event;
    boolean canceled;

}

接着把传入的 event,保存到了当前线程中的一个变量 PostingThreadState 的 eventQueue 中.

  • 注释2处,最后调用了 postSingleEvent() 方法,我们继续查看这个方法:

3.2 postSingleEvent(Object event, PostingThreadState postingState)

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {

    // 获取事件的Class对象

    Class<?> eventClass = event.getClass();

    boolean subscriptionFound = false;

    // 1

    if (eventInheritance) {  // eventInheritance一般为true

        // 2 找到当前的event的所有父类和实现的接口的class集合

        List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);

        int countTypes = eventTypes.size();

        for (int h = 0; h < countTypes; h++) {

            Class<?> clazz = eventTypes.get(h);

            // 遍历集合发送单个事件

            subscriptionFound |=

            // 3

            postSingleEventForEventType(event, postingState, clazz);

        }

    } else {

        subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);

    }

    if (!subscriptionFound) {

       if (logNoSubscriberMessages) { 

           logger.log(Level.FINE, "No subscribers registered for event " + eventClass); 

       } if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class && 

                   eventClass != SubscriberExceptionEvent.class) { 

           post(new NoSubscriberEvent(this, event)); 

       }

    }

}
  • 注释1处,首先取出 Event 的 class 类型,接着会对 eventInheritance 标志位 判断,它默认为true,如果设为 true 的话,它会在发射事件的时候判断是否需要发射父类事件,设为 false,能够提高一些性能。
  • 注释2处,会调用lookupAllEventTypes() 方法,它的作用就是取出 Event 及其父类和接口的 class 列表,当然重复取的话会影响性能,所以它也做了一个 eventTypesCache 的缓存,这样就不用重复调用 getSuperclass() 方法。
  • 注释3处,会调用postSingleEventForEventType()方法,我们看下这个方法:
3.2.1 postSingleEventForEventType(Object event, PostingThreadState postingState, Class <?> eventClass)
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class <?> eventClass) {
    CopyOnWriteArrayList <Subscription> subscriptions;
    synchronized(this) {
        // 根据事件获取所有订阅它的订阅者
        subscriptions = subscriptionsByEventType.get(eventClass);
    }

    if (subscriptions != null && !subscriptions.isEmpty()) {
        // 遍历集合
        for (Subscription subscription: subscriptions) {
            postingState.event = event;
            postingState.subscription = subscription;
            boolean aborted = false;
            try {
                // 将事件发送给订阅者
                postToSubscription(subscription, event, postingState.isMainThread);
                aborted = postingState.canceled;
            } finally {
                // 重置postingState
                postingState.event = null;
                postingState.subscription = null;
                postingState.canceled = false;
            }
            if (aborted) {
                break;
            }
        }
        return true;
    }
    return false;
}

这里直接根据 Event 类型从 subscriptionsByEventType 中取出对应的 subscriptions对象,最后调用了 postToSubscription() 方法。

3.2.1.1 postToSubscription(subscription, event, postingState.isMainThread)
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
    // 根据订阅方法的线程模式调用订阅方法
    switch (subscription.subscriberMethod.threadMode) {
        case POSTING://默认类型,表示发送事件操作直接调用订阅者的响应方法,不需要进行线程间的切换
            invokeSubscriber(subscription, event);
            break;
        case MAIN: //主线程,表示订阅者的响应方法在主线程进行接收事件
            if (isMainThread) {//如果发送者在主线程
                invokeSubscriber(subscription, event);
            } else {//如果事件的发送者不是主线程
                //添加到mainThreadPoster的队列中去,在主线程中调用响应方法
                mainThreadPoster.enqueue(subscription, event);
            }
            break;
        case MAIN_ORDERED:// 主线程优先模式
            if (mainThreadPoster != null) {
                mainThreadPoster.enqueue(subscription, event);
            } else {
                //如果不是主线程就在消息发送者的线程中进行调用响应方法
                invokeSubscriber(subscription, event);
            }
            break;
        case BACKGROUND:
            if (isMainThread) {
                 // 如果事件发送者在主线程,加入到backgroundPoster的队列中,在线程池中调用响应方法
                backgroundPoster.enqueue(subscription, event);
            } else {
                // 如果不是主线程,在事件发送者所在的线程调用响应方法
                invokeSubscriber(subscription, event);
            }
            break;
        case ASYNC:
            //这里没有进行线程的判断,也就是说不管是不是在主线程中,都会在子线程中调用响应方法
            asyncPoster.enqueue(subscription, event);
            break;
        default:
            throw new IllegalStateException("Unknow thread mode: " + subscription.subscriberMethod.threadMode);
    }

}

这里通过threadMode 来判断在哪个线程中去执行方法:

  • POSTING:执行 invokeSubscriber() 方法,内部直接采用反射调用。
  • MAIN:首先去判断当前是否在 UI 线程,如果是的话则直接反射调用,否则调用mainThreadPoster的enqueue()方法,即把当前的方法加入到队列之中,然后通过 handler 去发送一个消息,在 handler 的 handleMessage 中去执行方法。
  • MAIN_ORDERED:主线程优先模式。
  • BACKGROUND:判断当前是否在 UI 线程,如果不是的话直接反射调用,是的话通过backgroundPoster的enqueue()方法 将方法加入到后台的一个队列,最后通过线程池去执行。注意,backgroundPoster在 Executor的execute()方法 上添加了 synchronized关键字 并设立 了控制标记flag,保证任一时间只且仅能有一个任务会被线程池执行。
  • ASYNC:逻辑实现类似于BACKGROUND,将任务加入到后台的一个队列,最终由Eventbus 中的一个线程池去调用,这里的线程池与 BACKGROUND 逻辑中的线程池用的是同一个,即使用Executors的newCachedThreadPool()方法创建的线程池,它是一个有则用、无则创建、无数量上限的线程池。不同于backgroundPoster的保证任一时间只且仅能有一个任务会被线程池执行的特性,这里asyncPoster则是异步运行的,可以同时接收多个任务。

4. unregister(this)

public synchronized void unregister(Object subscriber) {
    // 获取订阅者订阅的所有事件
    List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
    if (subscribedTypes != null) {
        // 遍历集合
        for (Class<?> eventType : subscribedTypes) {
             // 1 将该订阅者的从订阅该事件的所有订阅者集合中移除
            unsubscribeByEventType(subscriber, eventType);
        }

        // 2 将订阅者从集合中移除
        typesBySubscriber.remove(subscriber);
    } else {
        logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
    }
}
  • 注释1处,unsubscribeByEventType() 方法中对 subscriptionsByEventType 移除了该 subscriber 的所有订阅信息。
  • 注释2处,移除了注册对象和其对应的所有 Event 事件链表。

4.1 unsubscribeByEventType(Object subscriber, Class<?> eventType)

private void unsubscribeByEventType(Object subscriber, Class<?> eventType) { 

    // 获取该事件的所有订阅者 
    List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
    if (subscriptions != null) {
        int size = subscriptions.size(); 
        // 遍历集合 
        for (int i = 0; i < size; i++) { 
            Subscription subscription = subscriptions.get(i); 
            // 将订阅者从集合中移除 
            if (subscription.subscriber == subscriber) { 
                subscription.active = false; 
                subscriptions.remove(i); 
                i--; 
                size--; 
            } 
        } 
    }
}

5. 粘性事件 EventBus.getDefault.postSticky(new CollectEvent())

public void postSticky(Object event) {
    synchronized (stickyEvents) {
        // 1 将事件添加到粘性事件集合中
        stickyEvents.put(event.getClass(), event);
    }
    // 2 发送事件
    post(event);
}
  • 将粘性事件加入到EventBus对象的粘性事件集合中,当有新的订阅者进入后,如果该订阅者订阅了该粘性事件,可以直接发送给订阅者。
  • 将粘性事件发送给已有的事件订阅者。

前面我们在分析register()方法的最后部分时,其中有关粘性事件的源码如下:

if (subscriberMethod.sticky) {
    Object stickyEvent = stickyEvents.get(eventType);
    if (stickyEvent != null) {
        postToSubscription(newSubscription, stickyEvent, isMainThread());
    }
}

可以看到,在这里会判断当前事件是否是 sticky 事件,如果 是,则从 stickyEvents 中拿出该事件并执行 postToSubscription() 方法。

四 流程图

image.png

image.png

五 总结

  1. EventBus使用 FindState 复用池来复用 FindState 对象
  2. 在各处使用了 synchronized 关键字进行代码块同步的一些优化操作
  3. 代码中通过 ignoreGeneratedIndex 来判断是否使用生成的 APT 代码去优化寻找接收事件的过程,如果开启了的话,那么将会通过 subscriberInfoIndexes 来快速得到接收事件方法的相关信息。
  4. EventBus最核心的逻辑就是利用了 subscriptionsByEventType 这个重要的列表,将订阅对象,即接收事件的方法存储在这个列表,发布事件的时候在列表中查询出相对应的方法并执行。