是什么
EventBus is a publish/subscribe event bus for Android and Java.
EventBus 是基于发布-订阅模式(也叫观察模式)的事件总线框架。在Android开发中,常用于组件之间的通信与数据传输,可以有效的进行组件之间的解耦。例如,Activity之间、与Fragment的参数传递,事件通知等。
有什么特点
使用简单,如果这也算一个优点的话。相比LocalBroadcastManager使用是真的简单。
有五种线程模式,支持订阅方法执行在指定的线程。
可以指定订阅者的优先级,按优先级处理事件,还可以拦截事件。
3.0版本之前使用的运行时的反射收集事件的订阅方法,有一定的性能损耗,但3.0之后使用了Apt技术,编译时期就完成了订阅事件的收集。
有什么缺点
- 没有生命周期感知的功能
注册和反注册需要成对的出现,如果忘记反注册可能会导致内存泄漏的问题发生,特别是在Activity、Fragment中使用时。毕竟这个框架出现的时间可比LifeCycle出现的早很多了。
- 不克制使用的话,会造成大量事件的满天飞,同时也导致POJO类的膨胀。
需要为Event事件专门定义java类,针对数据传输的场景,难以复用Event。
简单的使用
使用方式就不介绍了,参见官网:http://greenrobot.org/eventbus/
是如何运行的
基于3.6.0版本进行源码分析。
基本概念
- Event :事件,分为普通事件和粘性事件(StickyEvent),通常定义为一个键的pojo类。
- Publisher:发布者,使用post方法发布事件。
- Subscriber:订阅者,订阅关心的Event,在指定的线程执行。
先贴出一下EventBus类中的几个关键的缓存map
1 | public class EventBus { |
注册订阅者
入口在Eventbus的register
1 | public void register(Object subscriber) { |
收集事件订阅
拿到subscriber
的class,使用subscriberMethodFinder
找出当前class以及父类下的所有订阅方法,跟一下findSubscriberMethods
方法
1 | List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) { |
先看一下HEAD_CACHE
的定义
1 | // key:订阅者类,values:某一事件的所有处理方法的集合 |
查找时,先从METHOD_CACHE
缓存中查找,在METHOD_CACHE
缓存没有命中时,3.0之前的版本使用反射的方式查找,3.0的版本冲Apt时期生成的索引类中的查找。找到是放入METHOD_CACHE
中。
在进行重复注册与反注册的场景,可以有效避免反复查找的问题,比如,一个事件只在Activity
可见阶段才感兴趣的情况,就会在onResume
生命周期进行注册,在onPause
的生命周期进行反注册。这个空间换时间的缓存机制,就可以避免二次查找。
3.0之前反射方式
看一下3.0之前的反射方式的,主要跟一下findUsingReflection
方法
1 | private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) { |
使用迭代的方式,收集订阅者以及父类中的订阅方法,直到找到系统类是退出迭代。
FindState
用于存放查找过程中的各种现相关信息,使用了对象池,避免频繁的对象创建,又再一次用空间换时间,同时也可以避免频繁的GC,造成内存抖动,引起卡顿。
该类的部分源码
1 | static class FindState { |
之后就到了findUsingReflectionInSingleClass
方法。见名知意。
1 | private void findUsingReflectionInSingleClass(FindState findState) { |
找到@Subscribe
注解的非static
的、public
的,有且仅有一个参数的方法,包装到SubscriberMethod
中,解析ThreadMode、优先级priority、是否是粘性事件。
其中findState.checkAdd(method, eventType)
,处理了子类和父类中,是否存在订阅方法重新的问题。
1 | //检查了子类是否有重写重复的方法。有的话需要使用之类的方法。 |
3.0之后APT技术
使用EventBusAnnotationProcessor
在编译器,遍历所有的类 ,搜集订阅者与订阅方法,最终生成一个实现SubscriberInfoIndex
的类,然后在EventBus初始化是调用addIndex方法使用。
使用findUsingInfo查找所有订阅者的订阅方法,思路和3.0之前的版本一直,但是多了一个降级策略.
1 | private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) { |
粘性事件分发
通过步骤 1, 拿到所有的事件订阅方法,然后进行订阅
1 | synchronized (this) { |
1 | private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) { |
粘性事件分发
有粘性事件是在注册阶段就判断了是否有响应的订阅者,有的话就checkPostStickyEventToSubscription
最终调用到postToSubscription
,普通事件的分发最终也是通过postToSubscription
,该方法 ,具体的放到后面一起分析
事件发布与分发
核心逻辑,就是找到事件的订阅者,执行相应的订阅方法,
1 | //6 发送event |
currentPostingThreadState
是一个ThreadLocal变量,它提供线程本地变量,如果创建一个ThreadLocal变量,那么访问这个变量的每个线程都会有这个变量的一个副本,在实际多线程操作的时候,操作的是自己本地内存中的变量,从而规避了线程安全问题。通常EventBus都是process-wide范围的。
1 | final static class PostingThreadState { |
遍历eventQueue,从数组头部开始分发,postSingleEvent
之后调用到postSingleEventForEventType
,取出所有的events(继承的情况),之后再到postSingleEventForEventType
,从缓存中找到所有的订阅者与订阅方法的包装类,使用CopyOnWriteArrayList存放subscriptions,避免遍历时对Suscriber的反注册引起的问题。
1 | // 6.1 发送单个event,已经找到了eventClass |
遍历分发,最终到postToSubscription
1 | // 7. 分发 event 到 suscription, |
ThreadMode
中的POSTING
、MAIN
、MAIN_ORDERED
、BACKGROUND
,都可能阻塞post的所在线程,最好不要执行耗时操作。
mainThreadPoster
底层使用Android
的Handler
进行分发,backgroundPoster
、asyncPoster
都是依赖同一个Executors.newCachedThreadPool()
进行分发,被分发之后在执行时都是通过invokeSubscriber
进行调用。
其中backgroundPoster
内部也使用了一个链表queue,使事件按post的顺序执行。
取消注册
总结
框架整体考虑的非常的完善,各种级别的缓存,用空间换时间,避免二次查询,多处使用对象池,避免对象的频繁创建,通过DCL单例提供EventBus对象,使用Builder模式,优化使用大量参数进行初始化。