内容简介:当订阅者向由于把订阅记录内主要包括3个成员变量。
一、Subscription
当订阅者向 EventBus 注册时, EventBus 会扫描整个订阅者类,获取具体接收事件的方法,并构造出以下实例。每个订阅者可能有多个方法接收订阅事件,每个方法均会生成各自的 Subscription 作为接受事件的凭证。
由于把 Subscription 翻译为名词性的“订阅”后,和字面上动词性的“订阅”没法区分。所以本系列文章,把该词翻译为更贴切的名词“订阅记录”。这个词会将在后续文章继续沿用。
订阅记录内主要包括3个成员变量。 subscriber
表示订阅者类的实例, subscriberMethod
表示订阅者内部接受事件的方法,和表示订阅记录是否存活的 active
。
调用 EventBus#unregister(Object)
注销订阅者后, active
立即改为 false
。该值被负责队列事件投递的 EventBus#invokeSubscriber(PendingPost)
检查以避免 race conditions
。
final class Subscription { // 订阅者类的实例 final Object subscriber; // 订阅事件的方法 final SubscriberMethod subscriberMethod; // 订阅记录是否依然生效标志位,volatile修饰控制多线程可见性 volatile boolean active; // 构造方法,构造之后默认接受事件 Subscription(Object subscriber, SubscriberMethod subscriberMethod) { this.subscriber = subscriber; this.subscriberMethod = subscriberMethod; active = true; } @Override public boolean equals(Object other) { if (other instanceof Subscription) { Subscription otherSubscription = (Subscription) other; return subscriber == otherSubscription.subscriber && subscriberMethod.equals(otherSubscription.subscriberMethod); } else { return false; } } @Override public int hashCode() { return subscriber.hashCode() + subscriberMethod.methodString.hashCode(); } }
二、SubscriberMethod
具体到 Subscription 内部实现,里面还包含了 SubscriberMethod 。
前文已经提到,订阅者类通过 EventBus 的注解修饰并因此能被 EventBus 发现。 EventBus 通过注解处理器分析注解,和获取的订阅者方法信息构造成这个 SubscriberMethod 类,成为订阅者信息的索引。
public class SubscriberMethod { // 接收事件的方法实例 final Method method; // 线程模式 final ThreadMode threadMode; // 事件类型 final Class<?> eventType; // 事件优先级 final int priority; // 是否粘性事件 final boolean sticky; // 此字符串用于提高比较效率 String methodString; public SubscriberMethod(Method method, Class<?> eventType, ThreadMode threadMode, int priority, boolean sticky) { this.method = method; this.threadMode = threadMode; this.eventType = eventType; this.priority = priority; this.sticky = sticky; } // 比较两个订阅者方法 @Override public boolean equals(Object other) { if (other == this) { return true; } else if (other instanceof SubscriberMethod) { checkMethodString(); SubscriberMethod otherSubscriberMethod = (SubscriberMethod)other; otherSubscriberMethod.checkMethodString(); // Don't use method.equals because of http://code.google.com/p/android/issues/detail?id=7811#c6 return methodString.equals(otherSubscriberMethod.methodString); } else { return false; } } private synchronized void checkMethodString() { if (methodString == null) { // Method.toString has more overhead, just take relevant parts of the method StringBuilder builder = new StringBuilder(64); builder.append(method.getDeclaringClass().getName()); builder.append('#').append(method.getName()); builder.append('(').append(eventType.getName()); methodString = builder.toString(); } } @Override public int hashCode() { return method.hashCode(); } }
三、Subscribe注解
这个就是 EventBus 注解。从注解类可以看到 threadMode 、 sticky 、 priority 均能和 SubscriberMethod 类的数据成员匹配上。
@Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD}) public @interface Subscribe { // 通过指定线程模式调起订阅者方法 ThreadMode threadMode() default ThreadMode.POSTING; // 若是粘性事件,把最近的粘性事件发送给订阅者 boolean sticky() default false; // 方法接收事件的优先级,默认优先级是0 // 在相同线程内,高优先级订阅者方法比低优先级方法更早接收事件 // 此优先级不会影响不同线程模式中不同订阅者事件的派发 int priority() default 0; }
实例:
注解方法时不需要自定义条件,使用 @Subscribe 修饰且不指定参数即可
@Subscribe(threadMode = ThreadMode.MAIN, sticky = false, priority = 0) fun onEventReceived(event: UserEvent) { val name = event.name val age = event.age Log.i(TAG, "Event received, Name: $name, Age: $age.") }
通过特意构造的实例可知:如果实例没有可执行方法,注册到 EventBus 过程的类扫描会抛出异常:
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.phantomvk.playground/com.phantomvk.playground.MainActivity}: org.greenrobot.eventbus.EventBusException: Subscriber class com.phantomvk.playground.MainActivity and its super classes have no public methods with the @Subscribe annotation
四、SubscriberMethodFinder
前文铺垫 Subscription 、 SubscriberMethod 、 Subscribe 注解,全是都是为了降低 SubscriberMethodFinder 类的理解难度。因为通过此类扫描订阅者方法的 Subscribe 注解,为每个订阅方法生成 SubscriberMethod ,构造出订阅记录 Subscription 。所有事件根据 Subscription 派到对应订阅者的订阅方法。
4.1 类签名
class SubscriberMethodFinder
4.2 常量
在较新的类文件中编译器可能会添加方法, 这些方法被称为桥梁或合成方法。 EventBus 同时忽略这两种方法。这些修饰符都不是 public 的,而是以 Java 类文件格式定义的:http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-4.html#jvms-4.6-200-A.1
private static final int BRIDGE = 0x40; private static final int SYNTHETIC = 0x1000;
忽视抽象方法、静态方法、桥接方法、合成方法
private static final int MODIFIERS_IGNORE = Modifier.ABSTRACT | Modifier.STATIC | BRIDGE | SYNTHETIC;
扫描订阅者和订阅方法后的缓存
private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>();
FindState对象缓存池大小
private static final int POOL_SIZE = 4;
FindState对象缓存池
private static final FindState[] FIND_STATE_POOL = new FindState[POOL_SIZE];
4.3 数据成员
private List<SubscriberInfoIndex> subscriberInfoIndexes; private final boolean strictMethodVerification; private final boolean ignoreGeneratedIndex;
4.4 构造方法
SubscriberMethodFinder(List<SubscriberInfoIndex> subscriberInfoIndexes, boolean strictMethodVerification, boolean ignoreGeneratedIndex) { this.subscriberInfoIndexes = subscriberInfoIndexes; this.strictMethodVerification = strictMethodVerification; this.ignoreGeneratedIndex = ignoreGeneratedIndex; }
4.5 findSubscriberMethods
在订阅者类内扫描订阅者方法,如果订阅者类没有目标方法直接抛出异常
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) { // 根据订阅者类从缓存中获取订阅者方法 List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass); if (subscriberMethods != null) { return subscriberMethods; } // ignoreGeneratedIndex在EventBusBuilder.ignoreGeneratedIndex为false if (ignoreGeneratedIndex) { // 通过反射的方式查找订阅者方法 subscriberMethods = findUsingReflection(subscriberClass); } else { // 查找订阅者方法 subscriberMethods = findUsingInfo(subscriberClass); } // 在订阅者中没有找到使用注解标注的公开方法 if (subscriberMethods.isEmpty()) { throw new EventBusException("Subscriber " + subscriberClass + " and its super classes have no public methods with the @Subscribe annotation"); } else { // 结果放入缓存中 METHOD_CACHE.put(subscriberClass, subscriberMethods); // 返回订阅者方法 return subscriberMethods; } }
4.6 findUsingReflection
通过反射查找订阅者的订阅方法
private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) { // 从缓存池获取findState FindState findState = prepareFindState(); // 用订阅者类初始化findState findState.initForSubscriber(subscriberClass); while (findState.clazz != null) { findUsingReflectionInSingleClass(findState); findState.moveToSuperclass(); } return getMethodsAndRelease(findState); }
4.7 findUsingInfo
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) { // 从缓存池获取findState FindState findState = prepareFindState(); // 用订阅者类初始化findState findState.initForSubscriber(subscriberClass); while (findState.clazz != null) { findState.subscriberInfo = getSubscriberInfo(findState); if (findState.subscriberInfo != null) { SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods(); for (SubscriberMethod subscriberMethod : array) { if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) { findState.subscriberMethods.add(subscriberMethod); } } } else { findUsingReflectionInSingleClass(findState); } findState.moveToSuperclass(); } return getMethodsAndRelease(findState); }
4.8 getMethodsAndRelease
从 findState 获取订阅者的订阅方法, findState 重置后放入缓存之,最后返回订阅者方法列表
private List<SubscriberMethod> getMethodsAndRelease(FindState findState) { // 从findState获取所有订阅方法 List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods); // 重置findState findState.recycle(); // 把findState放入缓存池中 synchronized (FIND_STATE_POOL) { for (int i = 0; i < POOL_SIZE; i++) { // 找一个非空位置存放可缓存实例 if (FIND_STATE_POOL[i] == null) { FIND_STATE_POOL[i] = findState; break; } } } // 返回findState中保存的subscriberMethods return subscriberMethods; }
4.9 prepareFindState
重缓存池中获取 FindState 实例,如果缓存池没有缓存的实例,则创建新实例
private FindState prepareFindState() { synchronized (FIND_STATE_POOL) { for (int i = 0; i < POOL_SIZE; i++) { FindState state = FIND_STATE_POOL[i]; // 从缓存池取一个可用的实例 if (state != null) { FIND_STATE_POOL[i] = null; return state; } } } // 缓存池为空,创建并返回新实例 return new FindState(); }
4.10 getSubscriberInfo
获取订阅者的信息
private SubscriberInfo getSubscriberInfo(FindState findState) { if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) { SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo(); if (findState.clazz == superclassInfo.getSubscriberClass()) { return superclassInfo; } } if (subscriberInfoIndexes != null) { for (SubscriberInfoIndex index : subscriberInfoIndexes) { SubscriberInfo info = index.getSubscriberInfo(findState.clazz); if (info != null) { return info; } } } return null; }
4.11 findUsingReflectionInSingleClass
private void findUsingReflectionInSingleClass(FindState findState) { Method[] methods; try { // 此方法比getMethods速度更快,尤其是订阅者像Activities这种巨型类的时候 methods = findState.clazz.getDeclaredMethods(); } catch (Throwable th) { // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149 methods = findState.clazz.getMethods(); findState.skipSuperClasses = true; } // 遍历订阅者类内所有方法 for (Method method : methods) { int modifiers = method.getModifiers(); // 方法使用可见性为public,且没有使用MODIFIERS_IGNORE修饰 if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) { // 从方法获取变量类型 Class<?>[] parameterTypes = method.getParameterTypes(); // 变量的数量必须为1个 if (parameterTypes.length == 1) { // 检查方法是否有Subscribe注解修饰 Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class); if (subscribeAnnotation != null) { // 从方法变量中确认订阅事件的类型 Class<?> eventType = parameterTypes[0]; if (findState.checkAdd(method, eventType)) { // 从注解获取threadMode信息 ThreadMode threadMode = subscribeAnnotation.threadMode(); // 向findState增加新SubscriberMethod findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode, subscribeAnnotation.priority(), subscribeAnnotation.sticky())); } } } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) { // 订阅者方法的形参数量不唯一 String methodName = method.getDeclaringClass().getName() + "." + method.getName(); throw new EventBusException("@Subscribe method " + methodName + "must have exactly 1 parameter but has " + parameterTypes.length); } } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) { // 方式包含Subscribe注解,但不方法不能同时满足以下条件:公开可见性、非静态、非抽象 String methodName = method.getDeclaringClass().getName() + "." + method.getName(); throw new EventBusException(methodName + " is a illegal @Subscribe method: must be public, non-static, and non-abstract"); } } }
4.12 clearCaches
清除缓存
static void clearCaches() { METHOD_CACHE.clear(); }
五、FindState
5.1 类信息
EventBus创建多个缓存的 FindState 实例,当有订阅者需要扫描订阅方法时,从缓存池中取出一个 FindState ,向此实例中放入需被扫描的订阅者类。之后,包含订阅者的 FindState 传递给负责处理工作的方法,处理完毕后的订阅者方法结果也会存回到 FindState 。
static class FindState
因此处理完毕后的 FindState 既包含订阅者类的信息,也保存着订阅者方法的列表。 EventBus 从 FindState 获取所有订阅者方法后,该 FindState 会重置并重新放入缓存池中。
根据 FindState 内部结构可知, FindState 实例包含 ArrayList 、 HashMap 、 初始长度为128的StringBuilder ,所以 FindState 实例本身就有一定分量。如果实例不断创建并销毁,会加重虚拟机垃圾回收的负担。相反,把用过的 FindState 放入缓存池重用应该是更合理的行为。
从 SubscriberMethodFinder 的 POOL_SIZE 常量可知缓存池大小为4。
private static final int POOL_SIZE = 4;
5.2 不可变成员
这个数据成员用于保存订阅者类扫描扫描方法的结果,使用完毕后会简单清空为下次重用。
final List<SubscriberMethod> subscriberMethods = new ArrayList<>(); final Map<Class, Object> anyMethodByEventType = new HashMap<>(); final Map<String, Class> subscriberClassByMethodKey = new HashMap<>(); final StringBuilder methodKeyBuilder = new StringBuilder(128);
5.3 可变成员
这些成员用于存储订阅者类的信息。每次 EventBus 从缓存池获取 FindState 缓存实例后,都会把订阅者类的基本信息存入以下变量,作为后续操作的参考内容。
Class<?> subscriberClass; Class<?> clazz; boolean skipSuperClasses; SubscriberInfo subscriberInfo;
5.4 initForSubscriber
订阅者类数据通过此方法设置到 FindState
void initForSubscriber(Class<?> subscriberClass) { this.subscriberClass = clazz = subscriberClass; skipSuperClasses = false; subscriberInfo = null; }
5.5 recycle
FindState使用完毕后需调用 recycle() 清空后才能归还给缓存池。这个方法的处理方式有点像 Message 类的recycleUnchecked() 方法。
void recycle() { subscriberMethods.clear(); anyMethodByEventType.clear(); subscriberClassByMethodKey.clear(); methodKeyBuilder.setLength(0); subscriberClass = null; clazz = null; skipSuperClasses = false; subscriberInfo = null; }
5.6 checkAdd
boolean checkAdd(Method method, Class<?> eventType) { // 2 level check: 1st level with event type only (fast), 2nd level with complete signature when required. // Usually a subscriber doesn't have methods listening to the same event type. // 检查同一个订阅者类内是有多个方法订阅相同事件 Object existing = anyMethodByEventType.put(eventType, method); if (existing == null) { // 有多个方法订阅相同事件 return true; } else { if (existing instanceof Method) { if (!checkAddWithMethodSignature((Method) existing, eventType)) { throw new IllegalStateException(); } // Put any non-Method object to "consume" the existing Method anyMethodByEventType.put(eventType, this); } return checkAddWithMethodSignature(method, eventType); } }
5.7 checkAddWithMethodSignature
private boolean checkAddWithMethodSignature(Method method, Class<?> eventType) { methodKeyBuilder.setLength(0); methodKeyBuilder.append(method.getName()); methodKeyBuilder.append('>').append(eventType.getName()); // 例如:onEventReceived>UserEvent String methodKey = methodKeyBuilder.toString(); Class<?> methodClass = method.getDeclaringClass(); // 通过插入新(methodKey, methodClass),并获取旧value Class<?> methodClassOld = subscriberClassByMethodKey.put(methodKey, methodClass); // 旧value为空,或methodClassOld是methodClass的父类或同类 if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) { // Only add if not already found in a sub class // 只有在子类中找不到时才添加 return true; } else { // Revert the put, old class is further down the class hierarchy // 撤销插入,旧类是进一步向下的类层次结构 subscriberClassByMethodKey.put(methodKey, methodClassOld); return false; } }
5.8 moveToSuperclass
void moveToSuperclass() { if (skipSuperClasses) { clazz = null; } else { clazz = clazz.getSuperclass(); // 获取类名 String clazzName = clazz.getName(); /** Skip system classes, this just degrades performance. */ // 跳过系统类,这只会降低性能。 if (clazzName.startsWith("java.") || clazzName.startsWith("javax.") || clazzName.startsWith("android.")) { clazz = null; } } }
六、事件发送给订阅者
6.1 invokeSubscriber
根据 event 和 订阅记录 Subscription ,就能把事件发送给订阅方法。
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); } }
6.2 handleSubscriberException
检查标志位决定是否处理事件订阅方法抛出的异常
private void handleSubscriberException(Subscription subscription, Object event, Throwable cause) { if (event instanceof SubscriberExceptionEvent) { if (logSubscriberExceptions) { // Don't send another SubscriberExceptionEvent to avoid infinite event recursion, just log logger.log(Level.SEVERE, "SubscriberExceptionEvent subscriber " + subscription.subscriber.getClass() + " threw an exception", cause); SubscriberExceptionEvent exEvent = (SubscriberExceptionEvent) event; logger.log(Level.SEVERE, "Initial event " + exEvent.causingEvent + " caused exception in " + exEvent.causingSubscriber, exEvent.throwable); } } else { if (throwSubscriberException) { throw new EventBusException("Invoking subscriber failed", cause); } if (logSubscriberExceptions) { logger.log(Level.SEVERE, "Could not dispatch event: " + event.getClass() + " to subscribing class " + subscription.subscriber.getClass(), cause); } if (sendSubscriberExceptionEvent) { SubscriberExceptionEvent exEvent = new SubscriberExceptionEvent(this, cause, event, subscription.subscriber); post(exEvent); } } }
以上所述就是小编给大家介绍的《EventBus源码剖析(4) -- 订阅记录》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 【Java集合源码剖析】ArrayList源码剖析
- Java集合源码剖析:TreeMap源码剖析
- 我的源码阅读之路:redux源码剖析
- ThreadLocal源码深度剖析
- SharedPreferences源码剖析
- Volley源码剖析
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
HTML5和CSS3实例教程
Brian P.Hogan / 李杰、刘晓娜、柳靖、朱嵬 / 人民邮电出版社 / 2012-1 / 39.00元
《HTML5和CSS3实例教程》共分3部分,集中讨论了HTML5和CSS3规范及其技术的使用方法。首先是规范概述,介绍了新的结构化标签、表单域及其功能(包括自动聚焦功能和占位文本)和CSS3的新选择器。接下来是HTML对视频和音频的支持,讲述了画布上的图形绘制及CSS阴影、渐变和变换的使用方法。最后介绍使用HTML5的客户端特性(包括WebStorage、WebSQLDatabases以及离线支持......一起来看看 《HTML5和CSS3实例教程》 这本书的介绍吧!