EventBus源码阅读
读源码不是为了读而读,而是要学习优秀的设计,细节
给自己的要求:详略得当,不要太抠细节
EventBus.getDefault()
public EventBus() {
this(DEFAULT_BUILDER);
}
EventBus(EventBusBuilder builder) {
logger = builder.getLogger();
//1.
subscriptionsByEventType = new HashMap<>();
typesBySubscriber = new HashMap<>();
stickyEvents = new ConcurrentHashMap<>();
//2.
mainThreadSupport = builder.getMainThreadSupport();
//3.
mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
backgroundPoster = new BackgroundPoster(this);
asyncPoster = new AsyncPoster(this);
indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
//4.
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;
}
//1 处分别定义了三个HashMap
Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;key为自定义的Event类型,value为一个List,保存的是一个有序的(按照priority)接收该Event的列表。
看下Subscription的构造函数:
Subscription(Object subscriber, SubscriberMethod subscriberMethod) {
this.subscriber = subscriber;
this.subscriberMethod = subscriberMethod;
active = true;
}
一个Subscription由订阅的类和方法定义
此处存疑
private final Map<Object, List<Class<?>>> typesBySubscriber,key为订阅的类即eventbus.subscribe(this)的this,value为该类中接收的所有Event?
private final Map<Class<?>, Object> stickyEvents;此处key为Event对象,Object为接收者所在的对象
// 2
//3
处的三个Poster用于切换接收者的线程,故名思意mainThreadPoster:将event跑到主线程执行
backgroundPoster:将event抛到后台线程执行,asyncPoster:将event抛到后台异步执行
// 4 SubscriberMethodFinder
根据订阅的类找到订阅的方法,是非常重要的一个类
EventBus.getDefault().register(this)
首先看方法注释:介绍了使用方法和注意事项
Registers the given subscriber to receive events. Subscribers must call unregister(Object) once they are no longer interested in receiving events.
注册给定的订阅者以接收事件。一旦订阅者不再想要接收事件,必须调用 unregister(Object)。
Subscribers have event handling methods that must be annotated by Subscribe. The Subscribe annotation also allows configuration like ThreadMode and priority
订阅者具必须有@Subscribe注解的事件处理方法。 Subscribe 注解还允许配置 ThreadMode 和 priority(以及sticky)
再看源码:
public void register(Object subscriber) {
...合法性校验,忽略...
// 1
Class<?> subscriberClass = subscriber.getClass();
// 2
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
// 3
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
看一下方法,并思考你会怎么做
在注释1处拿到订阅者的Class对象
在注释2处根据这个Class拿到这个Class中具有Subscribe注解的方法
在注释3处根据对象和方法订阅
对于findSubscriberMethods
如果是我,我会想到使用反射去拿Class中的方法,然后找到具有@Subscribe注解的方法,记录方法与注解中设置的属性,然后返回
//3处synchronized (this),也就是说一个EventBus实例同时只能订阅一个对象,为什么加锁?复习一下加锁必定是存在对共享资源的竞争、修改,本文里应该是全局变量,Find it
取得订阅方法
用的也是常见的思路
- 如果缓存中包含该class则,从缓存中获取方法
- 如果没有则通过反射解析注解和方法
- 将解析好的方法放入缓存
因此咱们也需要带着这两个问题去看代码
- 缓存策略,是否有出彩的地方
- 解析的方法和存储的数据结构是否合理?
学习下源码
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
// 1
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
if (ignoreGeneratedIndex) { // 初始化EventBus的时候赋值,默认为false
subscriberMethods = findUsingReflection(subscriberClass);
} else {
// 2
subscriberMethods = findUsingInfo(subscriberClass);
}
if (subscriberMethods.isEmpty()) {
throw new EventBusException("Subscriber " + subscriberClass
+ " and its super classes have no public methods with the @Subscribe annotation");
} else {
// 3
METHOD_CACHE.put(subscriberClass, subscriberMethods);
return subscriberMethods;
}
}
首先是 // 1处,应该反应过来存在同一类的不同对象注册的情况,在这里加一个缓存
// 2处,再看findUsingInfo,应该是实际查找方法以及对于信息的地方
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
// 1
FindState findState = prepareFindState();
// 2
findState.initForSubscriber(subscriberClass);
// 3
while (findState.clazz != null) { // 4 像是递归的写法
findState.subscriberInfo = getSubscriberInfo(findState);
if (findState.subscriberInfo != null) { // 5
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();// 4
}
return getMethodsAndRelease(findState);
}
prepareFindState()有一段值得学习的代码,FIND_STATE_POOL是一个size为4的数组对象,在这里复用,不用重复new FindState对象。
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();
}
看起来是一个subscriberClass对应一个FindState,那么这个FindState有什么作用?接下来会看
// 2 处将FindState初始化了一下
void initForSubscriber(Class<?> subscriberClass) {
this.subscriberClass = clazz = subscriberClass;
skipSuperClasses = false;
subscriberInfo = null;
}
再看看//3 4处,在// 5处校验了是否为null,而在initForSubscriber中这一属性以及被设置为null,那为什么还要有这个判断呢?是因为这是个递归的方法,在判断父类型里面是否有遗漏的订阅方法的时候起作用,这里只分析一下findUsingReflectionInSingleClass。去掉一系列判断后如下
private void findUsingReflectionInSingleClass(FindState findState) {
Method[] methods;
try {
// This is faster than getMethods, especially when subscribers are fat classes like Activities 很明显,父类中方法太多
// 这里是拿到当前类的所有的方法(不包括父类),私有以及公有方法
methods = findState.clazz.getDeclaredMethods();
} catch (Throwable th) {
try {
// 获取当前类和父类的所有public的方法。
methods = findState.clazz.getMethods();
} catch (LinkageError error) {
throw new EventBusException(msg, error);
}
//失败的时候这个skipSuperClasses属性才被设置为true,意思是不要再去遍历父类了,因为 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();
// 参数只能有一个,就是params
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();
// 将method, eventType, threadMode,priority, sticky等属性放在findState
findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
}
}
} else{Throw Exception()}
}
}
}
在这个方法中findState的subscriberMethods字段被赋值,subscriberMethod有method, eventType, threadMode,priority, sticky等属性,可以唯一确定一个订阅者方法,记录相关的属性
再来看一下findStateInfo的最后一个方法:getMethodsAndRelease(findState);
private List<SubscriberMethod> getMethodsAndRelease(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) {
FIND_STATE_POOL[i] = findState;
break;
}
}
}
return subscriberMethods;
}
可以看出,主要是用来返回subscriberMethods和回收刚刚用到的findState对象
继续往下看,还记得findSubscriberMethods方法吗?
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
...
subscriberMethods = findUsingInfo(subscriberClass);
...
METHOD_CACHE.put(subscriberClass, subscriberMethods);
...
return subscriberMethods;
}
这个方法就分析结束了
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
// 1
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
// 2
subscribe(subscriber, subscriberMethod);
}
}
}
既然已经在1处找到方法了,接下来就该订阅了
订阅方法
// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
// 拿到方法的event类型
Class<?> eventType = subscriberMethod.eventType;
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
//-------------------------------------------------------------------------
// 此块代码为了维护subscriptionsByEventType<clazz,list<Subscription> >字段,
// 维护了一个事件类型和该事件类型的Subscription有序列表
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);
}
}
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;
}
}
// ------------------------------------------------------------------------------
// 此块代码维护了一个订阅者中包含有几种订阅事件
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
//------------------------------------------------------------------------------
// 如果是粘性事件则单独处理,
// 1. stickyEvents在eventbus.postSticky的时候赋值
// 2. 由于粘性事件允许先post再subscribe,这里实际上是执行post
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);
}
}
}
private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
if (stickyEvent != null) {
// If the subscriber is trying to abort the event, it will fail (event is not tracked in posting state)
// --> Strange corner case, which we don't take care of here.
// 可以看到实际是执行这个post,在后面的post的时候会讲
postToSubscription(newSubscription, stickyEvent, isMainThread());
}
}
EventBus.getDefault().post()
这个方法简单一点~~
/** Posts the given event to the event bus. */
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 {
// 关键是这里,yi'c
while (!eventQueue.isEmpty()) {
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
currentPostingThreadState是ThreadLocal的,也就是说每个线程一个
/** For ThreadLocal, much faster to set (and get multiple values). */
final static class PostingThreadState {
final List<Object> eventQueue = new ArrayList<>();
boolean isPosting;
boolean isMainThread;
Subscription subscription;
Object event;
boolean canceled;
}
应该是记录当前线程的post状态
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
if (eventInheritance) {
// 找到event的所有父类,如果有父类且也是注册过的event,则也通知父类对应的订阅方法
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) {
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));
}
}
}
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
// 如果有对应的注册事件则取
subscriptions = subscriptionsByEventType.get(eventClass);
}
if (subscriptions != null && !subscriptions.isEmpty()) {
// 通知每一个注册的方法
for (Subscription subscription : subscriptions) {
postingState.event = event;
postingState.subscription = subscription;
boolean aborted;
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;
}
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使用不同的Poster入队处理,这里是可以线程的原因
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);
}
}
可以看到invokeSubscriber是根据反射,执行对应的注册过的方法。
置于其他线程以BackGroundPoster为例子最终也只是在不同的线程中执行invokeSubscriber