EventBus是目前Android开发中流行的事件总线框架,本文假定大家已经熟悉EventBus的基本使用,我们一起来研究EventBus背后的原理。
在开始分析原理前,首先需要告知大家,在本文中:
- 将调用了
EventBus.getDefault().register(this);
的类称为注册类 - 将被
@Subscribe
注解所标记的方法称为订阅方法
好了,下面我们正式开始
一、基本使用
我们来看一个最基本的使用:
1.1、定义事件类
首先定义一个事件类EventLoveState
:
public class EventLoveState {
private String loveState;
public EventLoveState(String loveState) {
this.loveState = loveState;
}
public String getLoveState() {
return loveState;
}
public void setLoveState(String loveState) {
this.loveState = loveState;
}
}
1.2、订阅事件
接着我们在MainActivity
中对这个事件进行订阅:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
EventBus.getDefault().register(this);
}
@Subscribe
public void eventLoveState(EventLoveState eventLoveState) {
System.out.println(eventLoveState.getLoveState());
}
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
}
1.3、发送事件
这样,当在程序中任何地方使用EventBus.getDefault().post(new EventLoveState("失恋了"));
去发送这个事件时,此事件的订阅者(比如MainActivity
中的eventLoveState()
方法)都会被调用。
二、EventBus的register
为了弄清楚为何短短的一句EventBus.getDefault().register(this)
,就能使该类中所有被@Subscribe
标记的方法订阅某个事件类型,并在发送相应事件类型后,能自动调用方法呢?我们一起来分析其源码吧!
2.1、EventBus.getDefault()
来看一下EventBus.getDefault()
:
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
这是一个典型的DCL单例模式,通过阅读此代码,我们知道了EventBus
默认在应用中是以单例的形式存在。接着我们来看看EventBus
的构造方法:
public EventBus() {
// private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
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;
}
不难看出上面的代码就是将EventBusBuilder
中字段的值赋值给EventBus
中的字段,起到一个初始化的作用(Builder模式)。至于这些字段的作用和含义,现在不做探讨,就算现在说了也记不住,在后面用到的时候再说吧!现在混个眼熟即可,到此为止,EventBus对象就创建完成了。
// 目前需要眼熟的几个成员:
// 用于存储[事件类型]与[订阅者信息]的对应关系
Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
// 用于存储[注册类]与[事件类型]的对应关系
Map<Object, List<Class<?>>> typesBySubscriber;
// 用于查找订阅方法
SubscriberMethodFinder subscriberMethodFinder;
2.2、register()
下面就开始来看关键的register()
方法了:
public void register(Object subscriber) {
// 获取注册类的Class对象
Class<?> subscriberClass = subscriber.getClass();
// register分析1
List<SubscriberMethod> subscriberMethods =
subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
//register分析2
subscribe(subscriber, subscriberMethod);
}
}
}
register分析1
处,我们能够通过方法的名称以及返回结果大致猜出,这一行代码要做的事情是:根据注册类的Class
对象(subscriberClass
),找到该注册类中所有的订阅方法信息(List<SubscriberMethod>
)。
2.2.1、SubscriberMethod
到这里,我们遇到了一个陌生的类:SubscriberMethod
,它的关键代码如下:
public class SubscriberMethod {
// 方法Method对象
final Method method;
// @Subscribe注解中threadMode的值,表示接受事件的线程模式
final ThreadMode threadMode;
// 订阅方法所订阅的事件类型
final Class<?> eventType;
// @Subscribe注解中priority的值,表示接收同一线程模式事件的优先级
final int priority;
// @Subscribe注解中sticky的值,表示是否接收粘性事件
final boolean sticky;
String methodString;
public SubscriberMethod(Method method, Class<?> eventType, ThreadMode threadMode, int priority, boolean sticky) {
this.method = method;
this.threadMode = threadMode;
this.eventType = eventType;
this.priority = priority;
this.sticky = sticky;
}
}
其实它就是用于保存订阅方法(被@Subscribe
标记)的信息,其中包括了方法本身Method
对象、方法参数类型的Class对象eventType
以及注解中的参数值:
- threadMode:标志着订阅方法被调用时所在的线程,如果没有在
@Subscribe
显式指定,默认为ThreadMode.POSTING
,即发送事件时在哪个线程,接收事件就在哪个线程。 - priority:表示相同
threadMode
下订阅方法接收事件的优先级,如果没有在@Subscribe
显式指定,默认为0,优先级越高,能越早接收到事件。 - sticky:是否接收粘性事件,如果没有在
@Subscribe
显式指定,默认是false
。粘性事件指的是,哪怕预先没有注册此事件类型的观察者,在发送了粘性事件后再注册,仍然能收到注册前发送的事件消息。
到此,SubscriberMethod
分析完毕,顺便也提了下@Subscribe
中的一些参数的含义,在这里大家也不妨对照着看一下:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
ThreadMode threadMode() default ThreadMode.POSTING;
boolean sticky() default false;
int priority() default 0;
}
2.2.2、SubscriberMethodFinder # findSubscriberMethods()
回到register分析1
处继续看代码
List<SubscriberMethod> subscriberMethods =
subscriberMethodFinder.findSubscriberMethods(subscriberClass);
根据注册类找到注册类中所有的SubscriberMethod
(即所有订阅方法)。subscriberMethodFinder
是在创建EventBus
时初始化的:
EventBus(EventBusBuilder builder) {
//...
subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
builder.strictMethodVerification, builder.ignoreGeneratedIndex);
//...
}
跟进到SubscriberMethodFinder
类中的findSubscriberMethods()
方法一探究竟吧:
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
// Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>()
// 首先从缓存中取出注册类所包含的订阅方法集合
// 显然可看出,METHOD_CACHE是以注册类Class对象为key,订阅方法集合为value的Map
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
// 如果缓存中存在,则直接返回
return subscriberMethods;
}
// 如果我们使用的是默认的EventBusBuilder创建的EventBus对象,则此值为false
if (ignoreGeneratedIndex) {
subscriberMethods = findUsingReflection(subscriberClass);
} else {
// 默认将调用findUsingInfo()方法查找注册类中的注册方法信息
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;
}
}
上面代码流程非常清晰,首先根据subscriberClass
(注册类Class对象)从METHOD_CACHE
缓存中获取订阅方法,如果能获取到就直接返回,否则调用findUsingInfo()
方法查找。最终如果找到了注册方法,先将其缓存到METHOD_CACHE
中,再返回。如果没有找到,则抛出异常。METHOD_CACHE
的意义在于对于同一个注册类多次注册,只需查找一次订阅方法,后续直接从缓存中获取即可,以空间换时间。
2.2.3、SubscriberMethodFinder # findUsingInfo()
根据上面的分析我们知道,如果METHOD_CACHE
中没有缓存,则会调用findUsingInfo()
方法查找,接下来我们来看findUsingInfo()
方法:
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
// 调用prepareFindState()方法得到一个FindState对象
FindState findState = prepareFindState();
// 调用findState的initForSubscriber()初始化findState中的成员
findState.initForSubscriber(subscriberClass);
// 第一次findState.clazz就是注册类本身,后续表示注册类的父类,如果存在则继续循环
while (findState.clazz != null) {
// 默认使用情况下,getSubscriberInfo()返回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);
}
// 查找该注册类是否存在父类,如果存在,将父类Class对象赋值给findState.clazz
// 继续查找父类的订阅方法;如果不存在父类,则findState.clazz为null,停止循环
findState.moveToSuperclass();
}
// 将findState对象所有资源置空并放入缓存以待下次取出使用,
// 最终返回获取到的订阅方法信息集合(List<SubscriberMethod>)
return getMethodsAndRelease(findState);
}
上面的代码也很简单,整体流程就是先初始化FindState
对象,查找注册类以及其父类中所有注册方法信息。FindState
是SubscriberMethodFinder
中的静态内部类,在查找过程中起到辅助的作用,现在先来看看FindState
中几个比较重要的成员:
// 存储找到的订阅方法信息
final List<SubscriberMethod> subscriberMethods = new ArrayList<>();
// 注册类对象
Class<?> subscriberClass;
// 注册类或注册类父类对象(调用moveToSuperclass()时改变,默认为subscriberClass)
Class<?> clazz;
// 是否跳过父类,默认为false
boolean skipSuperClasses;
知道了FindState
中几个比较重要的成员后,我们来看EventBus
是如何得到FindState
对象的:
private FindState prepareFindState() {
synchronized (FIND_STATE_POOL) {
// POOL_SIZE = 4
for (int i = 0; i < POOL_SIZE; i++) {
// FindState[] FIND_STATE_POOL = new FindState[POOL_SIZE]
FindState state = FIND_STATE_POOL[i];
if (state != null) {
FIND_STATE_POOL[i] = null;
return state;
}
}
}
return new FindState();
}
会优先从FIND_STATE_POOL
数组中取出并使用FindState
对象,如果数组中没有可用的FindState
对象,则创建新的并返回,第一次时,FIND_STATE_POOL
中是没有可用的FindState
对象的,因此会new FindState()
对象返回,当findState
使用完成后,会将其成员数据清空,并放到FIND_STATE_POOL
中供下次使用(new是比较浪费时间&内存的)。接下来程序会调用findState.initForSubscriber(subscriberClass)
方法,对findState
中的成员进行初始化:
void initForSubscriber(Class<?> subscriberClass) {
// 初始化subscriberClass和clazz都为传入的注册类对象
this.subscriberClass = clazz = subscriberClass;
// 不跳过父类
skipSuperClasses = false;
subscriberInfo = null;
}
接下来程序会进入while
循环,由于初始化时,findState.clazz
就是注册类对象本身,因此循环肯定会至少执行一次,执行完一次后,会调用findState.moveToSuperclass()
查找注册类有无父类,如果有,则将父类对象赋值给findState.clazz
继续进行新一次循环,否则findState.clazz = null
循环结束:
void moveToSuperclass() {
// 如果忽略父类,则直接将clazz赋值为null停止循环
if (skipSuperClasses) {
clazz = null;
} else {
// 获取父类对象
clazz = clazz.getSuperclass();
// 获取父类对象的全限定名
String clazzName = clazz.getName();
// 如果以下列包名开头的父类,不需要处理,它们不是用户定义的类,里面不可能存在订阅方法
if (clazzName.startsWith("java.") || clazzName.startsWith("javax.") || clazzName.startsWith("android.")) {
clazz = null;
}
}
}
2.2.4、SubscriberMethodFinder # findUsingReflectionInSingleClass()
那么重点的方法就是findUsingReflectionInSingleClass(findState)
了,它利用反射获取了注册类中所有的订阅方法信息,下面我们就来一起看一下它的具体代码:
private void findUsingReflectionInSingleClass(FindState findState) {
Method[] methods;
try {
// 获取注册类中所有的方法对象
// 这个方法在极少数android设备上会崩溃,因此这里用了try...catch
// 那为什么不直接用getMethods()呢?官方给出的解释是getDeclaredMethods()
// 效率明显高于getMethods()
methods = findState.clazz.getDeclaredMethods();
} catch (Throwable th) {
// 如果出现异常,则调用getMethods()获取当前类以及其父类中所有public方法对象
methods = findState.clazz.getMethods();
// 由于getMethods()已经获取到了父类中的方法对象,因此无需再去查找父类了
findState.skipSuperClasses = true;
}
// for循环遍历所有methods得到method对象
for (Method method : methods) {
// 获取方法的修饰符
int modifiers = method.getModifiers();
// int MODIFIERS_IGNORE = Modifier.ABSTRACT | Modifier.STATIC | BRIDGE |
// SYNTHETIC
// 如果修饰符是public 且 不是MODIFIERS_IGNORE中的任一个,开始解析
if ((modifiers & Modifier.PUBLIC) != 0 &&
(modifiers & MODIFIERS_IGNORE) == 0) {
// 获取方法的参数列表类型
Class<?>[] parameterTypes = method.getParameterTypes();
// 当方法的参数列表只有1个参数时,才符合订阅方法的要求,一个订阅只能订阅一种事件类型
if (parameterTypes.length == 1) {
// 取得该方法上的注解Subscribe
Subscribe subscribeAnnotation =
method.getAnnotation(Subscribe.class);
// 如果该方法确实被@Subscribe所标记
if (subscribeAnnotation != null) {
// 取出第一个参数的类型(事件类型)
Class<?> eventType = parameterTypes[0];
// 调用findState的checkAdd()方法判断此方法能否添加
if (findState.checkAdd(method, eventType)) {
// 得到ThreadMode
ThreadMode threadMode = subscribeAnnotation.threadMode();
// 创建SubscriberMethod对象(包含:注册方法对象,事件类型,线程模式,
// 优先级,是否粘性事件),并添加到findState的subscriberMethods集合
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
所标记,最后根据Method
对象以及该方法上的@Subscribe
注解获取到SubscriberMethod
对象所需的信息并创建它的对象,最后将创建出的SubscriberMethod
对象保存到findState.subscriberMethods
集合中。唯一需要单独说明一下的就是findState.checkAdd(method, eventType)
,这里为什么需要做这样的检查呢?我们先来看看checkAdd()
方法:
/**
*
* @param method 订阅方法的Method对象
* @param eventType 订阅事件的类型
* @return true:该订阅方法允许添加至findState.subscriberMethods中
* false:该订阅方法不需添加
*/
boolean checkAdd(Method method, Class<?> eventType) {
// Map<Class, Object> anyMethodByEventType,它是FindState的成员变量
// 这里以订阅的事件类型为key,订阅方法的Method对象为value put进此Map
Object existing = anyMethodByEventType.put(eventType, method);
if (existing == null) {
// 若existing为null表示从未订阅过此类型的事件,当然直接返回true
// 一级检查
return true;
} else {
// 到这里说明anyMethodByEventType中存在了key为当前订阅事件(eventType)的值
// existing = Map中以前eventType对应的value
// 二级检查
if (existing instanceof Method) {
// 只会在第一次遇到相同事件类型时先调用checkAddWithMethodSignature
if (!checkAddWithMethodSignature((Method) existing, eventType)) {
throw new IllegalStateException();
}
// 将该事件类型对应的value置位非Method类型
anyMethodByEventType.put(eventType, this);
}
//检查订阅方法是否需要解析为SubscriberMethod对象并添加至集合
return checkAddWithMethodSignature(method, eventType);
}
}
此方法分为了两级检查,首先会检查订阅事件类型在anyMethodByEventType
是否存在了,如果不存在,则直接允许将该订阅方法添加至findState.subscriberMethods
中,如果存在,则调用checkAddWithMethodSignature()
进行方法签名的检查。为什么需要两级呢?因为大多数注册类中的订阅方法,一般都不会同时订阅相同类型的事件,此时便无需调用checkAddWithMethodSignature()
了。只有当注册类中的多个订阅方法同时订阅了同一事件类型时,才需要进一步验证。一级检查很简单,所以我们重点来看看二级检查:checkAddWithMethodSignature()
方法:
private boolean checkAddWithMethodSignature(Method method, Class<?> eventType) {
// 清空methodKeyBuilder中的内容
methodKeyBuilder.setLength(0);
// 将方法的名字添加到methodKeyBuilder中
methodKeyBuilder.append(method.getName());
// 将 > 符号 和 订阅事件类型全限定名点击到methodKeyBuilder中
methodKeyBuilder.append('>').append(eventType.getName());
// 假设订阅方法:public void methodName(String name);
// 此时methodKey是这样的格式:methodName>java.lang.String
String methodKey = methodKeyBuilder.toString();
// 获取订阅方法所在类的类名 例如:class MainActivity
Class<?> methodClass = method.getDeclaringClass();
// Map<String, Class> subscriberClassByMethodKey,它是FindState的成员
// key是methodKey(方法名+ > + 参数类型),value是订阅方法所在类的类名
// methodClassOld是以前在subscriberClassByMethodKey中key为methodKey的value,
// 如果没有则为null
Class<?> methodClassOld = subscriberClassByMethodKey.put(methodKey, methodClass);
// methodClassOld == null 这个好理解,表示此方法签名从来没存在过,
// 那当然是返回true
// 后面一个条件是什么鬼?它判断methodClassOld是否是methodClass自己或它的父类
// 详细的解释我会在下面说明
if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) {
return true;
} else {
subscriberClassByMethodKey.put(methodKey, methodClassOld);
return false;
}
}
需要说明的是subscriberClassByMethodKey
是一个以方法签名为key
,方法所在类对象为value
的Map
。方法签名的格式为:方法名 + 参数类型的全限定名
。
现在我们来看一下上文中提到的methodClassOld.isAssignableFrom(methodClass)
到底是干什么的?假设当subscriberClassByMethodKey
存在相同的方法签名(即存在方法名相同,参数类型相同)时,判断前一个同名方法所在的类是否是现在被检查的同名方法的父类,如果是,同样允许解析这个注册方法并添加到findState.subscriberMethods
集合中。
现在的问题是,方法签名一样的情况在什么时候会出现呢?答案是子类重写父类方法时。通过前面的源码阅读,我们知道订阅方法的查找顺序是:先查找注册类中的订阅方法,再查找注册类父类(如果有)中的订阅方法。那按照这个顺序,methodClassOld
永远都只能是methodClass
的子类,因此methodClassOld.isAssignableFrom(methodClass)
的结果永远都是false
。要这个判断还有什么意义呢?存在即必有用,还记得获取注册类中所有Method
对象的代码吗?
Method[] methods;
try {
// 获取注册类中所有的方法对象
// 这个方法在极少数android设备上会崩溃,因此这里用了try...catch
// 那为什么不直接用getMethods()呢?官方给出的解释是getDeclaredMethods()
// 效率明显高于getMethods()
methods = findState.clazz.getDeclaredMethods();
} catch (Throwable th) {
// 如果出现异常,则调用getMethods()获取当前类以及其父类中所有public方法对象
methods = findState.clazz.getMethods();
// 由于getMethods()已经获取到了父类中的方法对象,因此无需再去查找父类了
findState.skipSuperClasses = true;
}
这段代码出现在findUsingReflectionInSingleClass()
开头,一旦getDeclaredMethods()
出现了异常,则会使用getMethods()
来获取Method
对象。而getMethods()
会获取当前类以及其父类中所有public
方法对象,且不保证顺序,这就导致了可能出现父类的Method
对象比子类的Method
对象先解析的情况。所以在这种情况下methodClassOld.isAssignableFrom(methodClass)
就有意义了,虽然这种情况的几率非常非常小。
因此,checkAdd()
方法会做出以下过滤:
- 当注册类(子类)重写了父类的订阅方法且子类同样使用
@Subscribe
标记后,父类的订阅方法将不会被注册 - 当
getDeclaredMethods()
发送异常时 且 当注册类(子类)重写了父类的订阅方法且子类同样使用@Subscribe
标记后,确保注册类的订阅方法一定生效,而其父类的订阅方法可能生效,也可能不生效,取决于Method
的排序。(此情况非常罕见)
到此,我们回到2.2.3 findUsingInfo()
方法,最后一行代码:
return getMethodsAndRelease(findState);
private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {
// 从findState取出订阅方法集合
List<SubscriberMethod> subscriberMethods =
new ArrayList<>(findState.subscriberMethods);
// 置空操作
findState.recycle();
synchronized (FIND_STATE_POOL) {
for (int i = 0; i < POOL_SIZE; i++) {
if (FIND_STATE_POOL[i] == null) {
// 把findState放入数组,以便下次直接使用
FIND_STATE_POOL[i] = findState;
break;
}
}
}
return subscriberMethods;
}
好了,绕了这么大一圈,终于把注册类中的订阅方法信息解析完毕了。我们总结一下整个获取流程:
register()
方法需要得到List<SubscriberMethod>
(所有订阅方法信息)- 于是
register()
方法委托SubscriberMethodFinder
对象中的findSubscriberMethods()
方法帮忙查找 findSubscriberMethods()
会先看缓存中是否存在,如果缓存中存在,则直接告诉register()
我找到啦!如果缓存中不存在,则会调用findUsingInfo()
进行获取findUsingInfo()
会先初始化FindState
对象用于获取辅助,并调用findUsingReflectionInSingleClass()
进行反射获取findUsingReflectionInSingleClass()
查找完成后,将最终结果返回给findSubscriberMethods()
,findSubscriberMethods()
将结果放进缓存,并返回给register()
2.2.5、subscribe()
到此为止,我们继续回到register()
方法中:
public void register(Object subscriber) {
// 获取注册类的Class对象
Class<?> subscriberClass = subscriber.getClass();
// register分析1
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
//register分析2
subscribe(subscriber, subscriberMethod);
}
}
}
继续往后看,它会循环subscriberMethods
,并将注册类对象和订阅方法对象传给subscribe()
方法,下面我们一起来看看register分析2
处的subscribe()
代码:
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
// 得到事件类型
Class<?> eventType = subscriberMethod.eventType;
// 创建Subscription对象,并将 注册类对象 和 订阅方法信息对象(subscriberMethod)保存进去
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
// subscriptionsByEventType:
// key为事件类型
// value为该事件类型对应的Subscription(注册类+订阅方法信息)集合
// 取出eventType对应的List<Subscription>
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions == null) {
// 如果eventType对应的List<Subscription>为null,则创建一个新集合
subscriptions = new CopyOnWriteArrayList<>();
// 以eventType为key,将新集合put到subscriptionsByEventType中
subscriptionsByEventType.put(eventType, subscriptions);
} else {
// 若eventType对应的subscriptions(List<Subscription>)不为null
// 判断subscriptions中是否存在了newSubscription
// subscribe分析1
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) {
// 根据priority优先级,将newSubscription插入到subscriptions中合适的位置
// (subscriptions中始终保持priority降序)
subscriptions.add(i, newSubscription);
break;
}
}
// typesBySubscriber:key为注册类对象;value为该注册类中的事件类型集合
// 根据注册类获取注册类中所有的事件类型
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
// 如果获取到的事件类型为null,则创建一个新集合
subscribedEvents = new ArrayList<>();
// 以注册类对象为key,新建的事件类型集合为value放进typesBySubscriber中
typesBySubscriber.put(subscriber, subscribedEvents);
}
// 将eventType(当前订阅方法的订阅事件类型)加入到刚刚新建的集合中
subscribedEvents.add(eventType);
// 这里我们暂时不看,等后面分析粘性事件时,再回过来看.
if (subscriberMethod.sticky) {
// ...
}
}
整个subscribe()
方法的代码其实也比较简单,几乎每一行代码的作用在注释中已经写得非常详细了。现在我们主要来看subscribe分析1
处的代码:
if (subscriptions.contains(newSubscription)) {
// 如果存在,抛出异常
throw new EventBusException("Subscriber " + subscriber.getClass() +
" already registered to event " + eventType);
}
在这里,EventBus
会检查同一事件类型中是否包含了相同的Subscription
,这个Subscription
其实就是用来存储订阅类对象
和SubscriberMethod
对象:
final class Subscription {
final Object subscriber;
final SubscriberMethod subscriberMethod;
volatile boolean active;
Subscription(Object subscriber, SubscriberMethod subscriberMethod) {
this.subscriber = subscriber;
this.subscriberMethod = subscriberMethod;
active = true;
}
@Override
public boolean equals(Object other) {
if (other instanceof Subscription) {
Subscription otherSubscription = (Subscription) other;
return subscriber == otherSubscription.subscriber
&& subscriberMethod.equals(otherSubscription.subscriberMethod);
} else {
return false;
}
}
@Override
public int hashCode() {
return subscriber.hashCode() + subscriberMethod.methodString.hashCode();
}
}
我们注意到这个类重写了equals()
方法,因此两个Subscription
相同的条件就是:
- 它们必须属于同一个注册类
- 它们的
subscriberMethod
进行equals()
后的结果为true
而SubscriberMethod
类也对equals()
方法进行了重写:
@Override
public boolean equals(Object other) {
if (other == this) {
return true;
} else if (other instanceof SubscriberMethod) {
// 得到方法签名
checkMethodString();
SubscriberMethod otherSubscriberMethod = (SubscriberMethod)other;
// 得到传入SubscriberMethod对象的方法签名
otherSubscriberMethod.checkMethodString();
// 比较两个签名是否完全一样
return methodString.equals(otherSubscriberMethod.methodString);
} else {
return false;
}
}
private synchronized void checkMethodString() {
if (methodString == null) {
StringBuilder builder = new StringBuilder(64);
// 拼接方法所在类的全限定名
builder.append(method.getDeclaringClass().getName());
// 拼接方法的名字
builder.append('#').append(method.getName());
// 拼接方法参数的类型
builder.append('(').append(eventType.getName());
methodString = builder.toString();
}
}
在阅读了Subscription
与SubscriberMethod
的equals()
方法后,我们再来总结一下两个Subscription
相同的条件是:
- 它们必须属于同一个注册类
- 它们的成员
subscriberMethod
中的Method
的签名(方法所在类的全限定名 + 方法名 + 参数类型)完全一致
2.3、总结
经过以上步骤,整个register
过程已经完成了。回顾下整个register
流程其实并不复杂,无非就是反射获取所有订阅方法信息,然后遍历获取到的订阅方法信息,创建Subscription
对象。从subscriptionsByEventType
取出key
为当前订阅方法订阅的事情类型对应的List<Subscription>
,并将创建的Subscription
加入集合;以订阅类为key
,取出该订阅类下所有的订阅类型集合,并将该订阅方法订阅的事件类型加入集合。最后附上一张精心绘制的流程图:
三、EventBus的unregister
register
过程的核心就是填充两个Map
:
typesBySubscriber
:存储每个注册类中所订阅的事件类型集合subscriptionsByEventType
:存储每个事件类型所对应的订阅方法信息(包含了方法所在的类对象)
而unregister
的过程其实就是register
过程的反向操作。在代码中,我们一般会使用:
EventBus.getDefault().unregister(this);
3.1、unregister()
解除当前类中所有订阅方法对事件的订阅。现在我们来看看unregister()
方法的具体实现:
public synchronized void unregister(Object subscriber) {
// 从typesBySubscriber根据注册类对象获取所有的订阅事件类型
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
if (subscribedTypes != null) {
// 如果订阅类型集合不为空,则遍历得到每一个订阅事件类型
for (Class<?> eventType : subscribedTypes) {
// 根据订阅事件类型去subscriptionsByEventType中移除
// Subscription.subscriber与取消注册的subscriber相同的Subscription
unsubscribeByEventType(subscriber, eventType);
}
// 从typesBySubscriber移除注册类中对应的所有订阅事件类型
typesBySubscriber.remove(subscriber);
} else {
// 如果要解除注册的类中并不存在任何订阅,则打印此log
logger.log(Level.WARNING, "Subscriber to unregister was not
registered before: " + subscriber.getClass());
}
}
由于比较简单,所以请看注释。
3.1.1、unsubscribeByEventType()
下面我们来看看unsubscribeByEventType()
方法:
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
// 根据订阅事件类型获取到所有的Subscription对象
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;
// 从List<Subscription>中移除
subscriptions.remove(i);
i--;
size--;
}
}
}
}
这个方法做的事情就是根据订阅事件类型获取到List<Subscription>
,这个集合中的Subscription
只保证订阅方法所订阅的事件类型是传入的eventType
,但不能就这样把它干掉,还需要判断Subscription
是否属于需要解除注册的类,subscription.subscriber == subscriber
就是这个作用。一旦满足了这些条件,就可以放心的将Subscription
从集合中移除了。
3.2、总结
解除注册实在是太简单,因此我们直接用一张流程图总结下:
四、EventBus的post
订阅和解除订阅我们都学习完了,现在我们是时候来看一看发送事件的代码了,在日常使用中,我们一般是通过:
EventBus.getDefault().post()
来发送事件,订阅了此事件的订阅方法就会收到这个事件。现在我们就从post()
方法开刀,来看一看发送事件背后的操作:
4.1、post()
public void post(Object event) {
// currentPostingThreadState是一个ThreadLocal对象
// 调用get()方法得到PostingThreadState对象
PostingThreadState postingState = currentPostingThreadState.get();
// 获取PostingThreadState对象中的eventQueue(事件队列)
// 其实在几乎所有情况下,eventQueue中的事件个数都不会超过1
// 因为PostingThreadState对象是从ThreadLocal中得到的
// 单线程不可能同时post两个事件
// 至于为什么这里要使用队列保存,我也不知道,甚至连官方也没有给出明确的解释
// 详见https://github.com/greenrobot/EventBus/issues/359
// 如果你知道原因,也欢迎你告知我(握手)
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;
}
}
}
注释已经很详细,这里简单说一下:首先从ThreadLocal
中获得了PostingThreadState
对象,如果你对ThreadLocal
不熟悉,可以看看我之前写的一篇关于ThreadLocal
的文章:《ThreadLocal源码阅读记录》。
PostingThreadState
类非常简单:
final static class PostingThreadState {
// 事件队列(笔者认为此队列最多只会有1个事件,应该不需要使用队列)
final List<Object> eventQueue = new ArrayList<>();
// 标记是否正在发送事件
boolean isPosting;
// 标记是否是主线程
boolean isMainThread;
// 订阅者
Subscription subscription;
// 发送的事件对象
Object event;
// 是否取消
boolean canceled;
}
然后向PostingThreadState
对象的eventQueue
队列中加入这个事件,注释中有说到此队列存在的必要性,笔者认为PostingThreadState
对象是从ThreadLocal
中得到的,因此不会存在同一时刻往同一个PostingThreadState
对象中存储多个事件的可能,欢迎大家在留言区探讨。
4.1.1、isMainThread()
接下来调用isMainThread()
获取发送事件时所在线程是否是主线程:
private boolean isMainThread() {
// mainThreadSupport.isMainThread() 实际上调用了
// AndroidHandlerMainThreadSupport的isMainThread()
return mainThreadSupport != null ? mainThreadSupport.isMainThread() : true;
}
private final MainThreadSupport mainThreadSupport;
EventBus(EventBusBuilder builder) {
// ...
mainThreadSupport = builder.getMainThreadSupport();
// ...
}
MainThreadSupport getMainThreadSupport() {
if (mainThreadSupport != null) {
return mainThreadSupport;
// isAndroidLogAvailable()做的事情是:Class.forName("android.util.Log") != null
} else if (AndroidLogger.isAndroidLogAvailable()) {
// getAndroidMainLooperOrNull()返回的是Looper.getMainLooper();
Object looperOrNull = getAndroidMainLooperOrNull();
return looperOrNull == null ? null :
// AndroidHandlerMainThreadSupport类实现了MainThreadSupport接口
new MainThreadSupport.
AndroidHandlerMainThreadSupport((Looper) looperOrNull);
} else {
return null;
}
}
public interface MainThreadSupport {
boolean isMainThread();
Poster createPoster(EventBus eventBus);
class AndroidHandlerMainThreadSupport implements MainThreadSupport {
// MainLooper
private final Looper looper;
public AndroidHandlerMainThreadSupport(Looper looper) {
// 保存Looper.getMainLooper()
this.looper = looper;
}
@Override
public boolean isMainThread() {
// 比较当前线程的Looper是否是MainLooper
return looper == Looper.myLooper();
}
@Override
public Poster createPoster(EventBus eventBus) {
return new HandlerPoster(eventBus, looper, 10);
}
}
}
通过以上代码我们知道了,在创建EventBus
对象时,EventBus
的成员MainThreadSupport mainThreadSupport
被赋值为AndroidHandlerMainThreadSupport
对象,AndroidHandlerMainThreadSupport
保存有MainLooper
的引用,最终调用mainThreadSupport.isMainThread()
比较的就是当前线程的Looper
是否是MainLooper
来判断是否是主线程。
4.1.2、postSingleEvent()
扯远了,我们回到发送事件的代码,继续往下看:
try {
// 循环事件队列,发送事件.然而我认为这个事件队列中最多只有一个事件
while (!eventQueue.isEmpty()) {
// 调用此方法从队列中取出第一个事件发送
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
// 最终都会重置标记
postingState.isPosting = false;
postingState.isMainThread = false;
}
在这里,会循环事件队列中的全部事件(我认为只会有一个事件)并调用postSingleEvent()
:
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
// 得到事件对象的Class对象
Class<?> eventClass = event.getClass();
// 用于标记是否找到了事件的订阅方法
boolean subscriptionFound = false;
// 是否发送事件的父类事件,默认为true,可在创建EventBus时在Builder中修改
if (eventInheritance) {
// 查找此事件的所有父类事件
List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
int countTypes = eventTypes.size();
for (int h = 0; h < countTypes; h++) {
// 得到事件列表(包含父类事件)中的事件
Class<?> clazz = eventTypes.get(h);
// 调用postSingleEventForEventType()方法发送事件
// 如果有一个事件发送成功,则subscriptionFound为true
subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
}
} else {
// 忽略父类事件,则只发送当前事件
subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
}
if (!subscriptionFound) {
if (logNoSubscriberMessages) {
// 没有订阅此事件的订阅方法时,打印日志
logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
}
// sendNoSubscriberEvent:是否在没有订阅方法接收事件时,发送NoSubscribeEvent事件,默认true
if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
eventClass != SubscriberExceptionEvent.class) {
// 满足以上条件,调用post发送EventBus提供的NoSubscriberEvent事件
post(new NoSubscriberEvent(this, event));
}
}
}
代码中已经有详细注释,重点我们关注两个地方:
- lookupAllEventTypes查找父类Class对象
- postSingleEventForEventType发送事件
默认情况下,EventBus
会查找并发送事件的父类事件,如果你不希望发送父类事件以提高性能,可以在创建EventBus
对象时进行配置。下面我们来分析lookupAllEventTypes()
方法:
private static List<Class<?>> lookupAllEventTypes(Class<?> eventClass) {
synchronized (eventTypesCache) {
// 由于遍历父类事件也是一个比较耗时的操作,因此做了缓存
List<Class<?>> eventTypes = eventTypesCache.get(eventClass);
if (eventTypes == null) {
eventTypes = new ArrayList<>();
// 缓存为空,将当前事件类赋值给clazz以便无论如何都将其加入返回结果集合
Class<?> clazz = eventClass;
while (clazz != null) {
// 找到父类,加入集合(第一次就是事件类本身)
eventTypes.add(clazz);
// 找实现接口,如果有,则添加
addInterfaces(eventTypes, clazz.getInterfaces());
// 获取父类class 没有就null停止循环
clazz = clazz.getSuperclass();
}
// 将结果放进缓存以便下次需要时直接使用
eventTypesCache.put(eventClass, eventTypes);
}
return eventTypes;
}
}
static void addInterfaces(List<Class<?>> eventTypes, Class<?>[] interfaces) {
for (Class<?> interfaceClass : interfaces) {
if (!eventTypes.contains(interfaceClass)) {
eventTypes.add(interfaceClass);
addInterfaces(eventTypes, interfaceClass.getInterfaces());
}
}
}
至此,获取完毕,非常简单。
4.1.3、postSingleEventForEventType()
获取完事件对象所有的父类类型以后,就需要将此事件分别发送给合法的订阅者了,我们来看看postSingleEventForEventType()
的源码:
/**
* 如果需处理父类事件,则此方法在for循环中调用,将分别向订阅该事件类型或是其父类的订阅者发送
* 如果不需处理,则此方法只发送给订阅当前事件类型的订阅者
*
* @param event 欲发送的事件对象
* @param postingState PostingThreadState对象
* @param eventClass 事件的Class对象,用于获取List<Subscription>
* 它可能是欲发送事件本身的类对象,也可能是欲发送事件的父类类对象
* 或是接口类型Class对象
* @return 是否完成发送
*/
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
// [订阅方法+注册类信息集合] 我们可以统称为 [订阅信息集合]
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
// 根据事件类型从subscriptionsByEventType中得到订阅者信息
// subscriptionsByEventType之前已经分析过
// 如果忘记,请查看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;
}
这个方法比较简单,在此不过多说明。最终在这个方法中调用了postToSubscription()
方法,它的作用是将事件对象发送给每一个订阅方法:
4.1.4、postToSubscription()
private void postToSubscription(Subscription subscription,
Object event, boolean isMainThread) {
// 获取订阅方法注解信息中的threadMode
switch (subscription.subscriberMethod.threadMode) {
// POSTING即发送事件在哪个线程,接收事件就在哪个线程
case POSTING:
// 因此,直接反射调用
invokeSubscriber(subscription, event);
break;
// MAIN即发送事件时如果在主线程,则直接反射执行订阅方法
// 否则交给mainThreadPoster处理
case MAIN:
if (isMainThread) {
// 发送事件时处于主线程
invokeSubscriber(subscription, event);
} else {
// 发送事件时不处于主线程
mainThreadPoster.enqueue(subscription, event);
}
break;
// MAIN_ORDERED即无论发送事件时处于哪个线程,都交由mainThreadPoster处理
case MAIN_ORDERED:
if (mainThreadPoster != null) {
mainThreadPoster.enqueue(subscription, event);
} else {
invokeSubscriber(subscription, event);
}
break;
// BACKGROUND即如果发送事件时处于主线程,则交由backgroundPoster处理
// 否则发送事件时所处的线程,就是订阅方法接收事件时所处的线程
case BACKGROUND:
if (isMainThread) {
backgroundPoster.enqueue(subscription, event);
} else {
invokeSubscriber(subscription, event);
}
break;
// ASYNC即无论发送事件时处于哪个线程,都交由asyncPoster处理
case ASYNC:
asyncPoster.enqueue(subscription, event);
break;
default:
throw new IllegalStateException("Unknown thread mode: " +
subscription.subscriberMethod.threadMode);
}
}
在这个方法中,主要是根据订阅方法的threadMode
来选择线程的调度。下面我们分部针对5种threadMode
,分别分析。
4.2、深入threadMode
4.2.1、POSTING
我们知道当@Subscribe()
没有明确指定threadMode
时,模式的threadMode
就是ThreadMode.POSTING
。我们直接来看postToSubscription()
中的这段代码:
case POSTING:
// 直接反射调用
invokeSubscriber(subscription, event);
break;
void invokeSubscriber(Subscription subscription, Object event) {
try {
subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
} catch (InvocationTargetException e) {
handleSubscriberException(subscription, event, e.getCause());
} catch (IllegalAccessException e) {
throw new IllegalStateException("Unexpected exception", e);
}
}
看到这里,我们就可以得出这样一个结论:POSTING不会做任何线程切换,接收事件时的线程 = 发送事件时所在的线程
4.2.2、MAIN
当订阅方法被@Subscribe(threadMode = ThreadMode.MAIN)
注解所标注后,它的事件接收线程模式就为ThreadMode.MAIN
。我们直接来看postToSubscription()
中的这段代码:
// MAIN即发送事件时如果在主线程,则直接反射执行订阅方法
// 否则交给mainThreadPoster处理
case MAIN:
if (isMainThread) {
// 发送事件时处于主线程
invokeSubscriber(subscription, event);
} else {
// 发送事件时不处于主线程
mainThreadPoster.enqueue(subscription, event);
}
break;
首先它判断了发送事件时的线程时否为主线程,如果是则直接反射调用订阅方法,如果不是,则执行了mainThreadPoster.enqueue(subscription, event);
。
4.2.2.1、HandlerPoster
mainThreadPoster
是什么?在EventBus
的构造方法中,有这样一行代码:
mainThreadPoster = mainThreadSupport != null ?
mainThreadSupport.createPoster(this) : null;
mainThreadSupport
还有印象吗?它实际上就是AndroidHandlerMainThreadSupport
对象,它实现了MainThreadSupport
接口,自然也实现了createPoster()
方法。如果你忘记了,请回头查看4.1.1节
。因此mainThreadSupport.createPoster(this)
实际上就是return new HandlerPoster(eventBus, looper, 10);
注意此处的第二个参数looper
是主线程的looper
,也许我们已经猜到它的作用了。
HandlerPoster
继承自Handler
实现了Poster
接口,而Poster
接口非常简单:
interface Poster {
void enqueue(Subscription subscription, Object event);
}
当线程模式为MAIN
且发送事件时不处于主线程时,就会执行下面这行代码:
// 发送事件时不处于主线程
mainThreadPoster.enqueue(subscription, event);
通过前面的分析我们已经能准确分析出,这里的调用实际上是调用的HandlerPoster
的enqueue()
方法,下面我们就来具体看一看:
public class HandlerPoster extends Handler implements Poster {
// 维护着PendingPost(待发送事件与订阅者信息)的队列
private final PendingPostQueue queue;
private final int maxMillisInsideHandleMessage;
private final EventBus eventBus;
// handleMessage是否正在处理PendingPost
private boolean handlerActive;
// HandlerPoster在EventBus创建时便会创建
protected HandlerPoster(EventBus eventBus, Looper looper,
int maxMillisInsideHandleMessage) {
// 调用了public Handler(Looper looper)
super(looper);
this.eventBus = eventBus;
this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage;
queue = new PendingPostQueue();
}
public void enqueue(Subscription subscription, Object event) {
// 得到PendingPost对象
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
synchronized (this) {
// 将PendingPost对象加入到PendingPostQueue队列中
queue.enqueue(pendingPost);
if (!handlerActive) {
// 将标记位设为true,表示handleMessage正在处理PendingPost
handlerActive = true;
// 发送一个Message至MessageQueue
// 通过looper(MainLooper)使handleMessage被回调在主线程
if (!sendMessage(obtainMessage())) {
throw new EventBusException("Could not send handler message");
}
}
}
}
@Override
public void handleMessage(Message msg) {
//...
}
}
在enqueue()
方法中,首先通过obtainPendingPost()
方法得到了PendingPost
对象,下面我们先来看看PendingPost
:
final class PendingPost {
// PendingPost对象缓存池
private final static List<PendingPost> pendingPostPool = new ArrayList<PendingPost>();
Object event;
Subscription subscription;
PendingPost next;
private PendingPost(Object event, Subscription subscription) {
this.event = event;
this.subscription = subscription;
}
static PendingPost obtainPendingPost(Subscription subscription, Object event) {
synchronized (pendingPostPool) {
int size = pendingPostPool.size();
if (size > 0) {
// 若缓存池中有数据,则直接复用缓存池中的对象
PendingPost pendingPost = pendingPostPool.remove(size - 1);
pendingPost.event = event;
pendingPost.subscription = subscription;
pendingPost.next = null;
return pendingPost;
}
}
// 否则直接创建一个新的PendingPost对象
return new PendingPost(event, subscription);
}
static void releasePendingPost(PendingPost pendingPost) {
// 在使用完成后,对PendingPost对象字段置空
pendingPost.event = null;
pendingPost.subscription = null;
pendingPost.next = null;
synchronized (pendingPostPool) {
// 防止缓存池无限制增长,因此最大不能超过10000
if (pendingPostPool.size() < 10000) {
// 将PendingPost对象放入缓存池
pendingPostPool.add(pendingPost);
}
}
}
}
简单吧,到这里我们发现EventBus
中经常使用对象缓存池,之前的FindState
,这里的PendingPost
都是如此。
继续回到HandlerPoster
的enqueue()
方法中,得到了PendingPost
对象后,会将PendingPost
对象加入到PendingPostQueue
队列中,下面我们一起来看一看PendingPostQueue
的实现:
package org.greenrobot.eventbus;
final class PendingPostQueue {
// 队列头部引用
private PendingPost head;
// 队列尾部引用
private PendingPost tail;
synchronized void enqueue(PendingPost pendingPost) {
if (pendingPost == null) {
throw new NullPointerException("null cannot be enqueued");
}
if (tail != null) {
// 若队列存在尾部,则将尾部的next引用指向当前pendingPost
tail.next = pendingPost;
// 将tail更新为当前pendingPost
tail = pendingPost;
} else if (head == null) {
// 若不存在尾部,也不存在头部,则将当前pendingPost当做头部也当做尾部
head = tail = pendingPost;
} else {
throw new IllegalStateException("Head present, but no tail");
}
notifyAll();
}
synchronized PendingPost poll() {
// 队列先进先出,取出头部PendingPost对象
PendingPost pendingPost = head;
if (head != null) {
head = head.next;
if (head == null) {
tail = null;
}
}
return pendingPost;
}
synchronized PendingPost poll(int maxMillisToWait) throws InterruptedException {
if (head == null) {
wait(maxMillisToWait);
}
return poll();
}
}
PendingPostQueue
中包含了入队与出队的方法,都很简单。将PendingPost
加入到队列中后,执行了sendMessage(obtainMessage())
,这个时候,HandlerPoster
中的handleMessage()
方法就会被回调,又由于looper
是MainLooper
,因此它是被回调在主线程的,这样也就完成了线程切换:
public class HandlerPoster extends Handler implements Poster {
// 维护着PendingPost(待发送事件与订阅者信息)的队列
private final PendingPostQueue queue;
private final int maxMillisInsideHandleMessage;
private final EventBus eventBus;
// handleMessage是否正在处理PendingPost
private boolean handlerActive;
// HandlerPoster在EventBus创建时便会创建
protected HandlerPoster(EventBus eventBus, Looper looper,
int maxMillisInsideHandleMessage) {
// ...
}
public void enqueue(Subscription subscription, Object event) {
// ...
}
@Override
public void handleMessage(Message msg) {
boolean rescheduled = false;
try {
long started = SystemClock.uptimeMillis();
// 不断从PendingPostQueue中取出PendingPost
while (true) {
PendingPost pendingPost = queue.poll();
if (pendingPost == null) {
synchronized (this) {
// 再次尝试出列
pendingPost = queue.poll();
if (pendingPost == null) {
handlerActive = false;
return;
}
}
}
// 调用eventBus中的invokeSubscriber
// 作用是回收pendingPost对象并反射调用订阅方法
eventBus.invokeSubscriber(pendingPost);
long timeInMethod = SystemClock.uptimeMillis() - started;
// 当时间超出了maxMillisInsideHandleMessage(默认是10)
// 会重新发一条消息
// 至于有什么用,目前不太清楚.
if (timeInMethod >= maxMillisInsideHandleMessage) {
if (!sendMessage(obtainMessage())) {
throw new EventBusException("Could not send handler message");
}
rescheduled = true;
return;
}
}
} finally {
handlerActive = rescheduled;
}
}
}
可以看到,在handleMessage()
中会不停地从PendingPostQueue
中取出PendingPost
,交给EventBus
的invokeSubscriber
执行订阅方法的调用:
void invokeSubscriber(PendingPost pendingPost) {
// 事件对象
Object event = pendingPost.event;
// 订阅者信息对象
Subscription subscription = pendingPost.subscription;
// PendingPost成员置空
PendingPost.releasePendingPost(pendingPost);
if (subscription.active) {
// 反射调用订阅方法
invokeSubscriber(subscription, event);
}
}
到这里,一切都结束了。这就是EventBus
为何能够切换线程的奥秘所在。
4.2.3、MAIN_ORDERED
学习了MAIN
模式后,MAIN_ORDERED
也就没什么可说的了,它只不过是无论发送事件时处于哪个线程,都会统一交由mainThreadPoster
处理:
// MAIN_ORDERED即无论发送事件时处于哪个线程,都交由mainThreadPoster处理
case MAIN_ORDERED:
if (mainThreadPoster != null) {
mainThreadPoster.enqueue(subscription, event);
} else {
invokeSubscriber(subscription, event);
}
break;
4.2.4、BACKGROUND与ASYNC
有了上面的基础以后,大家可以自行查看BackgroundPoster.java
和AsyncPoster.java
,在这里简单说明一下:BackgroundPoster
会开启一个新的线程来处理队列中的事件;AsyncPoster
会为每个事件单独开启一个新的子线程来处理。
4.3、总结
现在我们来总结一下整个post
流程:
- 通过
ThreadLocal
得到PostingThreadState
对象,将事件对象存入PostingThreadState
的队列中 - 遍历
PostingThreadState
的事件对象队列,依次取出事件对象传给postSingleEvent()
方法进行下一步 - 查找事件对象继承的类和实现的接口,得到它们的
Class
对象并保存到集合List<Class<?>> eventTypes
中 - 循环
eventTypes
取出每个Class
对象(订阅当前事件或是其父类或接口的订阅方法也能收到此事件),调用postSingleEventForEventType()
进行下一步 - 根据传入的
Class
对象从subscriptionsByEventType
获得List<Subscription>
即所有订阅者信息集合,循环此集合,调用postToSubscription()
进行下一步 - 根据订阅者信息中的
threadMode
的值,分别选择不同的调用策略完成订阅方法的反射调用(主要是线程的切换)
五、EventBus的postSticky
EventBus
源码分析最后一部分了,加油呀!我们知道EventBus
除了发送普通事件以外,还能发送粘性事件,意思是如果我们发送了一个粘性事件后再去订阅此事件,同样能收到消息。这是怎么做到的呢?我们一起来学习这部分的源码吧:
public void postSticky(Object event) {
synchronized (stickyEvents) {
// 以事件Class对象为key,事件对象本身为value保存
stickyEvents.put(event.getClass(), event);
}
post(event);
}
只不过在执行正常post
流程之前,先将事件对象保存到了stickyEvents
这个Map
中。现在我们要回到register()
方法,来探究一个我们之前跳过的内容。
if (subscriberMethod.sticky) {
// eventInheritance还熟悉吧
// false表示仅将事件对象发送给订阅了自己的订阅者
// true表示还需要考虑发送给订阅了事件对象父类或接口的订阅者
// 在postSingleEvent()中也用到了此变量判断
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)) {
// 如果eventType(订阅类型的Class对象)是粘性Map中取出的事件Class对象的父类
// 则也需要接收此事件
Object stickyEvent = entry.getValue();
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
// 只关心事件对象本身类型就好
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
private void checkPostStickyEventToSubscription(Subscription newSubscription,
Object stickyEvent) {
if (stickyEvent != null) {
// 这又回到了post的最后一步
postToSubscription(newSubscription, stickyEvent, isMainThread());
}
}
5.1、总结
在发送粘性事件时,会将事件保存在Map
中。在订阅者完成订阅的最后一步,会去查找自己订阅的事件类型在粘性事件Map
中是否存在,如果存在则取出事件对象,执行发送过程。
六、结束
终于结束了对EventBus
的源码分析。从EventBus
的源码中收获了不少知识,感谢所有开源者!我们共同进步!
本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!