项目中用到了很多优秀的框架,但是一直停留在会用的阶段是,没有硬着头皮去看看它们是如何实现的,趁着活终于不多了,打算把项目中的用到的框架都研究研究,学习一下优秀的代码。今天就看看EventBus
吧,优秀的框架一般使用起来都特别简单,EventBus
也不例外。EventBus
的具体使用这里就不介绍了,无外乎register
注册;unregister
注销;@subscriber
在合适的线程关心相应的事件;post
/postSticky
发送事件,我们就从EventBus
的register
开始吧。
大多数情况下我们都是通过EventBus的静态方法getDefault
获得默认的EventBus
对象,然后register
注册。
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
很常见的单例设计模式,继续看看EventBus
初始化做了什么
public EventBus() {
this(DEFAULT_BUILDER);
}
EventBus(EventBusBuilder builder) {
// subscriptionsByEventType是一个map集合,存储着每个事件对应的subscription集合 (key:EventType value: List)
// subscription是对订阅者还有订阅方法SubscriberMethod的包装
// SubscriberMethod又是对method,优先级,线程类型,是否接受粘性事件的包装
subscriptionsByEventType = new HashMap<>();
// typesBySubscriber也是一个map集合,存储着每个订阅者订阅的事件集合( key:订阅者 value:List)
typesBySubscriber = new HashMap<>();
// 粘性事件集合
stickyEvents = new ConcurrentHashMap<>();
// 需要在主线程处理的事件的poster
mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
// 需要Background线程处理的事件的poster
backgroundPoster = new BackgroundPoster(this);
// 需要异步处理的事件的poster
asyncPoster = new AsyncPoster(this);
// 索引个数
indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
// 通过subscriberMethodFinder寻找订阅者的订阅方法 可以通过反射和索引两种方式
subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
builder.strictMethodVerification, builder.ignoreGeneratedIndex);
// 以下都是是否打印或者抛出异常的标志位
logSubscriberExceptions = builder.logSubscriberExceptions;
logNoSubscriberMessages = builder.logNoSubscriberMessages;
sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
throwSubscriberException = builder.throwSubscriberException;
// 是否支持事件继承
eventInheritance = builder.eventInheritance;
// 异步和 BackGround 处理方式的线程池
executorService = builder.executorService;
}
又是很常见的Builder
设计模式对EventBus
进行初始化。
接下来就是register
了
public void register(Object subscriber) {
// 订阅者的类型
Class> subscriberClass = subscriber.getClass();
// 通过SubscriberMethodFinder类寻找该subscriber所有带@subscriber注解方法相关信息(SubscriberMethod)的集合。
// SubscriberMethod实际上是对带@subscriber注解的方法一些信息的封装。
// 包括该方法的Method对象,事件回调的线程ThreadMode,优先级priority,是否响应粘性事件sticky,以及最重要的所关心的事件类型eventType。
List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
先看看上面findSubscriberMethods
方法是如何获取到SubscriberMethod
集合的:
List findSubscriberMethods(Class> subscriberClass) {
// 先看看缓存中有没有
List subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
// 是否忽略index,默认是false
if (ignoreGeneratedIndex) {
//通过反射查找
subscriberMethods = findUsingReflection(subscriberClass);
} else {
//尝试从索引中查找
subscriberMethods = findUsingInfo(subscriberClass);
}
// 某些类调用了register方法,却没有带@Subscribe注解的方法,就会抛出如下异常(是不是很眼熟?)
if (subscriberMethods.isEmpty()) {
throw new EventBusException("Subscriber " + subscriberClass
+ " and its super classes have no public methods with the @Subscribe annotation");
} else {
// 放入缓存并且返回subscriberMethods
METHOD_CACHE.put(subscriberClass, subscriberMethods);
return subscriberMethods;
}
}
从上面的代码中可以看到有两种方式来获取订阅者中带@Subscribe
注解的方法,这里其实是EventBus3.0
的优化,之前都是通过反射,而新版如果启用了索引的话,会在编译时期,将所有订阅者及其带@Subscribe
注解的方法相关信息通过硬编码的方式存放到map
中,这样的话就可以直接通过key
来获取了,下面是编译时期生成的索引类的部分代码:
//存放所有的订阅者及其带`@Subscribe`注解的方法相关信息
private static final Map, SubscriberInfo> SUBSCRIBER_INDEX;
static {
SUBSCRIBER_INDEX = new HashMap, SubscriberInfo>();
// 将所有的订阅者及其关心的事件存放到map中
putIndex(new SimpleSubscriberInfo(MainActivity.class, true, new SubscriberMethodInfo[] {
new SubscriberMethodInfo("onEventHanlder1", String.class, ThreadMode.MAIN),
new SubscriberMethodInfo("onEventHanlder2", String.class, ThreadMode.MAIN),
new SubscriberMethodInfo("onEventHanlder3", String.class, ThreadMode.MAIN),
new SubscriberMethodInfo("onEventHanlderInteger", Integer.class, ThreadMode.MAIN),
}));
putIndex(new SimpleSubscriberInfo(SecondActivity.class, true, new SubscriberMethodInfo[] {
new SubscriberMethodInfo("onSecondEvent1", String.class, ThreadMode.MAIN),
new SubscriberMethodInfo("onSecondEvent2", String.class, ThreadMode.MAIN, 0, true),
new SubscriberMethodInfo("onSecondEvent3", Integer.class, ThreadMode.MAIN),
new SubscriberMethodInfo("onSecondEvent4", Integer.class, ThreadMode.MAIN),
new SubscriberMethodInfo("onSecondEvent5", Integer.class, ThreadMode.MAIN),
new SubscriberMethodInfo("onSecondEvent6", Integer.class, ThreadMode.MAIN),
}));
}
private static void putIndex(SubscriberInfo info) {
SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);
}
先来看看通过反射的方式获取List
集合
private List findUsingReflection(Class> subscriberClass) {
// 从对象池中取一个FindState
FindState findState = prepareFindState();
// 初始化FindState
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
// 通过反射寻找订阅方法相关信息
findUsingReflectionInSingleClass(findState);
// 有父类的话 findState.clazz 设置为父类
// 没有的话findState.clazz = null, 结束寻找
findState.moveToSuperclass();
}
// 释放资源并且方法findState中的subscriberMethods
return getMethodsAndRelease(findState);
}
//通过反射查找
private void findUsingReflectionInSingleClass(FindState findState) {
Method[] methods;
try {
// This is faster than getMethods, especially when subscribers are fat classes like Activities
// 之所以比getMethods快,是因为getDeclaredMethods不包括继承的方法
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,是否是抽象方法,是否静态方法
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();
// 将该方法相关信息(method,eventType,threadMode,priority,sticky)包装下添加到subscriberMethods集合中
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)) {
// 抛异常,带注解的方法必须是public,非静态,非抽象(是不是很眼熟,有时候手快少写public或者写成了private)
String methodName = method.getDeclaringClass().getName() + "." + method.getName();
throw new EventBusException(methodName +
" is a illegal @Subscribe method: must be public, non-static, and non-abstract");
}
}
}
再来看看通过索引的方式吧
private List findUsingInfo(Class> subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
// 取出自动生成的索引中的subscriberInfo
findState.subscriberInfo = getSubscriberInfo(findState);
// 如果仅仅启用了索引,但未将索引添加到eventbus中,还是采用反射的方式
if (findState.subscriberInfo != null) {
// subscriberInfo中包含除了method以外的subscriberMethods所需要的字段
// 所以通过subscriberInfo的getSubscriberMethods方法进而通过createSubscriberMethod的方法获取method对象
// 并且创建SubscriberMethod对象
SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
for (SubscriberMethod subscriberMethod : array) {
if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
findState.subscriberMethods.add(subscriberMethod);
}
}
} else {
// 反射的方式
findUsingReflectionInSingleClass(findState);
}
// 有父类的话 findState.clazz 设置为父类
// 没有的话findState.clazz = null, 结束寻找
findState.moveToSuperclass();
}
// 释放资源并且方法findState中的subscriberMethods
return getMethodsAndRelease(findState);
}
通过上述的两种方式找出该订阅者所有的订阅方法信息, 我们继续回到egister方法中,走完剩下的逻辑
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
// 事件类型
Class> eventType = subscriberMethod.eventType;
// 用Subscription类包装订阅者以及订阅方法
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
// 取出所有订阅了该事件的Subscription集合
CopyOnWriteArrayList subscriptions = subscriptionsByEventType.get(eventType);
// 可能此eventType是第一次出现,初始化一下
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
// 该订阅者register了两次会出现此异常
// 可以自行查看Subscription equals方法 细节还是挺多的
if (subscriptions.contains(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
}
}
// 根据优先级重新排序下
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> subscribedEvents = typesBySubscriber.get(subscriber);
// 第一次先初始化
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
// 如果当前订阅方法接受粘性事件,并且订阅方法关心的事件在粘性事件集合中,那么将该event事件post给subscriber
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).
Set, Object>> entries = stickyEvents.entrySet();
for (Map.Entry, 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);
}
}
}
register
的流程大致分析完了,概况下就是通过合适的方式找到订阅者中所有的订阅方法及其信息、关心的事件,然后存到对应的集合中,如果存在关心粘性事件的订阅方法,那么在判断是否有之前post过的sticky事件,有则立即post给该订阅者。最后来张流程图装装逼吧: