前言
上一篇文章自己对EventBus的用法进行了简单的叙述,然后自己又去研究了一下EventBus3.0源码也参考了网上的一些大佬的博客进行进一步的理解,写这一篇文章让自己对EventBus有个好的总结回顾,如有不正确的地方欢迎留言。
一、EventBus简介
在EventBus没出现之前,那时候的开发者一般是使用Android四大组件中的广播进行组件间的消息传递,那么我们为什么要使用事件总线机制来替代广播呢?主要是因为:
广播:耗时、容易被捕获(不安全)。
事件总线:更节省资源、更高效,能将信息传递给原生以外的各种对象。
关于事件其实是一个泛泛的统称,指的是一个概念上的东西(不一定非得用什么Event命),通过查阅官方文档,事件的命名格式并没有任何要求,你可以定义一个对象作为事件,也可以发送基本数据类型如int,String等作为一个事件。后续的源码也可以进步证明(方法的命名并没有任何要求,只是加上@Subscribe注解即可!同时事件的命名也没有任何要求)。
EventBus作为一个消息总线主要有三个组成部分:
事件(Event): 可以是任意类型的对象。通过事件的发布者将事件进行传递。
事件订阅者(Subscriber): 接收特定的事件。
事件发布者(Publisher): 用于通知 Subscriber 有事件发生。可以在任意线程任 意位置发送事件。
上图1解释了整个EventBus的大概工作流程:事件的发布者(Publisher)将事件 (Event)通过post()方法发送。EventBus内部进行处理,找到订阅了该事件 (Event)的事件订阅者(Subscriber)。然后该事件的订阅者(Subscriber)通过 onEvent()方法接收事件进行相关处理。
二、EventBus源码解析
1、订阅者:EventBus.getDefault().register(this);
首先,我们从获取EventBus实例的方法getDefault()开始分析:
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
在getDefault()中使用了双重校验并加锁的单例模式来创建EventBus实例。
接着,我们看到EventBus的默认构造方法中做了什么:
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
public EventBus() {
this(DEFAULT_BUILDER);
}
在EventBus的默认构造方法中又调用了它的另一个有参构造方法,将一个类型为EventBusBuilder的DEFAULT_BUILDER对象传递进去了。这里的EventBusBuilder很明显是一个EventBus的建造器,以便于EventBus能够添加自定义的参数和安装一个自定义的默认EventBus实例。
我们再看一下EventBusBuilder的构造方法:
public class EventBusBuilder {
...
EventBusBuilder() {
}
...
}
EventBusBuilder的构造方法中什么也没有做,那我么继续查看EventBus的这个有参构造方法:
private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
private final Map<Object, List<Class<?>>> typesBySubscriber;
private final Map<Class<?>, Object> stickyEvents;
EventBus(EventBusBuilder builder) {
...
//Map<订阅事件, 订阅该事件的订阅者集合> 键为 Event的类类型(也就是定义订阅方法里面的实体类型),值为元素 Subscription(订阅信息)链表
//Subscription 类:关注类中两个字段,一个是 Object 类型的 subscriber,该字段即为注册的对象(在 Android 中时常为 Activity)
//另一个是 SubscriberMethod 类型,细节如下:
//subscriberMethod:SubscriberMethod 类型(订阅方法)。关注类中有个字段 eventType 是 Class<?> 类型,代表 Event 的类类型。
subscriptionsByEventType = new HashMap<>();
// Map<订阅者, 订阅事件集合> (举个例子就是Activity中对应着该Activity中所有的对应的订阅方法)
typesBySubscriber = new HashMap<>();
// Map<订阅事件类类型,订阅事件实例对象>. 专用于粘性事件处理的一个字,用于判断某个对象是否注册过.
stickyEvents = new ConcurrentHashMap<>();
//mainThreadPoster:主线程事件发送器,通过它的mainThreadPoster.enqueue(subscription, event)
//方法可以将订阅信息和对应的事件进行入队,
//然后通过 handler 去发送一个消息,在 handler 的 handleMessage 中去执行方法。
mainThreadSupport = builder.getMainThreadSupport();
mainThreadPoster = mainThreadSupport != null ?
mainThreadSupport.createPoster(this) : null;
//backgroundPoster:后台事件发送器,通过它的enqueue() 将方法加入到后台的一个队列,
//最后通过线程池去执行,注意,它在 Executor的execute()方法 上添加了 synchronized关键字
//并设立了控制标记flag,保证任一时间只且仅能有一个任务会被线程池执行。
backgroundPoster = new BackgroundPoster(this);
//asyncPoster:实现逻辑类似于backgroundPoster,不同于backgroundPoster的保证
//任一时间只且仅能有一个任务会被线程池执行的特性,asyncPoster则是异步运行的,可以同时接收多个任务
asyncPoster = new AsyncPoster(this);
...
subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
builder.strictMethodVerification, builder.ignoreGeneratedIndex);
// 从builder取中一些列订阅相关信息进行赋值
...
// 从builder中取出了一个默认的线程池对象,它由Executors的newCachedThreadPool()方法创建,它是一个有则用、无则创建、无数量上限的线程池。
executorService = builder.executorService;
}
下面看具体的register()中执行的代码。
public void register(Object subscriber) {
//订阅者类型
Class<?> subscriberClass = subscriber.getClass();
//判断该类是不是匿名类,如果是匿名类要使用反射
boolean forceReflection = subscriberClass.isAnonymousClass();
//获取订阅者全部的响应函数信息(即上面的onNewsEvent()之类的方法)
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(sub scriberClass, forceReflection);
//循环每一个事件响应函数,执行 subscribe()方法,更新订阅相关信息
for (SubscriberMethod subscriberMethod : subscriberMetho ds) { subscribe(subscriber, subscriberMethod);
}
}
接着我们查看SubscriberMethodFinder的findSubscriberMethods()方法:
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
// METHOD_CACHE:Map<Class<?>, List<SubscriberMethod>> 类型。键为注册类的 Class(例如:Activity),
//值为该类中所有 EventBus 回调的方法链表(也就是被 @Subscribe 标记的方法们)。
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
//ignoreGeneratedIndex 来判断是否使用生成的 APT 代码去优化寻找接收事件的过程,如果开启了的话,
//那么将会通过 subscriberInfoIndexes 来快速得到接收事件方法的相关信息。
//所以各位读者如果没有在项目中接入 EventBus 的 APT,
//那么可以将 ignoreGeneratedIndex 设为 false 提高性能
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为true,看下findUsingReflection()方法
private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
// 通过纯反射去获取被 @Subscribe 所修饰的方法
findUsingReflectionInSingleClass(findState);
// 将当前 class 的父类 class 赋值给 findState.clazz
findState.moveToSuperclass();
}
// 重置 FindState 便于下一次回收利用
return getMethodsAndRelease(findState);
}
初始化 FindState 对象后,会进入一个 while 循环中,不停地去反射获取当前类和其父类(注意,在 Java 中,如果当前类实现了一个接口,即使该接口的方法被 @Subscribe 所修饰,当前类中的方法也是不包含该注解属性的,所以如果在接口中对某个方法使用了 @Subscribe 修饰然后让类去实现这个接口是没有任何作用的)的订阅方法并添入列表中,最终返回这个列表并重置 FindState 对象利于下一次重复使用。```
这里ignoreGeneratedIndex 默认为false,所以会执行findUsingInfo()方法
```java
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
// 注释1处
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
// 注释2处
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 {
// 注释3处
findUsingReflectionInSingleClass(findState);
}
findState.moveToSuperclass();
}
// 重置 FindState 便于下一次回收利用
return getMethodsAndRelease(findState);
}
注释1处: SubscriberMethodFinder中的prepareFindState()方法:
//会先从 FIND_STATE_POOL 即 FindState 池中取出可用的 FindState(这里的POOL_SIZE为4),
//如果没有的话,则通过代码直接新建 一个新的 FindState 对象。
//由于 FindState 在注册流程中使用频繁且创建耗费资源,故创建 FindState 池复用 FindState 对象
private static final FindState[] FIND_STATE_POOL = new FindState[POOL_SIZE];
private FindState prepareFindState() {
synchronized(FIND_STATE_POOL) {
for (int i = 0; i < POOL_SIZE; i++) {
FindState state = FIND_STATE_POOL[i];
if (state != null) {
FIND_STATE_POOL[i] = null;
return state;
}
}
}
return new FindState();
}
分析下FindState这个类:
static class FindState {
....
void initForSubscriber(Class<?> subscriberClass) {
this.subscriberClass = clazz = subscriberClass;
skipSuperClasses = false;
subscriberInfo = null;
}
...
}
它是 SubscriberMethodFinder 的内部类,这个方法主要做一个初始化、回收对象等工作。
我们在回到SubscriberMethodFinder注释2处:getSubscriberInfo():
private SubscriberInfo getSubscriberInfo(FindState findState) {
if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) {
SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();
if (findState.clazz == superclassInfo.getSubscriberClass()) {
return superclassInfo;
}
}
if (subscriberInfoIndexes != null) {
for (SubscriberInfoIndex index: subscriberInfoIndexes) {
SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
if (info != null) {
return info;
}
}
}
return null;
}
这里由于初始化的时候,findState.subscriberInfo和subscriberInfoIndexes为空,所以这里直接返回null
接着我们查看注释3处的findUsingReflectionInSingleClass()方法:
private void findUsingReflectionInSingleClass(FindState findState) {
Method[] methods;
try {
// 返回当前类自身方法和显式重载的父类方法
methods = findState.clazz.getDeclaredMethods();
} catch (Throwable th) {
methods = findState.clazz.getMethods();
findState.skipSuperClasses = true;
}
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");
}
}
}
这个方法的逻辑是:
通过反射的方式获取订阅者类中的所有声明方法,然后在这些方法里面寻找以@Subscribe作为注解的方法进行处理,先经过一轮检查,看看findState.subscriberMethods是否存在,如果有的话,将方法名,threadMode,优先级,是否为sticky方法封装为SubscriberMethod对象,添加到subscriberMethods列表中。
而实际上是需要过滤一遍的,讲解 checkAdd() 源码前,请思考以下几个问题:
对于同一个 Event,当前类对该对象使用了多个方法进行了多次订阅,那么如果该 Event 被发射的时候,当前类会如何调用这些方法?
对于同一个 Event,父类对该对象进行了一次订阅,子类重写该订阅方法,那么如果该 Event 被发射的时候,父类子类当中会如何处理这些方法?
解决这些方法就需要去看看 checkAdd() 的底层实现了:
boolean checkAdd(Method method, Class<?> eventType) {
Object existing = anyMethodByEventType.put(eventType, method);
if (existing == null) {
return true;
} else {
return checkAddWithMethodSignature(method, eventType);
}
}
可以看到 anyMethodByEventType 使用了 Event 的 Class 作为键,这像是意味着一个类对于同一个 Event 只能订阅一次,事实上是不是这样,还得继续看看 checkAddWithMethodSignature(),其源码简化如下:
private boolean checkAddWithMethodSignature(Method method, Class<?> eventType) {
methodKeyBuilder.setLength(0);
methodKeyBuilder.append(method.getName());
methodKeyBuilder.append('>').append(eventType.getName());
String methodKey = methodKeyBuilder.toString();
Class<?> methodClass = method.getDeclaringClass();
Class<?> methodClassOld = subscriberClassByMethodKey.put(methodKey, methodClass);
if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) {
return true;
} else {
subscriberClassByMethodKey.put(methodKey, methodClassOld);
return false;
}
}
可以看到 subscriberClassByMethodKey 使用方法名 + ‘>’ + 事件类型作为键,这意味着对于同一个类来说,subscriberClassByMethodKey 肯定不会键重复(毕竟一个类中不能够方法名相同且方法参数、个数都相同),因此它最终会返回 true。这意味着一个类如果使用了多个方法对同一个 Event 对象进行注册,那么当该 Event 对象被发射出来的时候,所有的方法都将会得到回调。
但是当父类执行上述操作的时候,如果子类有「显示」实现父类的订阅方法,那么此时 subscriberClassByMethodKey.put(methodKey, methodClass) 返回值不会为空,且为子类的 Class,此时 if 上分支将会判断子类 Class 是否 isAssignableFrom 父类 Class,这肯定是会为 false 的,这将会走入 if 下分支并返回 false。这意味着当子类「显示」实现父类的订阅方法的时候,如果此时发射指定 Event 的话,父类的订阅方法将不会执行,而仅会执行子类的订阅方法。
返回subscriberMethods之后,register方法的最后会调用subscribe方法:
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
//获取订阅的事件类型
Class<?> eventType = subscriberMethod.eventType;
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
//获取订阅该事件的订阅者集合 在subscriptionsByEventType去查找一个CopyOnWriteArrayList ,
//如果没有则创建一个新的CopyOnWriteArrayList
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
//把通过register()订阅的订阅者包装成Subscription 对象 Subscription newSubscription
//= new Subscription(subscrib er, subscriberMethod);
//订阅者集合为空,创建新的集合,并把newSubscription 加入
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();
//添加 newSubscription对象,它是一个 Subscription 类,里面包含着 subscriber 和 subscriberMethod 等信息,
//并且这里有一个优先级的判断,说明它是按照优先级添加的。优先级越高,会插到在当前 List 靠前面的位置
for (int i = 0; i <= size; i++) {
if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
// 根据 priority 大小放入 List 中
subscriptions.add(i, newSubscription);
break;
}
}
//对typesBySubscriber 进行添加,这主要是在EventBus的isRegister()方法中去使用的,目的是用来判断这个 Subscriber对象 是否已被注册过
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
//会判断是否是 sticky事件。如果是sticky事件的话,会调用 checkPostStickyEventToSubscription() 方法
if (subscriberMethod.sticky) {
//响应订阅事件的父类事件
if (eventInheritance) {
Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
//循环获得每个stickyEvent事件
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);
}
}
}
我们来看下checkPostStickyEventToSubscription()方法:
private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
if (stickyEvent != null) {
postToSubscription(newSubscription, stickyEvent, isMainThread());
}
}
可以看到最终是调用了postToSubscription()这个方法来进行粘性事件的发送,对于粘性事件的处理,我们最后再分析,接下来我们看看事件是如何post的。
2、发布者:EventBus.getDefault().post(new Event());
先来看下post()方法:
public void post(Object event) {
//
PostingThreadState postingState = currentPostingThreadState.get();
List <Object> eventQueue = postingState.eventQueue;
eventQueue.add(event);
//判断新加入的事件是否在分发中
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;
}
}
}
currentPostingThreadState 是一个 ThreadLocal 类,通过它获取到 PostingThreadState 对象,再根据该对象获取到 event 链表(有没有联想到 Android 中的消息机制?),并将传入的 event 塞入该链表。为了控制 Event 出队列不会被调用多次,PostingThreadState 对象有一个 isPosting 来标记当前链表是否已经开始进行回调操作,通过源码可以看到,每次分发完一个 Event 事件,该事件也会被从链表中 remove 出去。可以看下currentPostingThreadState的源码如下:
private final ThreadLocal <PostingThreadState> currentPostingThreadState = new ThreadLocal <PostingThreadState> () {
@Override
protected PostingThreadState initialValue() {
return new PostingThreadState();
}
};
final static class PostingThreadState {
final List <Object> eventQueue = new ArrayList<>();
boolean isPosting;
boolean isMainThread;
Subscription subscription;
Object event;
boolean canceled;
}
下一步进入postSingleEvent()方法
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
//分发事件的类型
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
//默认为true,如果设为 true 的话,它会在发射事件的时候判断是否需要发射父类事件,设为 false,能够提高一些性能。
if (eventInheritance) {
//它的作用就是取出 Event 及其父类和接口的 class 列表,当然重复取的话会影响性能,
//所以它也做了一个 eventTypesCache 的缓存,这样就不用重复调用 getSuperclass() 方法。
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 {
subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
}
if (!subscriptionFound) {
...
}
}
然后我们在进入postSingleEventForEventType()方法:
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class <?> eventClass) {
CopyOnWriteArrayList <Subscription> subscriptions;
synchronized(this) {
// 获取订阅事件类类型对应的订阅者信息集合.(register函数时构造的集 合)
subscriptions = subscriptionsByEventType.get(eventClass);
}
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;
}
这里直接根据 Event 类型从 subscriptionsByEventType 中取出对应的 subscriptions对象,最后调用了 postToSubscription() 方法。
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 {
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("Unknow thread mode: " + subscription.subscriberMethod.threadMode);
}
}
其实可以发现只用到了两种方法,一种是 invokeSubscriber 意味着立即调用该方法,另一种是 xxxPoster.enqueue() 意味着需要使用其他线程来执行该方法。
以下是threadMode 哪几种应避免耗时操作,耗时时阻塞的是哪条线程:
- POSTING:接收事件方法应执行在发射事件方法所在的线程(由于发射事件方法线程可能是主线程,这意味着接收方法不能执行耗时操作,否则会阻塞主线程)
- MAIN:在 Android 中则接收事件方法应执行在主线程,否则(在 Java 项目中)等同于 POSTING。如果发射事件方法已位于主线程,那么接收事件方法会被「立即」调用(这意味着接收事件方法不能执行耗时操作,否则会阻塞主线程;同时,由于是「立即」调用,所以发射事件方法此时是会被接收事件方法所阻塞的),否则等同于 MAIN_ORDERED
- MAIN_ORDERED:在 Android 中则接收事件方法会被扔进 MessageQueue 中等待执行(这意味着发射事件方法是不会被阻塞的),否则(在 Java 项目中)等同于 POSTING。
- BACKGROUND:
- 在 Android 中
- 发射事件方法在主线程中执行,则接收事件方法应执行在子线程执行,但该子线程是 EventBus 维护的单一子线程,所以为了避免影响到其他接收事件方法的执行,该方法不应太耗时避免该子线程阻塞。
- 发射事件方法在子线程中执行,则接收事件方法应执行在发射事件方法所在的线程。
- 在 Java 项目中,接收事件方法会始终执行在 EventBus 维护的单一子线程中。
- 在 Android 中
- ASYNC:接收方法应执行在不同于发射事件方法所在的另一个线程。常用于耗时操作,例如网络访问。当然,尽量避免在同一个时间大量触发此类型方法,尽管 EventBus 为此专门创建了线程池来管理回收利用这些线程。
3、解除订阅:EventBus.getDefault().unregister(this);
public synchronized void unregister(Object subscriber) {
// 获取该订阅者所有的订阅事件类类型集合.
List<Class<?>> subscribedTypes = typesBySubscriber.get(subsc riber);
if (subscribedTypes != null) {
for (Class<?> eventType : subscribedTypes) {
//subscriptionsByEventType 移除了该 subscriber 的所有订阅信息
unsubscribeByEventType(subscriber, eventType);
}
// 从typesBySubscriber删除该<订阅者对象,订阅事件类类型集合>
typesBySubscriber.remove(subscriber);
} else {
Log.e("EventBus", "Subscriber to unregister was not regi stered 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 --;
}
}
}
}
4、发布黏性瞬间:EventBus.getDefault.postSticky(new CollectEvent())
普通事件是先注册,然后发送事件才能收到;而粘性事件,在发送事件之后再订阅该事件也能收到。此外,粘性事件会保存在内存中,每次进入都会去内存中查找获取最新的粘性事件,除非你手动解除注册
public void postSticky(Object event) {
synchronized (stickyEvents) {
// 先将该事件放入 stickyEvents 中
stickyEvents.put(event.getClass(), event);
}
post(event);
}
可以看到第一步是将该事件放入 stickyEvents 中,第二步则是正常 post()。为避免多线程操作 postSticky(Object) 和 removeStickyEvent(Class<?>) 引发的冲突,所以对 stickyEvents 对象添加了 synchronized 关键字,
前面我们在分析register()方法的最后部分时,其中有关粘性事件的源码如下:
if (subscriberMethod.sticky) {
Object stickyEvent = stickyEvents.get(eventType);
if (stickyEvent != null) {
postToSubscription(newSubscription, stickyEvent, isMainThread());
}
}
可以看到,在这里会判断当前事件是否是 sticky 事件,如果是,则从 stickyEvents 中拿出该事件并执行 postToSubscription() 方法。
好了EventBus源码到此结束了。
总结
EventBus 源码都说简单我可没看出简单啊,当中的很多设计技巧是非常值得学习的,例如前文提到的复用池,以及遍布 EventBus 源码各处的 synchronized 关键字。笔者也是第一次写源码的分析,也是借鉴网上的大佬们的文章。
参考链接:
Android主流三方库源码分析(九、深入理解EventBus源码)
聊一聊 EventBus 源码和设计之禅
EventBus 3.1.1 源码解析
来源:CSDN
作者:阿木木丶涛
链接:https://blog.csdn.net/xt173160769/article/details/104746105