- EventBus是如何实现事件的发送和线程切换问题?
- EventBus能实现进程间通信吗?
当我们调用EventBus.getDefault().post("TAG")
发送事件时,EventBus是如何保证事件发送到我们需要的线程中的。一切都要从post
说起:
public void post(Object event) {
//currentPostingThreadState是一个ThreadLocal对象,
PostingThreadState postingState = currentPostingThreadState.get();
List<Object> eventQueue = postingState.eventQueue;
//将事件添加到集合中
eventQueue.add(event);
//isPosting在有事件正在被处理时 isPosting == true
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;
}
}
}
post
的作用就是获取当前线程对应的PostingThreadState
对象,然后与集合中的事件一起发送出去。这个PostingThreadState
对象不得了,内部包含的变量与事件发送所在线程密切关联。
final static class PostingThreadState {
final List<Object> eventQueue = new ArrayList<>(); //当前线程用于存储事件的集合
boolean isPosting; //当前线程有事件在处理时isPosting=true,事件结束isPosting=false
boolean isMainThread; //前线程是不是主线程
Subscription subscription; //接收方的信息包装类
Object event; // 事件本身
boolean canceled; // 事件是否被删除的标志
}
获取到了PostingThreadState
对象后,会调用postSingleEvent
方法,该方法的用处在于筛选出发送事件的类型。
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
// 默认为true,表示考虑事件的层次结构
if (eventInheritance) {
List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
int countTypes = eventTypes.size();
for (int h = 0; h < countTypes; h++) {
Class<?> clazz = eventTypes.get(h);
//每一种类型,发送一次事件
subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
}
} else {
//直接发送事件
subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
}
......
}
默认情况下eventInheritance=true
,表示考虑事件的层次结构,也就是说会按照事件的类型、实现的接口类型、超类那类型以及实现的接口来单独发送事件。当然我们也可以设置eventInheritance=false
表示只考虑事件本身类型。
既然上面线程、事件类型都考虑了,下面也应该是事件具体接收了:
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;
try {
// 根据接收方法来回调
postToSubscription(subscription, event, postingState.isMainThread);
aborted = postingState.canceled;
} finally {
//重置参数
postingState.event = null;
postingState.subscription = null;
postingState.canceled = false;
}
if (aborted) {
break;
}
}
return true;
}
return false;
}
该方法用于获取事件对应的接收方法的具体信息,存储在subscriptions
集合中。这里的subscriptions
就对应着我们使用Subscribe
注解修饰的方法。最后调用postToSubscription
方法,不用说了,肯定得调用修饰的方法了。
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.enqueue(subscription, event);
}
break;
case MAIN_ORDERED: //主线程接收事件
if (mainThreadPoster != null) {
mainThreadPoster.enqueue(subscription, event);
} else {
// 一般不会走到这里
invokeSubscriber(subscription, event);
}
break;
case BACKGROUND: //后台接收事件
if (isMainThread) {
backgroundPoster.enqueue(subscription, event);
} else {
invokeSubscriber(subscription, event);
}
break;
case ASYNC: //单独的线程来接收事件
asyncPoster.enqueue(subscription, event);
break;
default:
throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
}
}
postToSubscription
方法中会采用Subscribe
注解设置的ThreadMode
来执行线程调度。当然最终的方法的调起还是会执行到invokeSubscriber
方法。
void invokeSubscriber(Subscription subscription, Object event) {
try {
subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
} catch (InvocationTargetException e) {
handleSubscriberException(subscription, event, e.getCause());
} catch (IllegalAccessException e) {
throw new IllegalStateException("Unexpected exception", e);
}
}
EventBus中线程调度的具体接口为Poster
,可以看到需要实现的方法enqueue
,需要执行线程调度的事件会有一个入队的操作。
interface Poster {
void enqueue(Subscription subscription, Object event);
}
Poster
具体的实现类有三种:
- HandlerPoster 主线程切换类,内部持有主线程的Looper对象,采用Handler切换线程。
- BackgroundPoster 采用EventBus中设置的线程池来执行事件,会延迟1s执行。
- AsyncPoster 采用EventBus中设置的线程池来执行事件。
到这里最开始的第一个问题就有了答案
1. EventBus是如何实现事件的发送和线程切换问题?
EventBus中通过使用Subscribe
注解设置的ThreadMode
来执行线程调度,分为以下几种情况:
- POSTING 事件订阅者和发布者所在线程一致。
- MAIN 如果发布者位于主线程则直接回调,否则内部通过Handler来切换到主线程。
- MAIN_ORDERED 不区分发布者所在线程,内部通过Handler来切换到主线程。
- BACKGROUND 如果发布者在子线程,则直接回调,否则通过EventBus内部线程池切换到子线程执行。
- ASYNC 通过EventBus内部线程池切换到子线程执行。
2. EventBus能实现进程间通信吗?
很干脆,不能。