EventBus 使用及源码分析

EventBus 是我们日常开发中使用很频繁的一个三方库,原因很简单,使用 EventBus 可以很容实现解耦,不需要设置很多回调接口,如在 Fragment 和 Activity 之间的通讯,不同 Activity 之间、不同的线程之间,不同的 Service 之间等,使用 EventBus 会减少很多代码,而且看起来更加简洁,在 Android Studio 中点击左侧的小安卓图标可以很方便从事件发送的位置跳转到事件接收的位置,或者从事件接收的位置跳转到事件的发送位置,如果你还没试过这个小操作,那你需要了解一下了。

1. EventBus 使用

(1)添加依赖

implementation 'org.greenrobot:eventbus:3.1.1'

(2)创建事件的实体类

对于每个事件,一般我们用不同的实体类来进行区分,在实体类中我们还可以设置相关的字段,来进行参数的传递。

public class MessageEvent {

}

(3)订阅者完成注册和事件接收

事件的订阅者,需要完成这两个步骤,注册和编写事件接受的方法。如果没有注册,则接收不到事件,如果没有设置接收事件方法,程序会报错,开始使用 EventBus 的童鞋应该会遇到这个问题,后面分析源码时,我们再看为什么会报错?

如果使用订阅者有生命周期,还需要在销毁前取消注册,如 Activity。

注册和取消注册:

@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}

@Override
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}

事件接收

@Subscribe(threadMode = ThreadMode.MAIN)  
public void onMessageEvent(MessageEvent event) {
// TODO 
};

(4)发送事件

EventBus.getDefault().post(new MessageEvent());

EventBus 使用起来就是这么简单,代码简洁,而且能够解耦。实际上 EventBus 的核心就是 订阅者 - 发布模型。这里做一个比喻:可以想像一下,你经常去报刊亭买你喜欢的科技杂志,但是你不知道这个杂志什么时候可以买,你想在报刊亭有这种杂志时就能够及时买到。然后你就留下电话给报刊亭老板,新一期的科技杂志到了,老板就给你打电话通知你来买,这就是一个很简单的发布订阅模型。再有有很多像你一样的报刊爱好者,都留下了电话,但是老板记不住怎么办?所以需要有记录一下,哪个人订阅了哪种杂志,需要对应起来,有可能一个杂志只有一个人订阅,也有可能一个杂志有多个人订阅。

eventbus

下面我们就来按照上面的订阅者模型来分析一下源码,EventBus 的源码不多,可以就我们使用过程为出发点,分析 EventBus 是如何创建,注册、发送、接收以及取消注册的。

2. EventBus 源码分析

EventBus 中类的关系图

eventbus_class

2.1 构建

EventBus.getDefault() 会返回一个 EventBus 实例,我们知道对于一个应用程序,使用订阅发布模型,一般要由一个实例来完成所有事件的管理和执行,所以 EventBus 应该是单例模式,EventBus 采用双重检验锁的单例模式。

public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}

当 EventBus 第一次初始化时,采用 Builder 模式来来构建实例,主要初始化多个集合,订阅者的各个执行方法,订阅个,订阅者事件类型,需要分门别类的放到各个集合中。


EventBus(EventBusBuilder builder) {
logger = builder.getLogger();
// 订阅者事件大集合,根据事件类型分类的 Map 集合
subscriptionsByEventType = new HashMap<>();
// 按订阅者对象分类的集合,便于后面取消订阅
typesBySubscriber = new HashMap<>();
// 粘性事件集合
stickyEvents = new ConcurrentHashMap<>();
mainThreadSupport = builder.getMainThreadSupport();
// 3 种 Poster,实现主线程处理,后台处理,异步处理
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;
}

2.2 注册

有了 EventBus 实例,就可以进行注册。代码很清晰,首先通过注册的对象,获取该对象的 Class,然后根据这个 Class 找到这个类中的订阅事件,这里订阅事件封装到 SubscriberMethod 中,然后遍历事件集合,将对象事件对应起来注册,实际上就是保存在一个集合中。

举个小例子,如在 XxActivity 中注册了,那么 EventBus 就会查找 XxActivity.class 文件中所有打上 @Subscribe 注解的方法。

这个过程我们可以很容易的想到,应该通过反射的方式来获取接收事件的函数,源码中是通过 SubscriberMethodFinder 这个订阅者执行函数存储和查找的辅助类来完成的。

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 的 findSubscriberMethods 方法来查找注册对象中所有的事件接收函数,为了提高效率,源码中做了缓存。


List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
// 做了缓存,如果存在直接返回
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}

