目录
3. 把Listener注册到EventBus上:eventBus.register(listener)
3.1 获得listener对应的所有订阅方法Multimap<Class<?>,EventSubscriber>
3.1.1 findAllSubscribers(listener)
4. 事件总线发布事件,触发监听器方法:eventBus.post(event)
4.1 根据缓存得到event及其子类Set<Class<?>> dispatchTypes
4.2 将EventSubscriber添加进转发队列:enqueueEvent(event, wrapper);
4.3 转发队列中event事件:dispatchQueuedEvents()
步骤:
构造一个事件总线EventBus
构造一个事件监听器Listener
把事件监听器注册到事件总线上EventBus#register(listener)
事件总线发布事件,触发监听器方法EventBus#post(event)
1. 创建EventBus
EventBus eventBus = new EventBus()
构造方法:
private SubscriberExceptionHandler subscriberExceptionHandler;
/**
* Creates a new EventBus named "default".
*/
public EventBus() {
this("default");
}
/**
* Creates a new EventBus with the given {@code identifier}.
*
* @param identifier a brief name for this bus, for logging purposes. Should
* be a valid Java identifier.
*/
public EventBus(String identifier) {
this(new LoggingSubscriberExceptionHandler(identifier));
}
/**
* Creates a new EventBus with the given {@link SubscriberExceptionHandler}.
*
* @param subscriberExceptionHandler Handler for subscriber exceptions.
* @since 16.0
*/
public EventBus(SubscriberExceptionHandler subscriberExceptionHandler) {
this.subscriberExceptionHandler = checkNotNull(subscriberExceptionHandler);
}
2. 创建Listener
通过@Subscribe将类的方法注解为listener。
例如:
public class FruitEaterListener {
private final static Logger LOGGER = LoggerFactory.getLogger(FruitEaterListener.class);
@Subscribe
public void eat(Fruit event){
if (LOGGER.isInfoEnabled()){
LOGGER.info("Fruit eat[{}]. ", event);
}
}
@Subscribe
public void eat(Apple event){
if (LOGGER.isInfoEnabled()){
LOGGER.info("Apple eat[{}]. ", event);
}
}
}
3. 把Listener注册到EventBus上:eventBus.register(listener)
- 获得listener对应的所有订阅方法Multimap<Class<?>, EventSubscriber>
- 从methodsInListener并发map中获取事件对应的事件订阅者set
private final SubscriberFindingStrategy finder = new AnnotatedSubscriberFinder();
private final SetMultimap<Class<?>, EventSubscriber> subscribersByType =
HashMultimap.create();
public void register(Object listener) {
//1. 获取该监听器类型对应的所有订阅方法,key是事件类型,value是订阅者集合
//Multimap实例(它是Google Guava集合框架提供的一个多值Map类型,也就是说一个key可以对应多个
//value),该Multimap用于存储事件类型对应的该订阅者内所有关于该事件的处理器方法集合,
//其key为事件的Class类型。
Multimap<Class<?>, EventSubscriber> methodsInListener =
finder.findAllSubscribers(listener);
subscribersByTypeLock.writeLock().lock();
try {
//2. 从methodsInListener并发map中获取事件对应的事件订阅者set
subscribersByType.putAll(methodsInListener);
}
finally {
subscribersByTypeLock.writeLock().unlock();
}
}
3.1 获得listener对应的所有订阅方法Multimap<Class<?>, EventSubscriber>
3.1.1 findAllSubscribers(listener)
SubscriberFindingStrategy 接口,通过实现类AnnotatedSubscriberFinder(收集所有被@Subscribe注解的订阅者方法)实例化finder,fider.findAllSubscribers(listener)
过程:
- 通过反射得到listener的class类
- 从缓存中获取listener类型对应的所有订阅方法
- 将订阅方法封装为一个EventSubscriber
- 事件类eventType<-->事件订阅方法eventSbuscriber
class AnnotatedSubscriberFinder implements SubscriberFindingStrategy {
/**
* 返回所有该监听器订阅者,以事件分组
*所有很有可能某个订阅者其父类(或者父类实现的某个接口)也订阅了某个事件。
*因此这里的查找需要顺着继承链向上查找父类的方法是否也被注解标注
*/
@Override
public Multimap<Class<?>, EventSubscriber> findAllSubscribers(Object listener) {
Multimap<Class<?>, EventSubscriber> methodsInListener = HashMultimap.create();
//1.通过反射得到listener的class类
Class<?> clazz = listener.getClass();
//2.从缓存中获取该监听器类型对应的所有订阅方法,遍历塞进Multimap
for (Method method : getAnnotatedMethods(clazz)) {
Class<?>[] parameterTypes = method.getParameterTypes();
Class<?> eventType = parameterTypes[0];
//将订阅方法封装为一个EventSubscriber
EventSubscriber subscriber = makeSubscriber(listener, method);
//事件类eventType<->事件订阅方法EventSubscriber
methodsInListener.put(eventType, subscriber);
}
return methodsInListener;
}
//从缓存中取 listener中订阅方法的不可变列表
private static ImmutableList<Method> getAnnotatedMethods(Class<?> clazz) {
try {
return subscriberMethodsCache.getUnchecked(clazz);
} catch (UncheckedExecutionException e) {
throw Throwables.propagate(e.getCause());
}
}
private static final LoadingCache<Class<?>, ImmutableList<Method>> subscriberMethodsCache =
CacheBuilder.newBuilder()
.weakKeys()
.build(new CacheLoader<Class<?>, ImmutableList<Method>>() {
@Override
public ImmutableList<Method> load(Class<?> concreteClass) throws Exception {
return getAnnotatedMethodsInternal(concreteClass);
}
});
//一个线程安全的缓存,包含从每个类到该类中的所有方法和所有超类的映射,这些超类都用
//{@code @Subscribe}注释。缓存是跨该类的所有实例共享的;如果创建了多个EventBus实例,
//并且在所有这些实例上注册了同一个类的对象,这将大大提高性能。
//所有很有可能某个订阅者其父类(或者父类实现的某个接口)也订阅了某个事件。
//因此这里的查找需要顺着继承链向上查找父类的方法是否也被注解标注
private static ImmutableList<Method> getAnnotatedMethodsInternal(Class<?> clazz) {
//获取超类class集合
Set<? extends Class<?>> supers = TypeToken.of(clazz).getTypes().rawTypes();
Map<MethodIdentifier, Method> identifiers = Maps.newHashMap();
//遍历超类
for (Class<?> superClazz : supers) {
//遍历超类中的所有定义的方法
for (Method superClazzMethod : superClazz.getMethods()) {
//如果方法上有@Subscribe注解
if (superClazzMethod.isAnnotationPresent(Subscribe.class)
&& !superClazzMethod.isBridge()) {
// 方法的参数类型数组
Class<?>[] parameterTypes = superClazzMethod.getParameterTypes();
// 校验:事件订阅方法必须只能有一个参数,即事件类
if (parameterTypes.length != 1) {
throw new IllegalArgumentException("Method " + superClazzMethod
+ " has @Subscribe annotation, but requires " + parameterTypes.length
+ " arguments. Event subscriber methods must require a single argument.");
}
// 封装方法定义对象
MethodIdentifier ident = new MethodIdentifier(superClazzMethod);
// 去重并添加进map
if (!identifiers.containsKey(ident)) {
identifiers.put(ident, superClazzMethod);
}
}
}
}
// map转ImmutableList
return ImmutableList.copyOf(identifiers.values());
}
}
3.1.2 EventSubscriber订阅器
class EventSubscriber {
/** Object sporting the subscriber method. */
/** 监听器 listener*/
private final Object target;
/** Subscriber method. */
/** 订阅者方法 */
private final Method method;
/**
* Creates a new EventSubscriber to wrap {@code method} on @{code target}.
*
* @param target object to which the method applies.
* @param method subscriber method.
*/
EventSubscriber(Object target, Method method) {
Preconditions.checkNotNull(target,
"EventSubscriber target cannot be null.");
Preconditions.checkNotNull(method, "EventSubscriber method cannot be null.");
this.target = target;
this.method = method;
method.setAccessible(true);
}
/**
* Invokes the wrapped subscriber method to handle {@code event}.
*
* @param event event to handle
* @throws InvocationTargetException if the wrapped method throws any
* {@link Throwable} that is not an {@link Error} ({@code Error} instances are
* propagated as-is).
*/
public void handleEvent(Object event) throws InvocationTargetException {
checkNotNull(event);
try {
method.invoke(target, new Object[] { event });
} catch (IllegalArgumentException e) {
throw new Error("Method rejected target/argument: " + event, e);
} catch (IllegalAccessException e) {
throw new Error("Method became inaccessible: " + event, e);
} catch (InvocationTargetException e) {
if (e.getCause() instanceof Error) {
throw (Error) e.getCause();
}
throw e;
}
}
}
3.2 获取事件对应的事件订阅者Set
private final SetMultimap<Class<?>, EventSubscriber> subscribersByType =
HashMultimap.create();
private final ReadWriteLock subscribersByTypeLock = new ReentrantReadWriteLock();
public void register(Object object) {
...
subscribersByTypeLock.writeLock().lock();
try {
subscribersByType.putAll(methodsInListener);
} finally {
subscribersByTypeLock.writeLock().unlock();
}
}
/**
* Stores all key-value pairs of {@code multimap} in this multimap, in the
* order returned by {@code multimap.entries()}.
*
* @return {@code true} if the multimap changed
*/
boolean putAll(Multimap<? extends K, ? extends V> multimap);
4. 事件总线发布事件,触发监听器方法:eventBus.post(event)
- 根据缓存得到event及其子类(eventType)
- 将每种eventType对应的EventSubscriber添加进转发队列
- 转发队列中event
/**
* Posts an event to all registered subscribers. This method will return
* successfully after the event has been posted to all subscribers, and
* regardless of any exceptions thrown by subscribers.
*
* <p>If no subscribers have been subscribed for {@code event}'s class, and
* {@code event} is not already a {@link DeadEvent}, it will be wrapped in a
* DeadEvent and reposted.
*
* @param event event to post.
*/
public void post(Object event) {
//1.根据缓存得到event及其子类
Set<Class<?>> dispatchTypes = flattenHierarchy(event.getClass());
boolean dispatched = false;
for (Class<?> eventType : dispatchTypes) {
subscribersByTypeLock.readLock().lock();
try {
Set<EventSubscriber> wrappers = subscribersByType.get(eventType);
if (!wrappers.isEmpty()) {
dispatched = true;
for (EventSubscriber wrapper : wrappers) {
//2.将每种eventType对应的EventSubscriber添加进转发队列
enqueueEvent(event, wrapper);
}
}
} finally {
subscribersByTypeLock.readLock().unlock();
}
}
//如果该事件即没有订阅者,也没事DeadEvent,那么封装成DeadEvent并重新发布
if (!dispatched && !(event instanceof DeadEvent)) {
post(new DeadEvent(this, event));
}
//3.转发队列中event
dispatchQueuedEvents();
}
4.1 根据缓存得到event及其子类Set<Class<?>> dispatchTypes
/**
* Flattens a class's type hierarchy into a set of Class objects. The set
* will include all superclasses (transitively), and all interfaces
* implemented by these superclasses.
*
* @param concreteClass class whose type hierarchy will be retrieved.
* @return {@code clazz}'s complete type hierarchy, flattened and uniqued.
*/
@VisibleForTesting
Set<Class<?>> flattenHierarchy(Class<?> concreteClass) {
try {
return flattenHierarchyCache.getUnchecked(concreteClass);
} catch (UncheckedExecutionException e) {
throw Throwables.propagate(e.getCause());
}
}
/**
* A thread-safe cache for flattenHierarchy(). The Class class is immutable. This cache
* is shared across all EventBus instances, which greatly improves performance if
* multiple such instances are created and objects of the same class are posted
*on all of them.
*/
private static final LoadingCache<Class<?>, Set<Class<?>>> flattenHierarchyCache =
CacheBuilder.newBuilder()
.weakKeys()
.build(new CacheLoader<Class<?>, Set<Class<?>>>() {
@SuppressWarnings({"unchecked", "rawtypes"}) // safe cast
@Override
public Set<Class<?>> load(Class<?> concreteClass) {
return (Set) TypeToken.of(concreteClass).getTypes().rawTypes();
}
});
4.2 将EventSubscriber添加进转发队列:enqueueEvent(event, wrapper);
每个线程待转发队列eventsToDispatch -> .get()得到Queue<EventWithSubscriber> -> .offer()事件添加进队列
/**
* Queue the {@code event} for dispatch during
* {@link #dispatchQueuedEvents()}. Events are queued in-order of occurrence
* so they can be dispatched in the same order.
*/
void enqueueEvent(Object event, EventSubscriber subscriber) {
eventsToDispatch.get().offer(new EventWithSubscriber(event, subscriber));
}
/** queues of events for the current thread to dispatch */
private final ThreadLocal<Queue<EventWithSubscriber>> eventsToDispatch =
new ThreadLocal<Queue<EventWithSubscriber>>() {
@Override protected Queue<EventWithSubscriber> initialValue() {
return new LinkedList<EventWithSubscriber>();
}
};
4.3 转发队列中event事件:dispatchQueuedEvents()
/**
* Drain the queue of events to be dispatched. As the queue is being drained,
* new events may be posted to the end of the queue.
*/
void dispatchQueuedEvents() {
// don't dispatch if we're already dispatching, that would allow reentrancy
// and out-of-order events. Instead, leave the events to be dispatched
// after the in-progress dispatch is complete.
if (isDispatching.get()) {
return;
}
isDispatching.set(true);
try {
//得到EventWithSubscriber事件队列
Queue<EventWithSubscriber> events = eventsToDispatch.get();
EventWithSubscriber eventWithSubscriber;
// 迭代从线程队列中取事件
while ((eventWithSubscriber = events.poll()) != null) {
dispatch(eventWithSubscriber.event, eventWithSubscriber.subscriber);
}
} finally {
isDispatching.remove();
eventsToDispatch.remove();
}
}
/** true if the current thread is currently dispatching an event */
//每个线程的转发状态,用于避免重入事件转发,初始化状态为fasle,即不在转发。
private final ThreadLocal<Boolean> isDispatching =
new ThreadLocal<Boolean>() {
@Override protected Boolean initialValue() {
return false;
}
};
/**
* Dispatches {@code event} to the subscriber in {@code wrapper}. This method
* is an appropriate override point for subclasses that wish to make
* event delivery asynchronous.
*
* @param event event to dispatch.
* @param wrapper wrapper that will call the subscriber.
*/
void dispatch(Object event, EventSubscriber wrapper) {
try {
//执行订阅者方法
wrapper.handleEvent(event);
} catch (InvocationTargetException e) {
try {
subscriberExceptionHandler.handleException(
e.getCause(),
new SubscriberExceptionContext(
this,
event,
wrapper.getSubscriber(),
wrapper.getMethod()));
} catch (Throwable t) {
// If the exception handler throws, log it. There isn't much else to do!
Logger.getLogger(EventBus.class.getName()).log(Level.SEVERE,
String.format(
"Exception %s thrown while handling exception: %s", t,
e.getCause()),
t);
}
}
}
4.4 DeadEvent
如果事件既没有订阅者,也不是DeadEvent,那么封装成DeadEvent并重新发布。
- source:事件源(通常指发布事件的EventBus对象)
- event:事件对象
public DeadEvent(Object source, Object event) {
this.source = checkNotNull(source);
this.event = checkNotNull(event);
}
5. 总结
eventType<->EventSubscriber:SetMultimap<Class<?>, EventSubscriber> subscribersByType
private final SetMultimap<Class<?>, EventSubscriber> subscribersByType =
HashMultimap.create();
register(listener):找到listener类及超类的订阅方法(注解)->根据参数得到eventType->对应EventSubscriber
注册了一个Listener,使用eventBus发送消息Listener父类的Subscribe也会对此消息进行处理。
post(event)(线程异步分发事件):得到event类及子类所有eventType->将对应EventSubscriber添加进分发队列->分发event
当作为参数的event之间有继承关系时,使用eventBus发送消息,event父类的listener也会对此消息进行处理。
传统观察者模式和EventBus的区别:
监听者管理 | 监听特定事件 | 把监听者注册到生产者 | 按事件超类监听 | 检测没有监听者的事件 | 分发事件 | |
传统观察者模式 | 用列表管理监听者,还要考虑线程同步;或者使用工具类 | 定义相应的事件监听者类 | 调用事件生产者的register方法,开发者必须知道所有事件生产者的类型,才能正确地注册监听者 | 很困难,需要开发者自己去实现匹配逻辑 | 在每个事件分发方法中添加逻辑代码 | 开发者自己写代码,包括事件类型匹配、异常处理、异步分发 |
EventBus | 内部已经实现了监听者管理 | 以自定义Event为唯一参数创建方法, 并用Subscribe注解标记。 | EventBus.register(Object) | EventBus自动把事件分发给事件超类的监听者 | EventBus会把所有发布后没有监听者处理的事件包装为DeadEvent | EventBus.post(Object) 异步分发可以直接用EventBus的子类AsyncEventBus |
原文:
https://blog.csdn.net/yanghua_kobe/article/details/46317297
https://www.cnblogs.com/dennyzhangdd/p/9324483.html
https://blog.csdn.net/wangdong5678999/article/details/80561198#1_Listener_9