EventBus源码分析(三):post方法发布事件【获取事件的所有订阅者,反射调用订阅者事件处理方法】(2.4版本)
来源:互联网 发布:2016淘宝女装销售排行 编辑:程序博客网 时间:2024/06/05 20:23
EventBus源码分析(一):入口函数提纲挈领(2.4版本)
EventBus源码分析(二):register方法保存事件的订阅者列表(2.4版本)
EventBus源码分析(三):post方法发布事件【获取事件的所有订阅者,反射调用订阅者事件处理方法】(2.4版本)
EventBus源码分析(四):线程模型分析(2.4版本)
EventBus源码解读详细注释(1)register的幕后黑手
EventBus源码解读详细注释(2)MainThread线程模型分析
EventBus源码解读详细注释(3)PostThread、MainThread、BackgroundThread、Async四种线程模式的区别
EventBus源码解读详细注释(4)register时刷新的两个map
EventBus源码解读详细注释(5)事件消息继承性分析 eventInheritance含义
EventBus源码解读详细注释(6)从事件发布到事件处理,究竟发生了什么!
EventBus维护了一个重要的HashMap,这个HashMap的键是事件,值是该事件的订阅者列表,因此post事件的时候就能够从此HashMap中取出事件的订阅者列表,对每个订阅者反射调用事件处理方法。
private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
post方法:发布者线程事件排队
/** Posts the given event to the event bus. */ public void post(Object event) { PostingThreadState postingState = currentPostingThreadState.get(); List<Object> eventQueue = postingState.eventQueue; eventQueue.add(event); if (!postingState.isPosting) { postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper(); 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发布事件的线程多样性,可能是主线程,可能是线程池分配的线程,也可能是临时非配的一个线程。EventBus发布事件和处理事件可能会线程切换,因此需要记录发布者线程的状态。
PostingThreadState:记录发布者线程状态
PostingThreadState类正是记录了发布者的状态
/** For ThreadLocal, much faster to set (and get multiple values). */ final static class PostingThreadState { final List<Object> eventQueue = new ArrayList<Object>(); boolean isPosting; boolean isMainThread; Subscription subscription; Object event; boolean canceled; }
可见PostingThreadState为给个线程设置了一个事件队列,一个事件,一个封装好的订阅者Subscription对象和三个标志位(最主要的信息就是发布者线程是否为主线程,随后会根据发布者线程是否为主线程决策是否要线程切换)。
Java中为每个线程设置不同的数据使用到的是ThreadLocal,EventBus通过ThreadLocal为每个线程创建一个PostingThreadState对象并保存发布者的状态。
private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() { @Override protected PostingThreadState initialValue() { return new PostingThreadState(); } };
post方法中首先获取了发布者线程的PostingThreadState对象,获取该线程对象的事件队列,并把这次发布的事件添加到发布者线程的事件队列中。
PostingThreadState postingState = currentPostingThreadState.get(); List<Object> eventQueue = postingState.eventQueue; eventQueue.add(event);
然后通过循环,取出发布者线程的事件队列的每一个事件,进行发布,一直到队列为空。
如果正在循环处理事件队列,进行事件发布,那么发布者线程的isPosting属性为真,这时候再次调用post发布事件,只会进队列,不会再次循环,因为之前的循环会处理队列中的事件,包括后来入队列的事件。事件队列此时为一个生产者消费者模型,EventBus消费事件,用户通过post生产事件。
postingState.isPosting = true;
因此post发布事件的时候,会先获取发布者线程的信息(最重要的线程信息是发布者线程是否为主线程isMainThread),然后对发布者线程发布的每一个事件进行排队,通过postSingleEvent方法对队列中的每一个事件进行处理,postSingleEvent方法需要知道发布者线程是否为主线程,通过传入发布者线程的ThreadLocal的PostingThreadState对象即可。
postSingleEvent:事件继承性分析
如果post(A),A extends B implements C
那么onEvent(A)、onEvent(B)、onEvent(C)这三个个事件处理方法那些能得到调用呢
答案是onEvent(A)、onEvent(B)、onEvent(C)这三个事件处理方法都会得到调用。
EventBus有个属性eventInheritance专门用来控制是否要处理事件的父类,默认值是true,表示默认是要处理事件父类的。
postSingleEvent对是否要处理事件的父类进行了分类讨论。
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error { Class<?> eventClass = event.getClass(); boolean subscriptionFound = false; 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) { Log.d(TAG, "No subscribers registered for event " + eventClass); } if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class && eventClass != SubscriberExceptionEvent.class) { post(new NoSubscriberEvent(this, event)); } } }
如果不需要处理事件父类的话,那么直接调用postSingleEventForEventType对当前的事件进行发布处理即可。
否则,需要找到当前发布事件的所有父类(包括实现的接口),对父类也通过postSingleEventForEventType方法进行处理。
如何获取一个类的所有父类和实现的接口
... private static final Map<Class<?>, List<Class<?>>> eventTypesCache = new HashMap<Class<?>, List<Class<?>>>(); ... /** Looks up all Class objects including super classes and interfaces. Should also work for interfaces. */ private List<Class<?>> lookupAllEventTypes(Class<?> eventClass) { synchronized (eventTypesCache) { List<Class<?>> eventTypes = eventTypesCache.get(eventClass); if (eventTypes == null) { eventTypes = new ArrayList<Class<?>>(); Class<?> clazz = eventClass; while (clazz != null) { eventTypes.add(clazz); addInterfaces(eventTypes, clazz.getInterfaces()); clazz = clazz.getSuperclass(); } eventTypesCache.put(eventClass, eventTypes); } return eventTypes; } }
EventBus对于缓存的设计是非常的精辟准确。获取一个类的所有的父类和实现的接口这一操作,是比较耗费性能的,为了更快的获取,采用缓存策略,以空间换时间,缓存同样通过静态的HashMap实现。
lookupAllEventTypes方法中同样是先获取缓存,只有在缓存不命中的情况下才进行查找,查找的过程就是向上循环迭代,遇到根类Object停止迭代 。
对应所实现的接口的处理,则是对每一层,进行递归获取
/** Recurses through super interfaces. */ static void addInterfaces(List<Class<?>> eventTypes, Class<?>[] interfaces) { for (Class<?> interfaceClass : interfaces) { if (!eventTypes.contains(interfaceClass)) { eventTypes.add(interfaceClass); addInterfaces(eventTypes, interfaceClass.getInterfaces()); } } }
postSingleEventForEventType:获取事件的订阅者列表,对每个订阅者进行事件处理
最开始EventBus维护的事件到订阅者列表的map终于派上用场了,从该map中获取待发布事件的所有订阅者,对每个订阅者都要进行事件处理。
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; }
postSingleEventForEventType方法首先在同步代码块中获取事件的订阅者列表
CopyOnWriteArrayList<Subscription> subscriptions; synchronized (this) { subscriptions = subscriptionsByEventType.get(eventClass); }
对每一个订阅者进行处理的方法需要传入一个发布者线程的信息,其实EventBus只关心发布者线程是否为主线程,因为根据不同的线程模型可能会线程切换。
for (Subscription subscription : subscriptions) { ... postToSubscription(subscription, event, postingState.isMainThread); ... }
postToSubscription:线程切换
在postToSubscription方法中,EventBus根据事件的线程模式和发布者线程是否为主线程,在规定的线程中对事件处理方法进行反射调用
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) { switch (subscription.subscriberMethod.threadMode) { case PostThread: invokeSubscriber(subscription, event); break; case MainThread: if (isMainThread) { invokeSubscriber(subscription, event); } else { mainThreadPoster.enqueue(subscription, event); } break; case BackgroundThread: 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); } }
- PostThread模式不需线程切换,直接在发布者线程进行事件处理。
- MainThread模式分类讨论:发布者线程是主线程则直接调用事件处理方法,否则通过Handler进行线程切换。
- BackgroundThread模式分类讨论:发布这线程不是主线程则在发布者线程直接处理事件,否则线程切换至线程池处理。
- Async模式不关心发布者线程直接在线程池中开辟一个新的线程处理事件。
具体的线程模型分析将会在下一篇进行详细分析。
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); } }
这里通过invoke方法对事件处理方法进行了反射调用。invoke方法会可能会抛出三个异常,这里只处理了两个。IllegalArgumentException异常并没有捕获,因为在保存事件处理方法的时候已经做了约束,保存的事件处理方法肯定只有一个参数,因此肯定不会抛出非法参数异常。
- EventBus源码分析(三):post方法发布事件【获取事件的所有订阅者,反射调用订阅者事件处理方法】(2.4版本)
- EventBus源码分析(二):register方法保存事件的订阅者列表(2.4版本)
- 发布 / 订阅的事件总线(eventBus)
- Guava EventBus源码分析(二):注册订阅者方法
- EventBus优化发布订阅事件详解
- Android EventBus发布/订阅事件总线
- Android EventBus发布/订阅事件总线
- Android发布订阅事件总线EventBus
- 使用反射取消所有事件订阅
- js事件编程的发布/订阅模式(一对一关系)
- android中EventBus框架(订阅事件总线)
- 在事件发布端移除所有事件订阅
- C#、最简单、事件委托、订阅事件、订阅者模式
- nodejs事件发布/订阅Emitter的使用
- 基于EventAggregator的事件发布及订阅
- 【C#】Event事件的订阅和发布
- JS事件发布和订阅的原理
- 基于EventAggregator的事件发布及订阅
- BZOJ4636 蒟蒻的数列
- ubuntu及linuxmint下电子词典首选:GoldenDict
- Struts小总结
- eclipse超级实用快捷键
- 我的实习(第一天)
- EventBus源码分析(三):post方法发布事件【获取事件的所有订阅者,反射调用订阅者事件处理方法】(2.4版本)
- 调用startActivityForResult,onActivityResult无响应的问题
- 一款清新、简约的PopupWindow
- Android四大组件之ContentProvider(下)
- MariaDB 数据库操作大全
- 全选选中或取消全选
- dvwa --Upload and use C99.php Backdoor shell
- CentOS 7源码安装mysql和配置mysql
- Android利用sharedpreferences缓存对象和数组数据