if (ignoreGeneratedIndex) {
// 通过反射获取
subscriberMethods = findUsingReflection(subscriberClass);
} else {
subscriberMethods = findUsingInfo(subscriberClass);
}
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 这个表示是用来表明通过哪种方式来获取注册对象的所有事件接收函数,默认情况下是通过反射来获取。在3.0版本中,EventBus提供了一个EventBusAnnotationProcessor注解处理器来在编译期通过读取@Subscribe()注解并解析,处理其中所包含的信息,然后生成java类来保存所有订阅者关于订阅的信息,这样就比在运行时使用反射来获得这些订阅者的信息速度要快。

这里我们只分析 findUsingReflection(subscriberClass) 方法,通过反射来获取注册对象的所有事件接收函数。

private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
findUsingReflectionInSingleClass(findState);
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}

这里又使用了一个辅助类 FindState,我们只看主要代码,遍历订阅者对象 subscriberClass以及它的父类们,并将遍历的结果保存到 findState 中。

private void findUsingReflectionInSingleClass(FindState findState) {
Method[] methods;
try {
// 获取这个类的所有公有方法,包括父类的方法
methods = findState.clazz.getDeclaredMethods();
} catch (Throwable th) {
methods = findState.clazz.getMethods();
findState.skipSuperClasses = true;
}
// 遍历所有方法,找到带有 @Subscribe 注解的方法
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()));
}
}
} else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
String methodName = method.getDeclaringClass().getName() + "." + method.getName();
throw new EventBusException("@Subscribe method " + methodName +
"must have exactly 1 parameter but has " + parameterTypes.length);
}
} else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
String methodName = method.getDeclaringClass().getName() + "." + method.getName();
throw new EventBusException(methodName +
" is a illegal @Subscribe method: must be public, non-static, and non-abstract");
}
}
}

findUsingReflectionInSingleClass() 方法通过遍历注册对象的类型,通过反射获取所有的公有的方法,然后根据注解找到事件接收函数的方法,然后将改方法进行封装,封装到 SubscriberMethod 中,最后添加到 findState 的 List subscriberMethods 集合中。

有几点需注意下:

(1)通过反射 getDeclaredMethods 获取所有的公有的方法,所以在编写事件接收函数时,一定是 public 的,再有需要打上 @Subscribe 注解,这个不用多说

(2) Class<?> eventType = parameterTypes[0]; 这个参数,代码中给了注释,这里再强调一下,事件接收函数一般我们只设定一个类型, EventBus 中也是根据这个类的类型来做区分的,所以这个 eventType 代表的就是我自定义类的类型,后面很多地方都会用到这个类型,不同的接收事件的区分就是根据这个类型来区分的。

@Subscribe(threadMode = ThreadMode.MAIN)  
public void onMessageEvent(MessageEvent event) {
// TODO 
};

找到了订阅者的执行函数,那么就可以将订阅者和订阅函数封装起来,将这个对象放到一个集合当中,当事件通知的时候,就从这个集合中取出来,执行相应的函数。下面来看注册过程,验证我们的想法。

public void register(Object subscriber) {
// 查找注册的订阅者的事件接收函数
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
// 开始执行注册过程
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}

将订阅者和订阅方法对应起来进行注册,这个方法需要同步执行,需要保证不能重复注册,一但重复注册,在接受事件时,订阅者的执行函数会被执行两次。

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
// 1 根据事件类型将封装的订阅对象 newSubscription 放到大集合中,大集合是一个 Map 类型
// key 是事件类型,value 是 CopyOnWriteArrayList 类型的订阅事件的集合
// 因为要保证同步,CopyOnWriteArrayList 是线程同步的
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);
} else {
if (subscriptions.contains(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
}
}
// 2 同一类型的订阅时间放在同一个集合中,即 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;
}
}

// 3 对于每个订阅者还需要维护一个集合,每个订阅者的事件类型,为什么需要有这样一个集合?我们知道,一个事件发送时,可以得到事件的类型,根据事件类型通知所有的订阅该类型的订阅。在取消注册时,需要根据订阅者对象拿到订阅的事件类型,然后再从大集合中将该对象的订阅事件移除。
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
// 如果是粘性事件,立即执行该事件,或者添加到执行队列当中
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);
}
}
}

到这里就将注册的过程分析完了,总结一下:注册时,先获取 EventBus 的单例,然后通过辅助类 查找该订阅者中的接收事件函数集合,然后将订阅者和接执行函数封装到一个对象中,遍历订事件集合完成注册,注册的过程也就是将事件保存起来,等待事件过来时,拿到事件执行函数开始执行。

eventbus_register

2.3 事件发送过程分析

事件的发送过程的触发,也是通过一句代码:

EventBus.getDefault().post(new MessageEvent());

下面就从 post 方法开始分析。

