EventBus
EventBus is a publish/subscribe event bus for Android and Java.
EventBus是适用于Android和Java的发布/订阅事件总线。
优点:
- 简化组件之间的通信
- 将事件发送方和接收方解耦
- 在活动、片段和后台线程中表现良好
- 避免复杂且容易出错的依赖关系和生命周期问题
- 使代码更简单
- 速度快
- 很小(~60k jar)
- 在实践中被安装量超过 1,000,000,000 的应用证明
- 具有高级功能,如交付线程,订阅者优先级等。
1 简单使用
1.1 定义事件
class MessageEvent(val msg:String)
1.2 定义订阅
class HomeActivity : AppCompatActivity() {
override fun onStart() {
super.onStart()
EventBus.getDefault().register(this)
}
override fun onStop() {
super.onStop()
EventBus.getDefault().unregister(this)
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun onMessageEvent(messageEvent: MessageEvent){
val msg = messageEvent.msg
when(msg){
"day"->{
//...
}
}
}
}
1.3 发送事件
EventBus.getDefault().post(MessageEvent("day"))
2 主要原理
2.1 角色
-
Event(事件):自定义事件,EventBus会根据事件的类型全局发送
-
Subscriber(订阅者):主要指需要接收事件的 Activitys 和 Fragments 。在 Activitys 和 Fragments 的生命周期中订阅/注销订阅,接收事件的方法以注解
@subscribe
进行标示,同时指定线程模型,线程模型默认为POSTING
的方式。 -
Publisher(发布者):事件的发布者。可以在任意线程中发布事件。利用
EventBus.getDefault()
获取一个全局静态对象EventBus,再通过链式编程调用post()将不同类型的事件发送。
2.2 线程模型
ThreadMode(线程模型)描述事件订阅者和事件发布者所在线程的关系。
ThreadMode主要有5种模式:POSTING、MAIN、MAIN_ORDERED、BACKGROUND、ASYNC(异步)
-
POSTING
事件订阅的默认线程。即事件订阅和事件发布在同一个线程。避免了切换线程的开销。对于已知在非常短的时间内完成而不需要主线程的简单任务,这是推荐的模式。使用此模式的事件处理程序必须快速返回,以避免阻塞可能是主线程的发布线程。
-
MAIN
事件处理程序方法在Android 的主线程(UI线程)被调用。使用该模式的订阅器必须快速执行, 避免阻塞主线程
-
如果发布线程是主线程,则直接调用事件处理方法(即上方的onMessageEvent()),此时为同步调用,阻塞主线程,与POSTING效果一样
-
否则,事件将排队等待传递(非阻塞)。
-
-
MAIN_ORDERED
事件处理程序方法在Android 的主线程(UI线程)被调用。与MAIN模式不同,事件将始终排队等待传递(非阻塞)。
使用该模式的订阅器也必须快速执行, 避免阻塞主线程。
MAIN与MAIN_ORDERED线程模式区别:
MAIN :事件处理程序中发布另一个事件,则第二个事件处理程序将在第一个事件处理程序之前完成(因为它是同步调用的)。
MAIN_ORDERED:第一个事件处理程序将完成,然后第二个事件处理程序将在稍后的时间点(一旦主线程具有容量)被调用。
-
BACKGROUND
事件处理程序方法在后台线程被调用。
-
发布线程不是主线程,直接在发布线程调用事件处理方法
-
发布线程是主线程,EventBus 将使用单个后台线程,该线程将按顺序传递其所有事件。使用此模式的事件处理程序应尝试快速返回,以避免阻塞后台线程。
-
-
ASYNC(异步)
事件处理程序方法在单独的线程中调用。始终独立于发布线程和主线程。直接在单独的线程调用事件处理程序方法,不等待。
如果事件处理程序方法的执行可能需要一些时间(例如,用于网络访问),则应使用此模式。避免同时触发大量长时间运行的异步处理程序方法来限制并发线程数。EventBus 使用线程池来有效地重用已完成的异步事件处理程序通知中的线程。
2.3 事件类型
-
普通事件
普通事件是指已有的订阅者能够收到发送的事件,在事件发送之后注册的事件接收者将无法收到事件。
//发送普通事件 EventBus.getDefault().post(new MessageEvent("day"));
-
粘性事件
粘性事件是指,不管是在事件发送之前注册的事件接收者还是在事件发送之后注册的事件接收者都能够收到事件。默认为普通事件。
//发送粘性事件 EventBus.getDefault().postSticky(new MessageEvent("Hello everyone!")); //事件处理 @Subscribe(sticky = true, threadMode = ThreadMode.MAIN) public void onEvent(MessageEvent event) { textField.setText(event.message); }
事件优先级
订阅者优先级以影响事件传递顺序。在同一传递线程ThreadMode
中,优先级较高的订阅者将在优先级较低的其他订阅者之前接收事件。默认优先级为0
。
注意:优先级不影响具有不同ThreadMode
的订阅服务器之间的传递顺序
//定义事件接收的优先级
@Subscribe(priority = 1);
public void onEvent(MessageEvent event) {
...
}
3 源码解析
1 初始化 getDafault()
public class EventBus {
static volatile EventBus defaultInstance;
public static EventBus getDefault() {
EventBus instance = defaultInstance;
if (instance == null) {
synchronized (EventBus.class) {
instance = EventBus.defaultInstance;
if (instance == null) {
instance = EventBus.defaultInstance = new EventBus();
}
}
}
return instance;
}
}
采用单例模式的DCL式创建全局单例对象EventBus
EventBus的构造函数:
//EventBus.java
public class EventBus {
//EventBuilder对象,初始化其内部属性
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
//事件类型缓存
//key-事件Class对象,value-事件父类型及本类型
private static final Map<Class<?>, List<Class<?>>> eventTypesCache = new HashMap<>();
//根据不同事件类型存储订阅者: subscriptionsByEventType
//key-事件Class对象,value-订阅者集合
private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
//不同订阅者的事件集合: typesBySubscriber
//key-具体订阅者对象,value-事件Class集合
private final Map<Object, List<Class<?>>> typesBySubscriber;
//粘性事件集合: stickyEvents
//key-事件Class对象,value-具体事件对象
private final Map<Class<?>, Object> stickyEvents;
//主线程事件发送支持(含判断是否主线程,创建主线程事件发送者)
private final MainThreadSupport mainThreadSupport;
private final Poster mainThreadPoster;//主线程事件发送者
private final BackgroundPoster backgroundPoster;//后台线程事件发送者
private final AsyncPoster asyncPoster;//异步线程事件发送者
//订阅事件查找:根据订阅者Class,找到订阅的事件集合
private final SubscriberMethodFinder subscriberMethodFinder;
private final ExecutorService executorService;
public EventBus() {
//传入静态EventBuilder对象
this(DEFAULT_BUILDER);
}
EventBus(EventBusBuilder builder) {
//日志
logger = builder.getLogger();
subscriptionsByEventType = new HashMap<>();
typesBySubscriber = new HashMap<>();
stickyEvents = new ConcurrentHashMap<>();
mainThreadSupport = builder.getMainThreadSupport();
mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
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;
}
//...
}
2 注册订阅 register(Object subscriber)
//EventBus.java
public void register(Object subscriber) {
//1. 反射获得订阅者的Class对象
Class<?> subscriberClass = subscriber.getClass();
//2. 通过subscriberMethodFinder找到订阅者所订阅的事件集合
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
//3. 遍历集合进行注册
subscribe(subscriber, subscriberMethod);
}
}
}
- SubscriberMethodFinder.findSubscriberMethods(subscriberClass):根据订阅者Class找到订阅者订阅的事件集合
//SubscriberMethodFinder.java
private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>();
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
//1. 从缓存获取
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
//2. 获取订阅者自定义的订阅方法集合
if (ignoreGeneratedIndex) {
//a. findUsingReflection
subscriberMethods = findUsingReflection(subscriberClass);
} else {
//b. findUsingInfo
subscriberMethods = findUsingInfo(subscriberClass);
}
if (subscriberMethods.isEmpty()) {
throw new EventBusException("Subscriber " + subscriberClass
+ " and its super classes have no public methods with the @Subscribe annotation");
} else {
//3. 放入缓存
METHOD_CACHE.put(subscriberClass, subscriberMethods);
return subscriberMethods;
}
}
a. findUsingReflection
//SubscriberMethodFinder.java
private List<SubscriberMethod> findUsingReflection(Class<?