EventBus
是为Android定制的发布/订阅总线
它可以简化组件之间的通信,解耦事件发送者和接受者,可以很好的与Activity、Fragment、后台线程协同工作,避免了复杂的依赖和生命周期相关的问题
广播稍显麻烦,并且传递对象时需要序列化
用法
基本
自定义事件类
1 | public class MessageEvent { |
注册和注销订阅者
1 | EventBus.getDefault().register(this); |
添加处理函数
1 | (threadMode = ThreadMode.MAIN) |
EventBus3
中处理函数可以任意取名,通过@Subscribe
注解标记
threadMode
:线程模式:
ThreadMode.POSTING
发送事件和接收事件在同一个线程ThreadMode.MAIN
事件处理在UI线程进行ThreadMode.BACKGROUND
如果是UI线程发送,则新建子线程执行,如果本身是后台线程发送,就在该线程执行ThreadMode.ASYNC
无论从哪个线程发送,都在新线程执行
sticky
:粘性事件,表示在订阅之前发布的事件订阅后也能收到
priority
:优先级,默认为0,越大优先级越高
发送事件
1 | EventBus.getDefault().post(new MessageEvent()); |
发送粘性事件
1 | EventBus.getDefault().postSticky(new MessageEvent()); |
配置注解处理器获得索引
修改build.gradle
buildscript {
dependencies {
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}
}
apply plugin: 'com.neenbedankt.android-apt'
dependencies {
apt 'org.greenrobot:eventbus-annotation-processor:3.0.1'
}
apt {
arguments {
// 这个就是生成的索引类的名字
eventBusIndex 'com.github.lakeshire.eventbusdemo.MyEventBusIndex'
}
}
编译期生成的类是这样的
1 | /** This class is generated by EventBus, do not edit. */ |
然后设置默认的EventBus
1 | EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus(); |
用下来,注册时间用反射时是8ms,用索引时是2ms,再次注册是1ms,区别很明显
源码
EventBus
提供一个默认的单例实现,也可以通过Builder自定义
1 | // 事件类型缓存 |
有几个重要的数据容器
注册
1 | public void register(Object subscriber) { |
首先通过一个订阅方法查找器找到对应这个订阅者的一组方法,查找器内部会保存一份方法缓存表,如果已存在直接返回,没有的话就要实际查找,有反射和索引两种查找方式,索引在编译期通过注解处理器获得,速度要比反射快
1 | private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) { |
- 查看
subscriptionsByEventType
中对应EventType
的列表,根据优先级把新的订阅者添加进去 - 查看
typesBySubscriber
中对应Subscriber
的列表,添加新的EventType
- 处理粘性事件,从粘性事件列表中获取对应
EventType
的事件,做一次发送
这里就是发送粘性事件的时机,所以注册前发送的粘性事件再注册后可以接收到,粘性事件列表维护的是事件类型-事件(不是列表)对,发送后不会主动移除掉,所以再次注册还是会发送
注销
就是清理subscriptionsByEventType
和typesBySubscriber
这两个列表
发送事件
1 | public void post(Object event) { |
发送是向线程本地变量里维护的队列中添加一个事件,如果没有在发送则发送队列头的事件
postSingleEventForEventType()
里在subscriptionsByEventType
中查找对应EventType
的订阅者,有的话就对每个订阅者发送
1 | private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) { |
这个就是发送的核心部分,根据线程模式决定做法,跟线程模式的定义匹配:
POSTING
直接调用订阅者的方法MAIN
判断是否在主线程,在主线程直接调用,否则放进主线程的消息队列BACKGROUND
判断是否在主线程,在主线程则放进后台线程的消息队列,否则直接调用ASYNC
直接放进后台线程的队列
EventBus
内有三个Handler
:
mainThreadPoster
持有UI线程的消息循环,发消息给UI线程并在上面处理backgroundPoster
和asyncPoster
都是在EventBus
内的线程池上执行
invokeSubscriber
就是调用订阅方法
发送粘性事件
1 | public void postSticky(Object event) { |
很简单,把事件设置到粘性事件列表的对应项,然后正常发送
事件继承
postSingleEvent()
中出现的代码段
1 | if (eventInheritance) { |
如果支持事件继承的话,需要查找对应事件类的所有EventType
,包括接口类和基类,然后全部发送一遍(可能某些场景有用)
注解处理器
参见生成的类,就是收集所有订阅者的处理函数并结构化形成索引