public void post(Object event) {
// 1 获取当前线程的 Post 状态
PostingThreadState postingState = currentPostingThreadState.get();
// 2 将事件加入到事件队列中
List<Object> eventQueue = postingState.eventQueue;
eventQueue.add(event);
// 3 通知订阅该事件类型的订阅者执行事件函数
if (!postingState.isPosting) {
postingState.isMainThread = isMainThread();
postingState.isPosting = true;
if (postingState.canceled) {
throw new EventBusException("Internal error. Abort state was not reset");
}
try {
while (!eventQueue.isEmpty()) {
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}

post() 事件发送后,执行 3 个步骤:

(1) 获取当前线程的 Post 状态

怎么保证是当前发送者线程呢? currentPostingThreadState 是一个 ThreadLocal 变量,不同的线程拥有不同的 PostingThreadState 对象,所以 currentPostingThreadState.get() 获取的就是发送者所处线程的 PostingThreadState 对象。

private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
@Override
protected PostingThreadState initialValue() {
return new PostingThreadState();
}
};

(2) 将事件加入到事件队列中

这里有点标书不准确,object 是我们携带的参数,并不是上面注册过程中的事件,但是通过 object 可以查找出来,因为 object.getClass() 能够得到事件的类型。

(3)通知订阅该事件类型的订阅者执行事件函数

通过这句开始执行 postSingleEvent(eventQueue.remove(0), postingState);

private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
// 1.event 允许继承的情况
if (eventInheritance) {
List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
int countTypes = eventTypes.size();
for (int h = 0; h < countTypes; h++) {
Class<?> clazz = eventTypes.get(h);
subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
}
} else {
// 2.event 不允许继承的情况
subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
}
// 有一个订阅事件执行,subscriptionFound 就不为 false,这里是指一个订阅事件都没有执行的情况
if (!subscriptionFound) {
if (logNoSubscriberMessages) {
logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
}
if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
eventClass != SubscriberExceptionEvent.class) {
post(new NoSubscriberEvent(this, event));
}
}
}

主要看下 event 允许继承的情况的情况做了哪些,会遍历 event 实现的接口以及继承的父类,添加到集合中。这么说可能有点抽象。举个栗子:如果允许继承,假设 上面介绍 EventBus 中使用的自定义类 MessageEvent 继承 BaseMessageEvent,那么 BaseMessageEvent.class 也会添加到 eventTypes 集合中,通过后面 MessageEvent.class 和 BaseMessageEvent.class 这两个事件类型的订阅事件都会被触发,这就是允许继承的情况。

如果不允许继承,那就只触发 MessageEvent.class 类型的订阅事件。

private static List<Class<?>> lookupAllEventTypes(Class<?> eventClass) {
synchronized (eventTypesCache) {
List<Class<?>> eventTypes = eventTypesCache.get(eventClass);
if (eventTypes == null) {
eventTypes = new ArrayList<>();
Class<?> clazz = eventClass;
while (clazz != null) {
eventTypes.add(clazz);
addInterfaces(eventTypes, clazz.getInterfaces());
clazz = clazz.getSuperclass();
}
eventTypesCache.put(eventClass, eventTypes);
}
return eventTypes;
}
}

接着看 postSingleEventForEventType(event, postingState, eventClass) 方法

private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
// 1. 根据事件类型获取订阅事件
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
subscriptions = subscriptionsByEventType.get(eventClass);
}
// 2. 遍历执行订阅事件
if (subscriptions != null && !subscriptions.isEmpty()) {
for (Subscription subscription : subscriptions) {
postingState.event = event;
postingState.subscription = subscription;
boolean aborted = false;
try {
postToSubscription(subscription, event, postingState.isMainThread);
aborted = postingState.canceled;
} finally {
postingState.event = null;
postingState.subscription = null;
postingState.canceled = false;
}
if (aborted) {
break;
}
}
return true;
}
return false;
}

postSingleEventForEventType 方法也很清晰,继续看 postToSubscription(subscription, event, postingState.isMainThread);

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 {
// temporary: technically not correct as poster not decoupled from subscriber
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);
}
}

通过不同的 threadMode 在不同的线程里 invoke() 订阅者的方法,ThreadMode 共有五类:

PostThread:默认的 ThreadMode,表示在执行 Post 操作的线程直接调用订阅者的事件响应方法,不论该线程是否为主线程(UI 线程)。当该线程为主线程时,响应方法中不能有耗时操作,否则有卡主线程的风险。适用场景:对于是否在主线程执行无要求,但若 Post 线程为主线程,不能耗时的操作;

