EventBus的使用
- 注册
EventBus.getDefault().register(context);
- 定义subscriber
@Subscribe(sticky = false)
public void getSubMsg(MsgEvent msg){
System.out.println("I got msg :"+msg);
}
- 发送消息
EventBus.getDefault().post(new MsgEvent());
- 发送黏性消息
EventBus.getDefault().postSticky(new MsgEvent());
流程图解
EventBus的流程
getDefault
getDefault
是一个获取EventBus单例的方法。EventBus内部采用了是双重校验锁的单例模式。
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;
}
EventBus()
构造方法中初始化了许多需要用到的变量,看名字是用到了Build模式。
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
public EventBus() {
this(DEFAULT_BUILDER);
}
EventBus(EventBusBuilder builder) {
...
mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
backgroundPoster = new BackgroundPoster(this);
asyncPoster = new AsyncPoster(this);
}
- register(Object subscriber)
注册方法如下:
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
获取传入的这个
subscriber
对象的Class对象。通过
findSubscriberMethods
获取其内部的所有被@Subscribe
标记过的Method
。遍历这些方法,依次调用
subscribe(subscriber, subscriberMethod);
subscriberMethod
是内部的一个类,里面有method、threadMode、Class<?> eventType、boolean sticky等信息。在findSubscriberMethods
中就已经都写进去了。
subscribe
subscribe分为两部分。
第一部分
前一部分对传入的subscriberMethod对象进行分类存储。
-
准确的说是以eventType为类别分开,存到一个个的List中。即
Map< eventType , ArrayList<Subscription> >
,也是下面代码中subscriptionsByEventType
的类型;eventType
是@Subscribe
标记的方法中传入的类型,一般是我们自定义的MsgEvent
;这整个方法都是在一个遍历中的,当前
eventType
对应的ArrayList
叫newSubscription
。 将
newSubscription
根据优先级排序。换个维度再分类一次。即
Map< Subscription, ArrayList<eventType> >
(对比第一步,就是换了分类的维度),存储在subscribedEvents
中。
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
-----------------------------------1.以eventType分类--------------------------------------------------------
Class<?> eventType = subscriberMethod.eventType;
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
if (subscriptions.contains(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event " + eventType);
}
}
-----------------------------------2.排序--------------------------------------------------------
int size = subscriptions.size();
for (int i = 0; i <= size; i++) {
if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
subscriptions.add(i, newSubscription);
break;
}
}
------------------------------------3.以subscriber分类----------------------------------------------
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
------------------------------------4.如果是黏性事件-------------------------------------------
...以下为第二部分代码...
}
第二部分
第二部分主要是对粘性事件的处理,代码紧接上一部分。(ps:只是为了看的清晰些而分开,并没有其他目的,其实代码是连在一起的。😀)。这里也可以先不关注,等到黏性事件解析的时候再看,没影响。
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
....
------------------------------------4.如果是黏性事件-------------------------------------------
if (subscriberMethod.sticky) {
if (eventInheritance) {
//如果是父类继承下来的方法
Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
for (Map.Entry<Class<?>, Object> entry : entries) {
Class<?> candidateEventType = entry.getKey();
if (eventType.isAssignableFrom(candidateEventType)) {
Object stickyEvent = entry.getValue();
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
stickyEvents
是一个Map,Key为黏性post传递对象的Class,value为这个传递的对象。
- 如果是父类继承下来的,.............那么....emm...这里我也没搞懂,以后弄懂了再补上😂。反正还是会调到
checkPostStickyEventToSubscription
方法。 - 否则,直接调用
checkPostStickyEventToSubscription
方法。
private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
if (stickyEvent != null) {
postToSubscription(newSubscription, stickyEvent, isMainThread());
}
}
postToSubscription
中就是按照线程分别执行invoke
方法。所以可以看到如果是粘性事件,那么直接把存放在stickyEvents
中的方法给执行完事了。因为黏性事件post方法中传递的对象就放在stickyEvents
中的。合情合理。
- post
post方法执行完以后,所有被@subscribe
标记的方法都会被执行。又因为上文register中已经对所有标记过的方法分类好了,所以按常理,post中要做的事情就是遍历出这些方法,然后一一invoke就好了。
不过真实代码没有这么简单粗暴,我们来看下。
currentPostingThreadState
是一个ThreadLocal
,里面存了PostingThreadState
类。
ThreadLocal
是一个关于创建线程局部变量的类。通常情况下,我们创建的变量是可以被任何一个线程访问并修改的。而使用
ThreadLocal
创建的变量只能被当前线程访问,其他线程则无法访问和修改。
/** Posts the given event to the event bus. */
public void post(Object event) {
-----------------------------------------1.登记-------------------------------------
PostingThreadState postingState = currentPostingThreadState.get();
List<Object> eventQueue = postingState.eventQueue;
eventQueue.add(event);
----------------------------------2.安检,一次一个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()) {
-------------------------------3.一切检查完成后,下一步-------------------------
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
刚进到方法里去,门口一个老大爷就要登记。于是,这些方法都被记录到了一个叫
eventQueue
的List中去。isPosting
状态标记当前是否正在发送消息中,一开始为false,进去后改为true,避免两个事件同时进去执行了。记录线程,看有没有canceled(返回)。做完一些常规检查,一切正常后执行postSingleEvent(eventQueue.remove(0), postingState);
postSingleEvent
不要忘了我们进来这个方法是为了干嘛,是要执行所有被标记过的eventType分类下的方法。所以要怎么做?
- 先把存储的
eventTypes
罗列出来,用lookupAllEventTypes
方法。 - 再亮出自己的身份,和eventTypes一一对比,如果相同则
postSingleEventForEventType
。准确的说是先postSingleEventForEventType
,然后返回结果,true表示匹配成功,false表示匹配失败。无论有没有成功都会接着下一次postSingleEventForEventType
直到遍历完所有的eventType
。 - 遍历完了还是false,说明没有注册呗。错误处理~
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
if (eventInheritance) {
------------------------------------1.罗列出所有eventTypes---------------------------
List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
int countTypes = eventTypes.size();
for (int h = 0; h < countTypes; h++) {
Class<?> clazz = eventTypes.get(h);
-------------------------2.直接执行方法,返回true表示信息发出去了----------------------------------
subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
}
} else {
subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
}
--------------------------------3.错误处理-------------------------------------
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
将一系列信息录入到postingState
中,然后执行postToSubscription
方法。不列源码了,直接上postToSubscription
。
postToSubscription
终于看到调用的方法了😂,按照不同的线程进行不同的invoke操作。
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);
}
}
要么放入一个Poster中,要么直接执行invokeSubscriber
方法。每个Poster也是一个Runnable
,将event传入Poster后最终调用的也是invokeSubscriber
方法,所以来看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);
}
}
没错,这正是我们所期盼的😂。和我们想的一样,最终是调用到了method.invoke
,将这些标记过的方法一一执行。
- postSticky
粘性事件的处理。
public void postSticky(Object event) {
synchronized (stickyEvents) {
stickyEvents.put(event.getClass(), event);
}
// Should be posted after it is putted, in case the subscriber wants to remove immediately
post(event);
}
代码很简单,将eventClass
和event
存在stickyEvents
中,然后和post一样就行了。
就这?为什么能产生黏性效果?
请回过头来看上文subscribe中的第二部分👆。
如果你记性还不错的话,还记得黏性事件会跳到一个叫checkPostStickyEventToSubscription
的方法,然后跳转到了postToSubscription
方法,那么恭喜你,postStickt的流程也走完了。
完结撒花~