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的键值对。
public void register(Object subscriber) {
...
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
SubscriberMethodFinder通过当前注册类subscriberClass,获取SubscriberMethod的集合。SubscriberMethod中封装了订阅方法所有的方法属性。之后拿着SubscriberMethod的集合进行遍历,并执行subscribe方法。通过方法subscribe将所有注册类下的订阅方法,以订阅事件EventType作为分类,存放到subscriptionsByEventType字典Map中。
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class<?> eventType = subscriberMethod.eventType;
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<>();
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集合?
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
..
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 {
methods = findState.clazz.getDeclaredMethods();
} catch (Throwable th) {
try {
methods = findState.clazz.getMethods();
} catch (LinkageError error) { ...}
}
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()));
}
... ...
return getMethodsAndRelease(findState);
}
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)实现事件的发布-订阅执行。
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
case POSTING:
invokeSubscriber(subscription, event);
break;
case MAIN:
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
mainThreadPoster.enqueue(subscription, event);
}
break;
case MAIN_ORDERED:
if (mainThreadPoster != null) {
mainThreadPoster.enqueue(subscription, event);
} else {
invokeSubscriber(subscription, event);
}
break;
case BACKGROUND:
if (isMainThread) {
backgroundPoster.enqueue(subscription, event);
} else {
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。 源码实现很简单~
public synchronized void unregister(Object subscriber) {
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
if (subscribedTypes != null) {
for (Class<?> eventType : subscribedTypes) {
unsubscribeByEventType(subscriber, eventType);
}
typesBySubscriber.remove(subscriber);
} else {
logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
private void unsubscribeByEventType(Object subscriber, Class<?> 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时,跟非粘性事件处理就一致了】
public void postSticky(Object event) {
synchronized (stickyEvents) {
stickyEvents.put(event.getClass(), event);
}
post(event);
}
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
...
if (subscriberMethod.sticky) {
if (eventInheritance) {
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);
}
}
}
private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
if (stickyEvent != null) {
postToSubscription(newSubscription, stickyEvent, isMainThread());
}
}
|