EventBus源码分析(二):register方法保存事件的订阅者列表(2.4版本)
2016-07-04 11:11
417 查看
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维护了两个重要的map:
事件到订阅者列表的map,发布事件的时候可以根据事件获取订阅者列表,这样可以逐一反射调用订阅者的事件处理方法。
订阅者到事件列表的map,这样订阅者向EventBus解除注册(unregister)的时候可以根据订阅者获取该订阅者订阅的所有事件,对每个事件分别解除注册。
因此register注册订阅者的时候,EventBus会通过反射寻找订阅者订阅的所有事件,更新上述两个map。如果是粘性sticky事件,在订阅的时候就取出保存的sticky事件直接发送,这样就做到了发布者先发布事件,之后订阅者订阅事件,接收订阅之前发布的粘性事件。
可以看到register主要做了两件事:
找到订阅者订阅的所有事件
对每个事件分别订阅,更新map
findSubscriberMethods方法传入订阅者的Class对象,返回该订阅者订阅的所有事件处理方法列表。
该方法代码比较长,分段进行阅读。
因为反射查找事件处理方法比较耗费性能,因此我们加入缓存机制,通过静态HashMap保存订阅者的事件处理方法。
先查找缓存,缓存命中的话直接返回事件处理方法列表,否则通过反射重新查找获取。
clazz.getSuperclass()方法会返回clazz父类的Class对象,如果clazz代表的类是根类Object、原生类型、接口或者Void则该方法返回null,因此EventBus不仅会保存当前订阅者订阅的事件,还会保存订阅者的父类订阅的事件。
为了性能考虑,直接跳过包名为java、javax和androd开头的类,因为这些类是系统类,我们不会订阅系统类为订阅者,因此不需处理系统类。
接下来通过EventBus约定的规则去寻找事件处理方法
事件处理方法必须是onEvent开头
只能是public的非抽象(abstract)、非静态(static)方法
事件处理方法只能有一个参数
经过上述重重过滤,找到了事件处理方法,然后根据事件处理方法的命名规则,判定处理该事件的线程模型
由此可知:
onEvent方法的线程模型是PostThread,在发布者线程处理事件
onEventMainThread方法的线程模型是MainThread,在主线程处理事件
onEventBackgroundThread方法的线程模型是BackgroundThread
onEventAsync方法的线程模型是Async
BackgroundThread和Async线程模型的区别随后会专门研究讨论,此处不表。
接着以”事件处理方法名>事件类型“为key,加入到一个set中
eventTypesFound这个set的主要作用是去重,如果子类覆写了父类的事件处理方法,那么EventBus只保存子类的事件处理方法,父类的事件处理方法就像覆写的意义那样,被覆盖掉。
如果订阅者没有订阅事件,也就是说没有找到事件处理方法,那么抛出异常。
如果找到了该订阅者的所有事件处理方法,在返回之前,要添加缓存。
subscribe方法实际上是更新两个map:
事件到订阅者列表的map,发布事件的时候可以根据事件获取订阅者列表,这样可以逐一反射调用订阅者的事件处理方法。
订阅者到事件列表的map,这样订阅者向EventBus解除注册(unregister)的时候可以根据订阅者获取该订阅者订阅的所有事件,对每个事件分别解除注册。
首先根据事件从map中获取该事件的订阅者列表,根据参数创建一个Subscription对象(封装了订阅者,事件处理方法和优先级)
如果该事件没有订阅者列表的话,就新建一个订阅者列表添加进map
否则判断是否为重复订阅,是的话抛出异常。
接着根据新的订阅者的优先级将新订阅者插入订阅者列表的合适位置
可见优先级数值越大,越靠近列表的前部。
因此订阅者列表是按照优先级从大到小排队的。
接着更新另外一个map,获取该订阅者订阅的所有事件
最后判断是否为sticky事件,是的话就读取保存的sticky事件发布出去
事件的发布将在下一篇中继续分析。
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维护了两个重要的map:
事件到订阅者列表的map,发布事件的时候可以根据事件获取订阅者列表,这样可以逐一反射调用订阅者的事件处理方法。
订阅者到事件列表的map,这样订阅者向EventBus解除注册(unregister)的时候可以根据订阅者获取该订阅者订阅的所有事件,对每个事件分别解除注册。
因此register注册订阅者的时候,EventBus会通过反射寻找订阅者订阅的所有事件,更新上述两个map。如果是粘性sticky事件,在订阅的时候就取出保存的sticky事件直接发送,这样就做到了发布者先发布事件,之后订阅者订阅事件,接收订阅之前发布的粘性事件。
private synchronized void register(Object subscriber, boolean sticky, int priority) { List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass()); for (SubscriberMethod subscriberMethod : subscriberMethods) { subscribe(subscriber, subscriberMethod, sticky, priority); } }
可以看到register主要做了两件事:
找到订阅者订阅的所有事件
对每个事件分别订阅,更新map
通过反射找到订阅者订阅的所有事件
在register方法中,要更新两个map,必须首先获取订阅者订阅的所有事件。List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass)
findSubscriberMethods方法传入订阅者的Class对象,返回该订阅者订阅的所有事件处理方法列表。
该方法代码比较长,分段进行阅读。
... private static final Map<String, List<SubscriberMethod>> methodCache = new HashMap<String, List<SubscriberMethod>>(); ... String key = subscriberClass.getName(); List<SubscriberMethod> subscriberMethods; synchronized (methodCache) { subscriberMethods = methodCache.get(key); } if (subscriberMethods != null) { return subscriberMethods; }
因为反射查找事件处理方法比较耗费性能,因此我们加入缓存机制,通过静态HashMap保存订阅者的事件处理方法。
先查找缓存,缓存命中的话直接返回事件处理方法列表,否则通过反射重新查找获取。
while (clazz != null) { ... clazz = clazz.getSuperclass(); }
clazz.getSuperclass()方法会返回clazz父类的Class对象,如果clazz代表的类是根类Object、原生类型、接口或者Void则该方法返回null,因此EventBus不仅会保存当前订阅者订阅的事件,还会保存订阅者的父类订阅的事件。
String name = clazz.getName(); if (name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("android.")) { // Skip system classes, this just degrades performance break; }
为了性能考虑,直接跳过包名为java、javax和androd开头的类,因为这些类是系统类,我们不会订阅系统类为订阅者,因此不需处理系统类。
// Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again) Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods) { String methodName = method.getName(); if (methodName.startsWith(ON_EVENT_METHOD_NAME)) { int modifiers = method.getModifiers(); if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) { Class<?>[] parameterTypes = method.getParameterTypes(); if (parameterTypes.length == 1) { String modifierString = methodName.substring(ON_EVENT_METHOD_NAME.length()); ThreadMode threadMode; if (modifierString.length() == 0) { threadMode = ThreadMode.PostThread; } else if (modifierString.equals("MainThread")) { threadMode = ThreadMode.MainThread; } else if (modifierString.equals("BackgroundThread")) { threadMode = ThreadMode.BackgroundThread; } else if (modifierString.equals("Async")) { threadMode = ThreadMode.Async; } else { if (skipMethodVerificationForClasses.containsKey(clazz)) { continue; } else { throw new EventBusException("Illegal onEvent method, check for typos: " + method); } }
接下来通过EventBus约定的规则去寻找事件处理方法
事件处理方法必须是onEvent开头
private static final String ON_EVENT_METHOD_NAME = "onEvent"; ... if (methodName.startsWith(ON_EVENT_METHOD_NAME)) { ...
只能是public的非抽象(abstract)、非静态(static)方法
int modifiers = method.getModifiers(); if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) { ...
事件处理方法只能有一个参数
Class<?>[] parameterTypes = method.getParameterTypes(); if (parameterTypes.length == 1) { ...
经过上述重重过滤,找到了事件处理方法,然后根据事件处理方法的命名规则,判定处理该事件的线程模型
String modifierString = methodName.substring(ON_EVENT_METHOD_NAME.length()); ThreadMode threadMode; if (modifierString.length() == 0) { threadMode = ThreadMode.PostThread; } else if (modifierString.equals("MainThread")) { threadMode = ThreadMode.MainThread; } else if (modifierString.equals("BackgroundThread")) { threadMode = ThreadMode.BackgroundThread; } else if (modifierString.equals("Async")) { threadMode = ThreadMode.Async; }
由此可知:
onEvent方法的线程模型是PostThread,在发布者线程处理事件
onEventMainThread方法的线程模型是MainThread,在主线程处理事件
onEventBackgroundThread方法的线程模型是BackgroundThread
onEventAsync方法的线程模型是Async
BackgroundThread和Async线程模型的区别随后会专门研究讨论,此处不表。
Class<?> eventType = parameterTypes[0]; methodKeyBuilder.setLength(0); methodKeyBuilder.append(methodName); methodKeyBuilder.append('>').append(eventType.getName()); String methodKey = methodKeyBuilder.toString(); if (eventTypesFound.add(methodKey)) { // Only add if not already found in a sub class subscriberMethods.add(new SubscriberMethod(method, threadMode, eventType)); }
接着以”事件处理方法名>事件类型“为key,加入到一个set中
HashSet<String> eventTypesFound = new HashSet<String>();
eventTypesFound这个set的主要作用是去重,如果子类覆写了父类的事件处理方法,那么EventBus只保存子类的事件处理方法,父类的事件处理方法就像覆写的意义那样,被覆盖掉。
if (subscriberMethods.isEmpty()) { throw new EventBusException("Subscriber " + subscriberClass + " has no public methods called " + ON_EVENT_METHOD_NAME); } else { synchronized (methodCache) { methodCache.put(key, subscriberMethods); } return subscriberMethods; }
如果订阅者没有订阅事件,也就是说没有找到事件处理方法,那么抛出异常。
如果找到了该订阅者的所有事件处理方法,在返回之前,要添加缓存。
对每个事件进行单独订阅,更新两个map
找到订阅者订阅的所有事件后,会通过subscribe方法对每个事件进行单独订阅。for (SubscriberMethod subscriberMethod : subscriberMethods) { subscribe(subscriber, subscriberMethod, sticky, priority); }
subscribe方法实际上是更新两个map:
事件到订阅者列表的map,发布事件的时候可以根据事件获取订阅者列表,这样可以逐一反射调用订阅者的事件处理方法。
订阅者到事件列表的map,这样订阅者向EventBus解除注册(unregister)的时候可以根据订阅者获取该订阅者订阅的所有事件,对每个事件分别解除注册。
// Must be called in synchronized block private void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority) { Class<?> eventType = subscriberMethod.eventType; CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType); Subscription newSubscription = new Subscription(subscriber, subscriberMethod, priority); if (subscriptions == null) { subscriptions = new CopyOnWriteArrayList<Subscription>(); subscriptionsByEventType.put(eventType, subscriptions); } else { if (subscriptions.contains(newSubscription)) { throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event " + eventType); } } // Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again) // subscriberMethod.method.setAccessible(true); int size = subscriptions.size(); for (int i = 0; i <= size; i++) { if (i == size || newSubscription.priority > subscriptions.get(i).priority) { subscriptions.add(i, newSubscription); break; } } List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber); if (subscribedEvents == null) { subscribedEvents = new ArrayList<Class<?>>(); typesBySubscriber.put(subscriber, subscribedEvents); } subscribedEvents.add(eventType); if (sticky) { Object stickyEvent; synchronized (stickyEvents) { stickyEvent = stickyEvents.get(eventType); } if (stickyEvent != null) { // If the subscriber is trying to abort the event, it will fail (event is not tracked in posting state) // --> Strange corner case, which we don't take care of here. postToSubscription(newSubscription, stickyEvent, Looper.getMainLooper() == Looper.myLooper()); } } }
首先根据事件从map中获取该事件的订阅者列表,根据参数创建一个Subscription对象(封装了订阅者,事件处理方法和优先级)
Class<?> eventType = subscriberMethod.eventType; CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType); Subscription newSubscription = new Subscription(subscriber, subscriberMethod, priority);
如果该事件没有订阅者列表的话,就新建一个订阅者列表添加进map
if (subscriptions == null) { subscriptions = new CopyOnWriteArrayList<Subscription>(); subscriptionsByEventType.put(eventType, subscriptions); }
否则判断是否为重复订阅,是的话抛出异常。
接着根据新的订阅者的优先级将新订阅者插入订阅者列表的合适位置
int size = subscriptions.size(); for (int i = 0; i <= size; i++) { if (i == size || newSubscription.priority > subscriptions.get(i).priority) { subscriptions.add(i, newSubscription); break; } }
可见优先级数值越大,越靠近列表的前部。
因此订阅者列表是按照优先级从大到小排队的。
接着更新另外一个map,获取该订阅者订阅的所有事件
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber); if (subscribedEvents == null) { subscribedEvents = new ArrayList<Class<?>>(); typesBySubscriber.put(subscriber, subscribedEvents); } subscribedEvents.add(eventType);
最后判断是否为sticky事件,是的话就读取保存的sticky事件发布出去
if (sticky) { Object stickyEvent; synchronized (stickyEvents) { stickyEvent = stickyEvents.get(eventType); } if (stickyEvent != null) { // If the subscriber is trying to abort the event, it will fail (event is not tracked in posting state) // --> Strange corner case, which we don't take care of here. postToSubscription(newSubscription, stickyEvent, Looper.getMainLooper() == Looper.myLooper()); } }
事件的发布将在下一篇中继续分析。
相关文章推荐
- 从源码安装Mysql/Percona 5.5
- 浅析Ruby的源代码布局及其编程风格
- asp.net 抓取网页源码三种实现方法
- JS小游戏之仙剑翻牌源码详解
- JS小游戏之宇宙战机源码详解
- jQuery源码分析之jQuery中的循环技巧详解
- 本人自用的global.js库源码分享
- java中原码、反码与补码的问题分析
- ASP.NET使用HttpWebRequest读取远程网页源代码
- PHP网页游戏学习之Xnova(ogame)源码解读(六)
- C#获取网页HTML源码实例
- PHP网页游戏学习之Xnova(ogame)源码解读(八)
- PHP网页游戏学习之Xnova(ogame)源码解读(四)
- 深入理解PHP之源码目录结构与功能说明
- JS小游戏之极速快跑源码详解
- JS小游戏之象棋暗棋源码详解
- android源码探索之定制android关机界面的方法
- 基于Android设计模式之--SDK源码之策略模式的详解
- Android游戏源码分享之2048
- C语言借助EasyX实现的生命游戏源码