EventBus使用很简单,了解它的源码的实现也是应该的,不但学习设计者的思想,也可以提高自己的能力。
注册
EventBus.getDefault().register(this);
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
可见,EventBus使用注解和反射机制。查看findSubscriberMethods
,查找当前注册类是否有订阅方法。
整个查找订阅方法就过程开始了
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
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;
}
}
- 通过
METHOD_CACHE
获取是否有缓存该类的所有方法,有缓存则直接返回一个List<SubscriberMethod>
。METHOD_CACHE
是一个ConcurrentHashMap
,以订阅类的Class
为key,保存所有的订阅方法List<SubscriberMethod>
为value。 - 通过跟踪代码,
ignoreGeneratedIndex
默认值为false
,所有这里执行findUsingInfo
方法。
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
//finaState保留订阅类的相关信息
FindState findState = prepareFindState();
//findState的初始化
findState.initForSubscriber(subscriberClass);
//由于上一行的初始化,clazz!=null
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);
}
配合代码中的注释,FindState类主要保留注册类的相关信息,在prepareFindState
和initForSubscriber
,初始化后,findSate.clazz并不为空,findSate指向当前的订阅类的class
。查看getSubscriberInfo
,此时返回值是null
。在前面初始化中,findState.subscriberInfo
的值是null,为此查看findUsingReflectionInSingleClass
。
private void findUsingReflectionInSingleClass(FindState findState) {
Method[] methods;
try {
// This is faster than getMethods, especially when subscribers are fat classes like 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();
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length == 1) {
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
if (subscribeAnnotation != null) {
Class<?> eventType = parameterTypes[0];
if (findState.checkAdd(method, eventType)) {
ThreadMode threadMode = subscribeAnnotation.threadMode();
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)) {
String methodName = method.getDeclaringClass().getName() + "." + method.getName();
throw new EventBusException(methodName +
" is a illegal @Subscribe method: must be public, non-static, and non-abstract");
}
}
}
- 这里通过反射机制查看订阅类是否有订阅方法。
- 符合订阅方法规则。
- 订阅方法必须public修饰,不能是静态方法和抽象方法。否则会抛出错误。
- 参数只有一个的。
- 使用Subscribe注解的
- 在符合第二个要求后,通过
checkAdd
方法检测订阅方法是否已存在。如果不存在则封装成SubscriberMethod
保存在findState.subscriberMethods
。
在这里,我们查看一下FindState
类的初始化和checkAdd
方法。
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);
Class<?> subscriberClass;
Class<?> clazz;
boolean skipSuperClasses;
SubscriberInfo subscriberInfo;
void initForSubscriber(Class<?> subscriberClass) {
this.subscriberClass = clazz = subscriberClass;
skipSuperClasses = false;
subscriberInfo = null;
}
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)) {
// Paranoia check
throw new IllegalStateException();
}
// Put any non-Method object to "consume" the existing Method
anyMethodByEventType.put(eventType, this);
}
return checkAddWithMethodSignature(method, eventType);
}
}
private boolean checkAddWithMethodSignature(Method method, Class<?> eventType) {
methodKeyBuilder.setLength(0);
methodKeyBuilder.append(method.getName());
methodKeyBuilder.append('>').append(eventType.getName());
String methodKey = methodKeyBuilder.toString();
Class<?> methodClass = method.getDeclaringClass();
Class<?> methodClassOld = subscriberClassByMethodKey.put(methodKey, methodClass);
//检测方法名是否相同
//检测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;
}
}
-
subscriberMethods
用于保存所有订阅方法。anyMethodByEventType
以事件类型EventType
为key,保存方法Method
。subscriberClassByMethodKey
以method.getName()>eventType.getName()
为key,保存methodClass
。 -
checkAdd
的第一步是检查该事件类型的参数的方法是否已经存在,如果不存在,直接返回true
添加,添加该订阅方法。第二步是,但存在该类型方法时,可能存在方法名不同或者方法名相同父类被子类重写。此时调用checkAddWithMethodSignature
方法进行检查。 - 在
checkAddWithMethodSignature
方法中,当methodClassOld==null
,表示方法名不同,那么直接添加订阅方法。methodClassOld.isAssignableFrom(methodClass)
表示methodClassOld
是否methodClass
的父类或者父接口。如果是的化,也直接添加。
这里的双重检查,判断该订阅方法是否已存在:
- 与已有的订阅方法参数类型是否相同,不相同,则直接添加。相同则下一步。
- 检测方法名是否相同,不相同则添加。相同则检查已有的订阅方法的类是否是订阅类的父类或父接口。
主要是查找所有的订阅方法,并封装为SubscriberMethod
回到查到订阅方法的开始。
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
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;
}
}
当注册类后但没有订阅方法后,会报异常
把查到到的订阅方法放到METHOD_CACHE
,以便下次查找订阅方法直接返回。
到这里整个查找订阅方法结束了
接着回到最开始的方法register
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
可见,通过subscribe
方法将订阅者和其所有的订阅方法关联在一起。
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class<?> eventType = subscriberMethod.eventType;
//Subscription保存着订阅者和订阅方法的信息和状态
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);
}
}
//根据事件的优先权,顺序添加到subscriptions中
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;
}
}
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
//判断是否订阅粘性方法,是的需要在注册后需要接收最后一条该类型粘性广播
if (subscriberMethod.sticky) {
if (eventInheritance) {
// Existing sticky events of all subclasses of eventType have to be considered.
// Note: Iterating over all events may be inefficient with lots of sticky events,
// thus data structure should be changed to allow a more efficient lookup
// (e.g. an additional map storing sub classes of super classes: Class -> List<Class>).
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);
}
}
}
- 主要将订阅者和订阅方法关联起来,封装成
Subscription
,并以事件类型为key保存到subscriptionsByEventType
中。 - 如果
subscriptionsByEventType
已有订阅信息,会抛出异常。 - 如果订阅方法是粘性的,则会收到该类型的最后一条粘性广播。
到这里,EventBus的注册过程基本就结束了。
整个流程总结:
1、 寻订阅类是否有订阅方法,是否符合要求
2、 将订阅方法相关信息封装成SubscriberMethod
,并把所有订阅方法存在List
和缓存到METHOD_CACHE
中。
3 、将订阅者和订阅方法关联起来,封装成Subscription
。并保存在subscriptionsByEventType
。
4、 如果是粘性事件,接收最后一条广播。
通过注册流程中,我们应该也能猜想EventBus的工作原理了。当我们post一个eventType时,去读取subscriptionsByEventType
中该类型的相关订阅信息,然后达到调用我们订阅方法。也就是说明为什么EventBus是属于发布-订阅模式。
分析下来,涉及到了单例模式,建造者模式,观察者模式。
网友评论