Android必知必会EventBus源码分析之发布事件
2019-03-11 本文已影响18人
GitCode8
知道了EventBus的注册流程后,我们来了解一下EventBus发布事件的流程。如果还没看过注册流程,建议先浏览:
Android必知必会的EventBus之使用篇
Android必知必会的EventBus源码分析之注册
我们知道EventBus的发布事件一行代码解决
EventBus.getDefault().post(new MessageEvent());
MessageEvent
是我们在其他地方订阅的事件类型。接着我们看看post
方法。
/** Posts the given event to the event bus. */
public void post(Object event) {
//通过ThreadLocal获取当前线程的状态
PostingThreadState postingState = currentPostingThreadState.get();
//获取当前线程的事件队列
List<Object> eventQueue = postingState.eventQueue;
//将我们发送的类型事件添加到队列中
eventQueue.add(event);
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;
}
}
}
通过上面代码,我们知道EventBus的post
方法通过本地线程ThreadLocal
去获取事件队列。并将我们发布的事件类型添加到队列中。在队列eventQueue
不为空的情况,调用postSingleEvent
方法。我们看看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);
}
if (!subscriptionFound) {
if (logNoSubscriberMessages) {
logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
}
if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
eventClass != SubscriberExceptionEvent.class) {
post(new NoSubscriberEvent(this, event));
}
}
}
通过上面代码,我们知道最终会调用postSingleEventForEventType
。
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 = false;
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存放着事件的订阅类和订阅方法。让我们看看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 {
// temporary: technically not correct as poster not decoupled from subscriber
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);
}
}
通过代码可以发现,EventBus
的线程切换在此次。至于不同参考EventBus的使用这里我们先看看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);
}
}
因此,我们知道,在注册过程中,通过注解和反射机制,将相关的订阅类和方法包装到了Subscription
。在此次,切换线程后,再调用Subscription
中的订阅方法。到这里,发布事件流程就结束了。
至于黏性事件的发布,原理应该差不多,大家可以自行查阅。