EventBus是一款发布/订阅事件总线框架,基于观察者模式。将事件的接收者和发送者分开,简化了组件之间的通信,使用简单、效率高、体积小。
EventBus实现原理分析主要从以下4点着手分析:
1.EventBus事件的注册EventBus.getDefault().register(this);
2.EventBus事件的解注册EventBus.getDefault().unRegister(this);
3.EventBus发送事件EventBus.getDefault().post(new MessageEvent());
4.EventBus发送粘性事件EventBus.getDefault().postSticky(new MessageEvent());
分析前,列举出在EventBus在实现发布/订阅事件总线框架过程中使用到的5个核心类。
数据类型 | 解释说明 |
---|---|
subscriptionsByEventType | 存储key&value键值对,其中key是eventType,value是List集合。List集合中存放的数据类型是Subscription。 |
typesBySubscriber | 存储key&value键值对,其中key是subscriber订阅者Class对象,value是List集合。集合中存放的数据类型是EventType的List集合。 |
EventType | 订阅方法参数类型。 |
Subscription | 一种数据类型,类似JavaBean,封装了SubscriberMethod。 |
SubscriberMethod | 内部封装了订阅方法的Method、线程类型(threadMode)、事件类型(eventType)、事件优先级、是否是粘性事件等。 |
EventBus事件的注册
注册事件所要实现的结果,是使用EventBus中定义的Map——subscriptionsByEventType,在当前注册类中去收集和存放 以事件类(接收事件方法的参数类型)Class为key,和以订阅方法属性的包装类集合作为value的键值对。
/// EventBus.java
public void register(Object subscriber) {
...
Class<?> subscriberClass = subscriber.getClass();
// 获取包含订阅方法属性对象SubscriberMethod的集合
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
// 遍历,以订阅方法事件类型分类,将订阅方法包装对象存放到Map中
subscribe(subscriber, subscriberMethod);
}
}
}
SubscriberMethodFinder通过当前注册类subscriberClass,获取SubscriberMethod的集合。SubscriberMethod中封装了订阅方法所有的方法属性。之后拿着SubscriberMethod的集合进行遍历,并执行subscribe方法。通过方法subscribe将所有注册类下的订阅方法,以订阅事件EventType作为分类,存放到subscriptionsByEventType字典Map中。
/// EventBus.java
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<>();
// 以eventType进行分类存放订阅者方法属性包装对象
subscriptionsByEventType.put(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;
}
}
SubscriberMethodFinder
如何通过注册类获取SubscriberMethod集合?
// SubscriberMethodFinder.java
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
..
// 使有生成的索引,也强制使用反射(默认值:false)。
if (ignoreGeneratedIndex) {
subscriberMethods = findUsingReflection(subscriberClass);
} else {
// 默认执行这里
subscriberMethods = findUsingInfo(subscriberClass);
}
...
}
// 方法执行流转到这里继续执行
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
... findUsingReflectionInSingleClass(findState); ...
}
// 方法执行流转到这里,通过反射执行必要逻辑
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
try {
// 失败,则反射获取所有子类、父类的public访问权限修饰符的方法
methods = findState.clazz.getMethods();
} catch (LinkageError error) { ...}
}
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) {
// 获取订阅方法的注解类型实例——被{@link Subscribe}注释的事件处理方法
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
if (subscribeAnnotation != null) {
// 获取订阅方法参数的事件类型
Class<?> eventType = parameterTypes[0];
if (findState.checkAdd(method, eventType)) {
// 检测未添加,则获取该订阅方法执行的线程模式
ThreadMode threadMode = subscribeAnnotation.threadMode();
// 封装订阅方法的方法、事件类型、线程模式、优先级、粘性否等,并添加存入FindState.subscriberMethods集合中[List]
findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode, subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
}
... ...
// 返回集合List
return getMethodsAndRelease(findState);
}
// 在方法getMethodsAndRelease中,返回集合List。因此在EvebtBus.register执行subscriberMethodFinder.findSubscriberMethods(subscriberClass)时,可获取到集合List的对象。
private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {
List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods);
findState.recycle();
......
return subscriberMethods;
}
EventBus发送事件
EventBus发送事件原理是,subscriptionsByEventType通过事件类型EventType.class获取到订阅方法包装类List集合。然后通过postToSubscription方法,在方法中匹配发送事件的线程模式threadMode,例如匹配到主线程时,会直接通过反射调用订阅方法subscription.subscriberMethod.method.invoke(subscription.subscriber, event)实现事件的发布-订阅执行。
/// EventBus.java 发起事件,经到这里会匹配发起事件的线程模式
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
case POSTING:
// 直接通过invoke反射调用订阅方法的方法执行通知
invokeSubscriber(subscription, event);
break;
case MAIN:
if (isMainThread) {// 上面时序图画的这个过程
// 直接通过invoke反射调用订阅方法的方法执行通知
invokeSubscriber(subscription, event);
} else {
// mainThreadPoster实现类 HandlerPoster extends Handler,
// 即如果发送通知不在主线程,该通知会通过Handler将线程切换到主线程,
// 然后通过Android消息机制在主线程的handleMessage中通过invoke反射调用订阅方法的方法执行通知
mainThreadPoster.enqueue(subscription, event);
}
break;
case MAIN_ORDERED:
if (mainThreadPoster != null) {
mainThreadPoster.enqueue(subscription, event);
} else {
// temporary: technically not correct as poster not decoupled from subscriber
invokeSubscriber(subscription, event);
}
break;
case BACKGROUND:
if (isMainThread) {
// 如果在主线程中发送了事件通知, 会通过BackgroundPoster implements Runnable 将事件处理交给工作线程线程池ExecutorService.execute(this)处理,终在工作线程中通过invoke反射调用订阅方法的方法执行通知
backgroundPoster.enqueue(subscription, event);
} else {
// 直接通过invoke反射调用订阅方法的方法执行通知
invokeSubscriber(subscription, event);
}
break;
case ASYNC://底部时序图画的这个过程。后台线程执行该事件-订阅方法
asyncPoster.enqueue(subscription, event);
break;
default:
throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
}
}
如果匹配到的是上面源码中case ASYNC
,AsyncPoster会将Subscription和事件类的实例封装到PendingPost中,并入队PendingPostQueue。然后在异步线程中执行eventBus.invokeSubscriber(pendingPost);,并最终是在工作线程中通过反射调用执行订阅方法。具体代码是subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
EventBus事件的解注册
EventBus解注册原理则是清空typesBySubscriber和subscriptionsByEventType。
源码实现很简单~
// EventBus.java 方法解注册
public synchronized void unregister(Object subscriber) {
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
if (subscribedTypes != null) {
for (Class<?> eventType : subscribedTypes) {
// 解注册 —— 移除Map中当前类(定义的)对应的事件类型的Subscription集合
unsubscribeByEventType(subscriber, eventType);
}
// 解注册当前类的注册,直接在Map中remove掉即可
typesBySubscriber.remove(subscriber);
} else {
logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
// 移除(解注册)条件:需满足是eventType事件类型的方法订阅
List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions != null) {
int size = subscriptions.size();
for (int i = 0; i < size; i++) {
Subscription subscription = subscriptions.get(i);
if (subscription.subscriber == subscriber) {
// 移除(解注册)条件:需满足在当前注册类的方法订阅,才能执行移除
subscription.active = false;
subscriptions.remove(i);
i--;
size--;
}
}
}
}
EventBus发送粘性事件
EventBus在处理粘性事件的原理,先通过postSticky方法将键值对(key=事件类型class,value=事件实例)存入字典stickyEvents中。而真实的执行粘性事件的订阅方法,则是通过EventBus.register()来实现的。发布事件的订阅类在注册时如果判断得知该事件属于粘性事件,那么就会通过循环遍历字典stickyEvents,以执行订阅方法的执行~ 【在执行订阅方法进入方法postToSubscription时,跟非粘性事件处理就一致了】
// EventBus.js 发起粘性事件通知
public void postSticky(Object event) {
synchronized (stickyEvents) {
// 在发起事件的时候,将键值对(key=事件类型class,value=事件实例)存入字典stickyEvents中
stickyEvents.put(event.getClass(), event);
}
// Should be posted after it is putted, in case the subscriber wants to remove immediately
post(event);
}
///EventBus.js
// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
...
if (subscriberMethod.sticky) {// 如果是粘性事件
if (eventInheritance) {// 默认情况下true,EventBus考虑事件类(订阅者)的层次结构(超类的订阅者将被通知)
Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
// 遍历字典stickyEvents,获取存入的key=事件类型class,value=事件实例
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);
}
}
}
/// 从这里开启粘性订阅方法的被调用执行~
private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
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发起订阅方法执行
postToSubscription(newSubscription, stickyEvent, isMainThread());
}
}