这篇文章将会为大家梳理一下EventBus的基本流程,本人使用的版本号为3.1.1,为了方便阅读,文章中的源码部分将省略部分有关异常捕获与日志相关代码。
使用示例
首先,按照官方的文档来看看一个最简单的EventBus示例是什么样的:
第一步:定义消息实体类
1 | public class MessageEvent { |
第二步:使用注解创建订阅方法
1 |
|
第三步:注册与注销订阅(注意与生命周期的绑定)
1 |
|
第四步:发送通知消息
1 | EventBus.getDefault().post(new MessageEvent("Hello everyone!")); |
这样,一个简单的Demo就完成了,本文将以这个Demo为基础,分析订阅事件从注册到接收并执行都是如何实现的。
1.EventBus.getDefault().register(this);
EventBus.getDefault()
1 | static volatile EventBus defaultInstance; |
获取EventBus的单例对象,很标准的单例模式,就不细说了。
EventBus.register(Object subscriber)
1 | public void register(Object subscriber) { |
SubscriberMethodFinder.findSubscriberMethods(Class<?> subscriberClass)
1 | List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) { |
从这个方法可以看出EventBus会将注册的订阅事件以Class对象为key,订阅方法的Method对象为value存入METHOD_CACHE缓存中,避免同一个类多次注册订阅时重复解析的问题,提升解析的性能。
在注解解析方面,EventBus提供了传统的反射解析与使用Subscriber Index两种方式,下面将主要对反射解析方式分别进行分析
SubscriberMethodFinder.findUsingReflection(Class<?> subscriberClass)
1 | private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) { |
在这段代码中出现了一个FindState类对象,其中含有解析相关的配置,解析出的方法等等,整个解析的过程都是围绕着这个对象进行处理。
FindState.checkAdd
1 | final Map<Class, Object> anyMethodByEventType = new HashMap<>(); |
这部分代码的作用主要有两点:
- 当一个类重载了父类的一个订阅方法,在向上级父类遍历时跳过父类中的这个方法的订阅,也就是说以子类的订阅方法为准
- 允许一个类有多个方法名不同的方法对同个事件进行订阅
值得一提的是关于methodClassOld.isAssignableFrom(methodClass)这一句代码,有文章提到因为之前的代码中会使用Class.getMethods()方法,会得到这个类以及父类的public方法,所以这句代码的结果可能为true,但是经我测试,如果父类方法被子类重载,那么getMethods方法得到的只会有子类的重载方法一个,父类的方法并不会出现,所以从这方面来说,这句代码依然只能是false。
EventBus.register(Object subscriber, SubscriberMethod subscriberMethod)
1 | private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) { |
到现在为止,其实已经可以总结出EventBus中订阅者注册的核心逻辑了,就是筛选出注册的类中带有Subscribe注解的方法,然后将其解析为SubscriberMethod对象,以订阅的事件类型为key,SubscriberMethod对象为value的形式存入subscribedEvents中。
对于粘性事件的发送,其实与之后的一般事件执行逻辑相同,这里就不再深入了。
EventBus.getDefault().post(new MessageEvent(“Hello everyone!”))
EventBus.post(Object event)
1 | public void post(Object event) { |
在这段代码中,isMainThread()方法可以用来判断消息发送是否是在主线程中。其中的逻辑非常简单,通过Looper类中的getMainLooper()获取到主线程中的Looper对象,然后再通过Looper类中的myLooper()方法获取当前线程的Looper对象,然后相互比较。
EventBus.postSingleEvent(Object event, PostingThreadState postingState)
1 | private void postSingleEvent(Object event, PostingThreadState postingState) throws Error { |
EventBus.postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass)
1 | private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) { |
这个方法的主要作用就是获得消息的订阅事件列表,完成对于postingState中参数的赋值,其中对于所有的订阅事件,使用的其实是同一个PostingThreadState对象,只是对于不同的订阅事件,会改变其中的部分参数的值。
EventBus.postToSubscription(Subscription subscription, Object event, boolean isMainThread)
1 | private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) { |
在默认情况下,EventBus会在调用线程中直接执行invokeSubscriber方法调用订阅事件,但当调用线程与期望的执行线程不一致或希望异步调用时,使用Poster来进行不同线程间的调度,每一个Poster中都会持有一个消息队列,并在指定线程执行。
EventBus.invokeSubscriber(Subscription subscription, Object event)
1 | void invokeSubscriber(Subscription subscription, Object event) { |
这段代码也就是订阅事件执行部分的最后一段代码了,可以看到逻辑非常的简单,就是通过反射来执行相应的订阅事件。
总结
在上文的分析中可以看出,EventBus的主要逻辑非常简单,核心流程就是注册时先解析出带有相应注解的方法,然后将其的Method对象与其所订阅的消息类型绑定加入到一个集合中,然后发送消息时获取到消息类型所对应的订阅事件的Method对象,通过反射来调用执行。