MainThread:在主线程中执行响应方法。如果发布线程就是主线程,则直接调用订阅者的事件响应方法,否则通过主线程的 Handler 发送消息在主线程中处理——调用订阅者的事件响应函数。显然,MainThread类的方法也不能有耗时操作,以避免卡主线程。适用场景:必须在主线程执行的操作;

MAIN_ORDERED:和 MainThread 类似,只是在执行过程中,事件按照在队列中的顺序依次执行。

BackgroundThread:在后台线程中执行响应方法。如果发布线程不是主线程,则直接调用订阅者的事件响应函数,否则启动唯一的后台线程去处理。由于后台线程是唯一的,当事件超过一个的时候,它们会被放在队列中依次执行,因此该类响应方法虽然没有PostThread类和MainThread类方法对性能敏感,但最好不要有重度耗时的操作或太频繁的轻度耗时操作,以造成其他操作等待。适用场景:操作轻微耗时且不会过于频繁,即一般的耗时操作都可以放在这里;

Async:不论发布线程是否为主线程,都使用一个空闲线程来处理。和BackgroundThread不同的是,Async类的所有线程是相互独立的,因此不会出现卡线程的问题,Async 模式下采用的是线程池来执行。适用场景:长耗时操作,例如网络访问。

在 postToSubscription 函数中执行时,如果模式是主线程模式,则调用 invokeSubscriber(subscription, event); 直接执行,如果不是主线程,需要通过 Poster 类加入队列中,然后激活队列依次执行。这里以 mainThreadPoster 为例来分析一下。

mainThreadPoster 的实际类型是 HandlerPoster,继承自 Handler,并实现 Poster 接口,

public class HandlerPoster extends Handler implements Poster {

private final PendingPostQueue queue;
private final int maxMillisInsideHandleMessage;
private final EventBus eventBus;
private boolean handlerActive;

protected HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) {
super(looper);
this.eventBus = eventBus;
this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage;
queue = new PendingPostQueue();
}

// 1. 将消息加入到订阅事件队列中
// 发送空消息,激活消息队列
public void enqueue(Subscription subscription, Object event) {
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
synchronized (this) {
queue.enqueue(pendingPost);
if (!handlerActive) {
handlerActive = true;
if (!sendMessage(obtainMessage())) {
throw new EventBusException("Could not send handler message");
}
}
}
}

// 2. 这部分很熟悉,消息回调 Handler 的执行方法,即执行订阅事件
@Override
public void handleMessage(Message msg) {
boolean rescheduled = false;
try {
long started = SystemClock.uptimeMillis();
while (true) {
PendingPost pendingPost = queue.poll();
if (pendingPost == null) {
synchronized (this) {
// Check again, this time in synchronized
pendingPost = queue.poll();
if (pendingPost == null) {
handlerActive = false;
return;
}
}
}
eventBus.invokeSubscriber(pendingPost);
long timeInMethod = SystemClock.uptimeMillis() - started;
if (timeInMethod >= maxMillisInsideHandleMessage) {
if (!sendMessage(obtainMessage())) {
throw new EventBusException("Could not send handler message");
}
rescheduled = true;
return;
}
}
} finally {
handlerActive = rescheduled;
}
}
}

HandlerPoster 的作用就是将订阅事件加入到消息队列中,并激活消息队列,开始执行消息队列中的订阅事件,执行过程是是通过反射执行的,即 eventBus.invokeSubscriber(pendingPost);

源码这里有一个巧妙的设计,消息队列中的对象是一个 PendingPost 对象,其内部维护一个对象池,主要完成复用的过程,防止创建过多的对象,具体就不分析了,自己看下源码吧!

到这里,事件发送以及执行的过程都分析完毕,总结起来:就是通过事件类型, 查找具该类型的所有订阅事件(Subscription),然后遍历执行,执行过程中,根据不同的模式来执行,执行过程采用的是反射方式。

event_bus_post

2.4 取消注册

取消注册过程就相对比较简单了,实际上就是将订阅事件从集合中移除,关键找到对应的订阅事件。还记得上面的 typesBySubscriber 集合吗?它记录了订阅者中的订阅事件的类型,通过订阅者得到它的订阅类型集合,然后根据这个类型找到这个订阅者的订阅事件(Subscription),然将它从大集合 subscriptionsByEventType 中把它移除。

public synchronized void unregister(Object subscriber) {
// 1. 获取事件类型
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--;
}
}
}
}

3. 总结

EventBus 采用的是 订阅-发布模型,基本思想:注册时将订阅者的类型以及执行的函数放到集合中,发布事件时,根据事件类型遍历集合,获取相应的订阅事件,通知订阅者,通过反射的方式执行订阅函数。

参考

EventBus

EventBus 3.0 源码分析(流程图来源于该文章-感谢)

EventBus源码研读

打赏一个呗

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