简介
本周我将开启一个系列篇,围绕着EventBus的 运行流程 + 源码 的形式进行精细剖析,分为一下几篇
EventBus系列『一』——注册与注销
EventBus系列『二』——Post与postSticky事件的发布与接收
EventBus系列『番外』——认真剖析 『PendingPostQueue』队列的实现思想
EventBus准备提要
在讲解我们注册和注销之前EventBus需要做一些准备工作
[1] 将一些特殊重要属性封装成 ,PostingThreadState
类型,并将PostingThreadState
的实例放置于ThreadLocal
final static class PostingThreadState {
final List
[2] 准备一个Map集合将订阅事件参数类与事件参数的所有父类集合关联起来
private static final Map, List>> eventTypesCache = new HashMap<>();
[3]创建一个EventBus
实例,初始化里面的参数
//创建单例
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
public EventBus() {
this(DEFAULT_BUILDER);
}
EventBus(EventBusBuilder builder) {
//创建EventBus的Log打印
logger = builder.getLogger();
//用于将订阅方法的EventType属性与订阅事件集合
//(CopyOnWriteArrayList)关联,缓存进HashMap
subscriptionsByEventType = new HashMap<>();
////用于将注册类与注册类中的订阅方法的类型集合(List>)关联,缓存进HashMap
typesBySubscriber = new HashMap<>();
// //用于缓存粘性事件,将粘性事件的消息实体类 与 粘性事件关联 存入 ConcurrentHashMap中
stickyEvents = new ConcurrentHashMap<>();
//创建EventBus主线程实例
mainThreadSupport = builder.getMainThreadSupport();
//创建主线程Poster,用于将事件放入主线程处理队列
mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
//创建 后台的Poster ,用于将事件放入后台处理队列
backgroundPoster = new BackgroundPoster(this);
//创建异步Poster ,用于将事件放入异步处理队列
asyncPoster = new AsyncPoster(this);
//获取订阅事件注册类的索引数量
indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
//创建用于查询订阅事件的实例
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;
//获取线程池实例
executorService = builder.executorService;
}
EventBus注册
我们先来看一下
EventBus
注册流程图:
我将历程图分为两个部分 第一部分为【主流程】而 第二部分为【事件集合的遍历流程】 以方便大家理解,那么我们接下来就依据流程图为大家逐一讲解.
[1] EventBus.getDefault()
获取EventBus
实例
[2] 进入 EventBus.java
#方法 EventBus.getDefault().register(this)
函数
public void register(Object subscriber) {
Class> subscriberClass = subscriber.getClass(); //获取 subscriber类对象 即 上层参数 [ this ] 的类对象
// [ 2.1 ] 检索出注册类中的所有 [ 订阅信息方法]
List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
//由于订阅事件必须在同步块中实现,所以这里使用synchroniezd关键字锁住
synchronized (this) {
//使用for循环,将事件逐个发布
for (SubscriberMethod subscriberMethod : subscriberMethods) {
// [2.2] 发布订阅信息
subscribe(subscriber, subscriberMethod);
}
}
}
- 检索出注册类中的所有 [ 订阅信息方法]
- 遍历获取的订阅信息集合,并逐一发布订阅信息
[2.1] 检索注册类中的所有订阅方法 进入 SubscriberMethodFinder.java
执行
subscriberMethodFinder.findSubscriberMethods(subscriberClass)
//全局 [ 订阅事件 ] 方法缓存Map
private static final Map, List> METHOD_CACHE = new ConcurrentHashMap<>();
List findSubscriberMethods(Class> subscriberClass) {
//根据注册类class,从缓存中获取订阅事件链
List subscriberMethods = METHOD_CACHE.get(subscriberClass);
//已缓存过,直接返回订阅事件List
if (subscriberMethods != null) {
return subscriberMethods;
}
//是否使用通过反射生成的索引,默认false
if (ignoreGeneratedIndex) {
//通过索引类取出注册类的订阅事件信息 @问题:注册时如何通过索引类获取注册类的订阅事件集合
subscriberMethods = findUsingReflection(subscriberClass);
} else {
//使用反射获取订阅事件链List @问题:注册时如何通过反射获取注册类的订阅事件集合
subscriberMethods = findUsingInfo(subscriberClass);
}
//如果获取的事件链List为空,则抛出异常
if (subscriberMethods.isEmpty()) {
throw new EventBusException("Subscriber " + subscriberClass
+ " and its super classes have no public methods with the @Subscribe annotation");
} else {
//将注册类与事件链List关联起来,放入缓存
METHOD_CACHE.put(subscriberClass, subscriberMethods);
//返回订阅链list
return subscriberMethods;
}
}
- 根据注册类class,从缓存中获取订阅事件链,若获取的订阅信息方法集合已存在,则直接返回该集合。
- 是否使用通过反射生成的索引,默认false。
- 若使用反射生成索引 则通过索引类取出注册类的订阅事件信息,否则通过反射获取订阅信息集合
- 将注册类与事件链List关联起来,放入缓存
[2.2] 在 函数 subscribe(subscriber, subscriberMethod)
中发布订阅事件
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class> eventType = subscriberMethod.eventType; //获取事件的类型
Subscription newSubscription = new Subscription(subscriber, subscriberMethod); //生成订阅实例
CopyOnWriteArrayList subscriptions = subscriptionsByEventType.get(eventType); //根据事件类型,从Map缓存中查找订阅链表
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions); //保存入Map中
} else {
if (subscriptions.contains(newSubscription)) { //在订阅链表中匹配当前订阅事件
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType); //抛出一个EventBusException异常,告诉用户该事件已经被订阅过了
}
}
//将当前的订阅事件放入 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;
}
}
//通过注册类从typesBySubscriber中获取获取对应的subscribedEvents
List> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
//将注册类和注册类中的订阅方法的evenType属性集合关联起来放入 typesBySubscriber 中
typesBySubscriber.put(subscriber, subscribedEvents);
}
//将订阅方法的eventType属性放入subscribedEvents集合
subscribedEvents.add(eventType);
//判断当前订阅事件方法是不是粘性事件
if (subscriberMethod.sticky) {
if (eventInheritance) { //判断是不是继承事件
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);
}
}
}
- 首先根据传递的订阅信息和完整的订阅方法集合,生成订阅实例
- 根据事件类型,从
subscriptionsByEventType
Map缓存中查找对应的订阅链表- 若获取的订阅链表为空,则将当前订阅方法的
eventType
属性与空的CopyOnWriteArrayList
关联起来放入subscriptionsByEventType
Map集合,之后我们会向CopyOnWriteArrayList
的实例对象subscribedEvents
填充数据,保证他们之间关联准确性- 将当前的订阅事件放入
subscriptions
- 通过注册类从
typesBySubscriber
中获取获取对应的subscribedEvents
- 将注册类和注册类中的订阅方法的
evenType
属性集合关联起来放入typesBySubscriber
中- 将订阅方法的eventType属性放入subscribedEvents集合
- 判断当前订阅事件方法是不是粘性事件,若是则 从缓存
stickyEvents
获取,并将其当做一个新事件发布到总线
EventBus注销
EventBus
注销流程图 :
与注册相比注销流程就显得简单的多了,主要的就是将事件及注册类的移除,接下来我们结合源码进行了解一下
[1] EventBus.getDefault()
获取EventBus
实例
[2] 进入 EventBus.java
#方法 EventBus.getDefault().unregister(this)
函数
public synchronized void unregister(Object subscriber) {
//通过注册类从typesBySubscriber中获取对应的订阅事件类型集合
List> subscribedTypes = typesBySubscriber.get(subscriber);
if (subscribedTypes != null) {
for (Class> eventType : subscribedTypes) {
// [ 2.1 ] 移除与当前注册类相同的对象中包含的订阅事件
unsubscribeByEventType(subscriber, eventType);
}
//从typesBySubscriber中移除注册类
typesBySubscriber.remove(subscriber);
} else {
logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
- 通过注册类从typesBySubscriber中获取对应的订阅事件类型集合
- 遍历集合移除与当前注册类相同的对象中包含的订阅事件
- 遍历结束后 即代表注册类中的所有的订阅都已经被移除后从
typesBySubscriber
中移除注册类
[ 2.1 ] 移除与当前注册类相同的对象中包含的订阅事件
private void unsubscribeByEventType(Object subscriber, Class> eventType) {
//通过eventType从subscriptionsByEventType获取订阅事件集合
List 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--;
}
}
}
}
- 通过
eventType
从subscriptionsByEventType
获取订阅事件集合- 遍历集合,判定当前订阅事件的注册类是否与要取消的注册类对象相同,若判定成功则将订阅事件从集合中移除。
总结
本篇我们详细讲解了EventBus
的 注册 与 注销流程,从中可以发现 :
- 在注销流程其实没什么复杂操作,就是讲订阅事件和注册类进行了逐一移除.
- 注册流程就相对复杂许多,在注册的最后我们发现了对
postSticky 粘性事件
的特殊处理,而并没有对POST 事件
做什么特殊处理。