本片文章不写简单实用,如果是想学习集成EventBus,本篇文章不适合你。
目录
- 图解EventBus类的主要依赖
- 类EventBus(含事件多线程工作原理)
- 类SubscriberMethod
- 类Subscription
- 类SubscriberMethodFinder
图解EventBus类的主要依赖

类EventBus
register方法:
(1)将订阅者的每个订阅方法缓存在SubscriberMethodFinder中,避免重复订阅
(2)将每个订阅方法缓存在HasMap<消息体, CopyOnWriteArrayList<Subscription>>中,用来记录所有拥有‘消息体’的方法,在发送消息时,根据’消息体’找到所有方法进行触发
(3)将订阅者缓存在HashMap<订阅者, List<消息体>>中,用来对外判断订阅者是否订阅某方法
(4)如果订阅方法是粘性广播,如果之前已有发送该事件,那么先触发一次该订阅方法。
原理是:
public void postSticky(Object event) {
synchronized (stickyEvents) {
stickyEvents.put(event.getClass(), event);
}
post(event);
}
每次发送粘性广播,使用HashMap将<订阅方法的参数的类名,订阅方法的参>缓存起来。在订阅方法时,发现参数之前已有发送过消息,那么就取到对应参数,触发该订阅方法,因此这里有以下限制:
(1)HashMap保存的是参数的类名,因此,要使用粘性事件,每个事件必须得定义一个特殊的参数来避免消息错乱
unregister方法:
(1)清空HasMap<消息体, CopyOnWriteArrayList<Subscription>>
(2)清空HashMap<订阅者, List<消息体>>
post方法(含事件多线程工作原理)
事件多线程工作原理
如下面代码所示,currentPostingThreadState使用ThreadLocal封装PostingThreadState,这样保证每个发送线程有自己的PostingThreadState,也就是每个线程有自己消息队列以及状态,在调用post(Object event)方法不相互影响。倘若不使用ThreadLocal,只有一个消息队列,无法记录维护消息的发送线程,线程切换处理将会很复杂,例如,当ThreadMode为默认的POSTING(消息发送线程是什么类型,订阅的方法接收时就运行在什么类型的线程)时,多线程情况下,这时非常可能出现,在主线程分发的消息,订阅的方法会运行在异步线程中。
public void post(Object event) {
//currentPostingThreadState.get使用ThreadLocal,这样保证每个发送线程有自己的PostingThreadState,
//也就是每个线程有自己消息队列以及状态,在调用post(Object event)方法不相互影响。
//倘若不使用ThreadLocal,只有一个消息队列,无法记录维护消息的发送线程,线程切换处理将会很复杂,
//例如,当ThreadMode为默认的POSTING(消息发送线程是什么类型,订阅的方法接收时就运行在什么类型的线程)时,
//多线程情况下,这时非常可能出现,在主线程分发的消息,订阅的方法会运行在异步线程中
PostingThreadState postingState = currentPostingThreadState.get();
List<Object> eventQueue = postingState.eventQueue;
eventQueue.add(event);
if (!postingState.isPosting) {
postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper();
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;
}
}
}
postSticky方法:
postSticky(消息体)与post(消息体)区别在于,会先将‘消息体’使用ConcurrentHashMap<消息体Class, 消息体>缓存起来,这个缓存在下一个订阅者订阅时,如果有订阅方法的消息体对得上,会先触发一下。因此达到了粘性广播的目的,如下所示。
//代码位于EventBus#postSticky(Object event)中
public void postSticky(Object event) {
synchronized (stickyEvents) {
// stickyEvents就是ConcurrentHashMap<消息体Class, 消息体>
stickyEvents.put(event.getClass(), event);
}
post(event);
}
看看订阅时触发的代码:
//代码位于EventBus#subscribe(Object subscriber, SubscriberMethod subscriberMethod)中
if (subscriberMethod.sticky) {
//eventInheritance,默认true。true表示如果订阅的方法的参数是
//stickyEvents(ConcurrentHashMap<消息体Class, 消息体>)
//中某个key‘的父类或者父接口,也要触发。
//比如,stickyEvents中有key为‘A’的数据,而IA是A的接口,
//那么在订阅粘性方法method(IA)时,该方法会被触发。
if (eventInheritance) {
// Existing sticky events of all subclasses of eventType have to be considered.
//这里有性能问题,建议sticky事件的参数尽量简洁,以下是参数反例:List<Class>)
// Note: Iterating over all events may be inefficient with lots of sticky events,
// thus data structure should be changed to allow a more efficient lookup
// (e.g. an additional map storing sub classes of super classes: Class -> List<Class>).
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);
}
}
类SubscriberMethod
每个订阅方法就是一个SubscriberMethod,SubscriberMethod包含以下属性
- Method:Java原有类
- ThreadMode:4种类型,默认、主线程、后台线程(内置的单个线程中执行)、异步线程(每次起一个线程)
- final Class<?> eventType:订阅方法的参数,被订阅的方法肯定只有一个参数
- priority:优先级,在队列中,在订阅时,将SubscriberMethod按照priority从大到小排序
- sticky:是否是粘性广播
- String methodString:方便在两个SubscriberMethod的比较,规则 Method所属类名#Method名(eventType 名
类Subscription
有以下3个属性
//订阅者
final Object subscriber;
//见上面类SubscriberMethod
final SubscriberMethod subscriberMethod;
//是否激活,true表示SubscriberMethod还可接受消息。在构造函数中被初始化为true,所以在注册时,遍历查找哪些方法是SubscriberMethod时被注册;在取消注册时这个变量立即被置为false
volatile boolean active;
类SubscriberMethodFinder
订阅者注册时,用来辅助查找订阅者中是否含有被'@Subscribe’注解的方法,找到的话使用ConcurrentHashMap<Class<?>, List<SubscriberMethod>>缓存起来,起到缓存查询作用
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
if (ignoreGeneratedIndex) {
//通过反射查找被@Subscribe注解的方法,并且会循环遍历父类查找
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;
}
}