内容简介:当订阅者向由于把订阅记录内主要包括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源码剖析
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。