今天这篇文章从源码去简单理解EventBus
的实现。
从源码解读的主要模块有:
- 入口
- 注册、注销
- 普通消息
- 粘性消息
入口:
入口函数如下:
EventBus.getDefault()
进入源码我们可以看到EventBus
是单例实现的,源码如下:
static volatile EventBus defaultInstance;
/** Convenience singleton for apps using a process-wide EventBus instance. */
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
这是一个双重检查单例模式(DCL), 还用到了关键字volatile
volatile
是什么?作用又是什么?
推荐查看这篇文章 Java并发编程:volatile关键字解析
在访问volatile变量时不会执行加锁操作,因此也就不会使执行线程阻塞,volatile 关键字是一种比 sychronized 关键字更轻量级的同步机制。
synchronized
关键字
synchronized
是什么?作用是什么?
推荐查看这篇文章 Java synchronized 详解
在构造函数中又调用了一个带参的构造函数,主要是用于用户自定义的配置,如果没配置就使用默认的配置信息,源码如下:
// 默认的builder
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
// 调用下方的构造函数
public EventBus() {
this(DEFAULT_BUILDER);
}
// 初始化操作
EventBus(EventBusBuilder builder) {
logger = builder.getLogger();
subscriptionsByEventType = new HashMap<>();
typesBySubscriber = new HashMap<>();
stickyEvents = new ConcurrentHashMap<>();
mainThreadSupport = builder.getMainThreadSupport();
mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
backgroundPoster = new BackgroundPoster(this);
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;
}
总结:使用单例双重检查锁模式。使用 synchronized
和volatila
关键字是为了降低EventBus
对象在并发操作时可能出现的原子性问题、可见性问题、有序性问题
register 操作
注册观察者: 先从订阅者集合中找到订阅对象,找到则直接返回,没有找到则通过一个index 值再去查找。找不到则通过反射获取到订阅者的信息,将其放入缓存中,再返回。然后判断订阅者是否已经被注册,如果已经注册过会抛出异常。否则再判断是否是粘性事件,然后就准备发送了..
/**
* Registers the given subscriber to receive events. Subscribers must call {@link #unregister(Object)} once they
* are no longer interested in receiving events.
* <p/>
* Subscribers have event handling methods that must be annotated by {@link Subscribe}.
* The {@link Subscribe} annotation also allows configuration like {@link
* ThreadMode} and priority.
*/
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
// 用 subscriberMethodFinder 提供的方法,找到在 subscriber 这个类里面订阅的内容。
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
// 为什么加锁?保证代码块的同步性,加锁会安全,但是性能会慢一些
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
在findSubscriberMethods
中找到订阅的集合, findSubscriberMethods
方法的实现如下:
/**
* 键值对:键是类对象,值是List<SubscriberMethod> 集合对象。SubscriberMethod 是 每个订阅者与回调方法的关系对象,里面封装了包括优先级,是否接收黏性事件和所在线程等信息。
*/
private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>();
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
// 在缓存中查找 SubscriberMethod 集合
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
//ignoreGeneratedIndex 属性表示是否忽略注解器生成的 MyEventBusIndex,ignoreGeneratedIndex 默认为false
if (ignoreGeneratedIndex) {
// 通过反射查找
subscriberMethods = findUsingReflection(subscriberClass);
} else {
// 查找
subscriberMethods = findUsingInfo(subscriberClass);
}
//在获得subscriberMethods以后,如果订阅者中不存在@Subscribe注解并且为public的订阅方法,则会抛出异常。
if (subscriberMethods.isEmpty()) {
throw new EventBusException("Subscriber " + subscriberClass
+ " and its super classes have no public methods with the @Subscribe annotation");
} else {
// 放入缓存
METHOD_CACHE.put(subscriberClass, subscriberMethods);
return subscriberMethods;
}
}
ignoreGeneratedIndex
默认为``false`,源码如下:
/** Forces the use of reflection even if there's a generated index (default: false). */
public EventBusBuilder ignoreGeneratedIndex(boolean ignoreGeneratedIndex) {
this.ignoreGeneratedIndex = ignoreGeneratedIndex;
return this;
}
所以先查看这个findUsingInfo
方法的实现
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
findState.subscriberInfo = getSubscriberInfo(findState);
if (findState.subscriberInfo != null) {
SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
for (SubscriberMethod subscriberMethod : array) {
if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
findState.subscriberMethods.add(subscriberMethod);
}
}
} else {
findUsingReflectionInSingleClass(findState);
}
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
发送事件
post 从 PostingThreadState 对象中取出事件队列,然后将事件队列交给 postSingleEvent 方法进行处理。该方法会取出事件对应的订阅对象,如果发送的事件实在主线程中,那么通过反射直接运行订阅的方法,如果不是则加入到主线程队列中,通过Handler 将订阅方法切换到主线程执行。
事件取消
unregister :之前在注册的时候有将订阅的事件添加到一个 map 集合中,所以事件的取消这里直接便利 map 集合,移除对应的订阅事件即可。
网友评论