EventBus的不完全解析 EventBus的使用
1 EventBus.getDefault().register(context);
1 2 3 4 @Subscribe(sticky = false) public void getSubMsg (MsgEvent msg) { System.out.println("I got msg :" +msg); }
1 EventBus.getDefault().post(new MsgEvent());
1 EventBus.getDefault().postSticky(new MsgEvent());
流程图解
EventBus的流程 getDefault getDefault是一个获取EventBus单例的方法。EventBus内部采用了是双重校验锁 的单例模式。
1 2 3 4 5 6 7 8 9 10 11 12 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模式。
1 2 3 4 5 6 7 8 9 10 11 12 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) 注册方法如下:
1 2 3 4 5 6 7 8 9 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中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 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:只是为了看的清晰些而分开,并没有其他目的,其实代码是连在一起的。😀)。这里也可以先不关注,等到黏性事件解析的时候再看,没影响。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 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方法。
1 2 3 4 5 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创建的变量只能被当前线程访问,其他线程则无法访问和修改。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 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,说明没有注册呗。错误处理~
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 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操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 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); } }
要么放入一个Poster中,要么直接执行invokeSubscriber方法。每个Poster也是一个Runnable,将event传入Poster后最终调用的也是invokeSubscriber方法,所以来看invokeSubscriber。
1 2 3 4 5 6 7 8 9 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 粘性事件的处理。
1 2 3 4 5 6 7 public void postSticky (Object event) { synchronized (stickyEvents) { stickyEvents.put(event.getClass(), event); } post(event); }
代码很简单,将eventClass和event存在stickyEvents中,然后和post一样就行了。
就这?为什么能产生黏性效果?
请回过头来看上文subscribe 中的第二部分 👆。
如果你记性还不错的话,还记得黏性事件会跳到一个叫checkPostStickyEventToSubscription的方法,然后跳转到了postToSubscription方法,那么恭喜你,postStickt的流程也走完了。
完结撒花~