EventBusEventBus 源码解析 ( 注册订阅者 | 注册订阅方法详细过程 )
Posted 韩曙亮
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了EventBusEventBus 源码解析 ( 注册订阅者 | 注册订阅方法详细过程 )相关的知识,希望对你有一定的参考价值。
前言
在上一篇博客 【EventBus】EventBus 源码解析 ( 注册订阅者 | 订阅方法 | 查找订阅方法 ) 中 , 介绍了注册订阅者的第一个步骤 , 查找订阅者 ;
首先要获取当前的 List<SubscriberMethod> subscriberMethods
订阅方法集合 , 该集合从 SubscriberMethodFinder
的缓存 Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE
获取 , 该缓存是在第一次获取订阅方法时生成 , 之后获取都直接从缓存中获取即可 ;
METHOD_CACHE
缓存生成策略 : 如果是第一次获取订阅方法 , 缓存是空的 , 此时通过反射获取该 订阅类 所有符合条件的订阅方法 , 将 订阅方法 封装到 SubscriberMethod
中 , 然后添加到 findState.subscriberMethods
集合中 ;
一、EventBus 注册订阅者
获取到订阅方法集合后 , 然后开始遍历订阅方法集合 , 调用 subscribe
方法 , 注册订阅者 ;
public class EventBus {
/**
* 注册给定订阅服务器以接收事件。订阅者一旦对接收事件不再感兴趣,就必须调用{@link#unregister(Object)}。
* <p/>
* 订阅服务器具有必须由{@link Subscribe}注释的事件处理方法。
* {@link Subscribe}注释还允许类似{@link ThreadMode}和优先级的配置。
*/
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
// 1. 获取 订阅者 集合 , 查找当前订阅类中符合条件的订阅方法集合
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
// 2. 遍历 订阅者 集合 , 进行事件订阅 , 保存数据 , 这些数据就是一些映射关系
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
}
二、注册订阅方法的具体过程
获取订阅方法参数类型 , 可以是任意类型 , 自定义的 MessageEvent 消息类型 ;
subscribe(Object subscriber, SubscriberMethod subscriberMethod)
封装 Subscription 对象 , Subscription 中封装了一个订阅者对象和一个订阅方法 ;
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
从 Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType
成员变量中获取 Subscription
集合 , 该 HashMap 的用途是保存当前的 eventType 对应的所有的订阅类和订阅方法 , 以便消息中心获取对应类型的消息后 , 可以顺利将其传递给相应订阅方法 ;
- Key 是事件类型对象 ;
- Value 是 Subscription 集合 , Subscription 中封装了一个订阅者对象和一个订阅方法
CopyOnWriteArrayList 是线程安全集合 , 写入数据的时候会拷贝一份出来 , 不会影响其它线程读取该集合的操作
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);
}
}
如果订阅方法设置了优先级注解属性 , 则处理订阅方法优先级 , 重新进行排列 ;
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;
}
}
向 Map<Object, List<Class<?>>> typesBySubscriber
成员变量 添加 订阅者对象 - 订阅方法参数类型集合 键值对 , 该成员变量的作用是取消注册时查找相关的订阅方法 ;
// Map<Object, List<Class<?>>> typesBySubscriber
// Key : 订阅者对象
// Value : 订阅方法参数类型集合
// 取消注册时 , 需要使用该方法
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
注册订阅方法代码示例 :
/**
* EventBus是Java和android的中央发布/订阅事件系统。
* 事件被发布({@link#post(Object)})到总线,总线将其传递给具有匹配处理程序的订阅者
* 事件类型的方法。
* 要接收事件,订阅者必须使用{@link#register(Object)}将自己注册到总线。
* 一旦注册,订阅服务器将接收事件,直到调用{@link#unregister(Object)}。
* 事件处理方法必须由{@link Subscribe}注释,必须是公共的,不返回任何内容(void),
* 并且只有一个参数(事件)。
*/
public class EventBus {
// 该方法必须在同步代码块中调用
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
// 获取订阅方法参数类型 , 可以是任意类型 , 自定义的 MessageEvent 消息类型
Class<?> eventType = subscriberMethod.eventType;
// Subscription 中封装了一个订阅者对象和一个订阅方法
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
// Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType 成员变量
// Key 是事件类型对象
// Value 是 Subscription 集合 , Subscription 中封装了一个订阅者对象和一个订阅方法
// 该 HashMap 的用途是保存当前的 eventType 对应的所有的订阅类和订阅方法
// 以便消息中心获取对应类型的消息后 , 可以顺利将其传递给相应订阅方法
// CopyOnWriteArrayList 是线程安全集合 , 写入数据的时候会拷贝一份出来 , 不会影响其它线程读取该集合的操作
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);
}
}
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;
}
}
// Map<Object, List<Class<?>>> typesBySubscriber
// Key : 订阅者对象
// Value : 订阅方法参数类型集合
// 取消注册时 , 需要使用该方法
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
// 处理方法粘性相关业务逻辑
if (subscriberMethod.sticky) {
if (eventInheritance) {
// 必须考虑eventType的所有子类的现有粘性事件。
// 注意:迭代所有事件可能效率低下,因为有很多粘性事件,
// 因此,应更改数据结构以允许更高效的查找
// 例如,存储超类子类的附加映射: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 类
Subscription 类中封装了一个订阅者对象和一个订阅方法
final class Subscription {
final Object subscriber;
final SubscriberMethod subscriberMethod;
}
以上是关于EventBusEventBus 源码解析 ( 注册订阅者 | 注册订阅方法详细过程 )的主要内容,如果未能解决你的问题,请参考以下文章
EventBusEventBus 源码解析 ( 注册订阅者 | 订阅方法 | 查找订阅方法 )
EventBusEventBus 源码解析 ( 事件发送 | 线程池中执行订阅方法 )
EventBusEventBus 源码解析 ( 事件发送 | postToSubscription 方法 | EventBus 线程模式处理细节 )
EventBusEventBus 源码解析 ( 事件发送 | EventBus.post 方法 | EventBus.postSingleEvent 方法 )
EventBusEventBus 源码解析 ( 注册订阅者总结 | 从封装的数据结构角度分析 EventBus )
EventBusEventBus 源码解析 ( 事件发送 | 发布线程为 子线程 切换到 主线程 执行订阅方法的过程分析 )