前边文章主要跟大家大概讲了下EventBus的用法和注解,接下来则是从源码角度来看EventBus的内部处理
EventBus源码解析系列
EventBus源码解析(一)关于用法和注解
EventBus源码解析(二)register与unregister
EventBus源码解析(三)Post方法和注解处理器
首先我们从register方法看起
EventBus.getDefault().register(this);
getDefault则是用的一个双重校验锁来保证线程安全,保持一个单例
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
它的构造方法则为
public EventBus() {
this(DEFAULT_BUILDER);
}
在这里DEFAULT_BUILDER
是一个Builder,之后就根据Builder设定的值赋值给它的成员变量,我们主要看这几个变量
private final Map, CopyOnWriteArrayList> subscriptionsByEventType;
private final Map
subscriptionsByEventType:以当前event事件类为key,订阅对象Subscription为value。事件发送之后,则是在这里寻找订阅者。在这里,
Subscription
则是一个封装了订阅者和方法体的一个类。typesBySubscriber:以订阅类为Key,以event事件类为value,在进行register和unregister操作时会操作这个map
stickyEvents:保存的是粘性事件,关于粘性事件的设置以及使用上一篇文章则有说到。
后面三个Poster则是用来处理粘性事件的,这个后面慢慢介绍到。
看下register方法
public void register(Object subscriber) {
Class> subscriberClass = subscriber.getClass();
List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
可以看到这个register方法传入当前类的对象,然后通过subscriberMethodFinder.findSubscriberMethods
获取到当前对象的所有方法,而方法则是封装在SubscriberMethod
这个类中,获取到方法之后则遍历调用subscribe
方法。
我们来看一下EventBus内部是如何拿到一个类的方法的。
List findSubscriberMethods(Class> subscriberClass) {
List subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
//①默认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;
}
}
首先,它会根据当前类的key去缓存中寻找,如果缓存中有当前类的方法集合,则直接获取。当然,一开始则是为Null。
接下来对ignoreGeneratedIndex
进行判断,这里暂不解释它,默认值则为false,除非你在Builder自己设置,否则通过EventBus.getDefault()
则为false。false后调用了findUsingInfo
方法,这个方法则肯定是用来获取当前类的方法的。之后获取到方法之后则判断当前方法是否是public以及是否有设置注解@Subscribe,否则则抛出异常。
private List findUsingInfo(Class> subscriberClass) {
//①获取FindState对象
FindState findState = prepareFindState();
//初始化
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
//②获取订阅者的信息,一开始为null,如果有使用注解处理器,则不为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 {
//③通过反射来获取方法信息,之后保存在findState
findUsingReflectionInSingleClass(findState);
}
findState.moveToSuperclass();
}
//④从findState中获取到SubscriberMethod
return getMethodsAndRelease(findState);
}
FindState
主要用来保存订阅者的信息,内部同样有很多用来保存订阅信息的map
//订阅方法的保存
final List subscriberMethods = new ArrayList<>();
//以event为key,以method为value
final Map anyMethodByEventType = new HashMap<>();
//以method的名字生成一个method为key,以订阅者类为value
final Map subscriberClassByMethodKey = new HashMap<>();
首先看下上面流程
- ①调用
prepareFindState
来获取一个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();
}
prepareFindState
方法里面则会先去线程池中去查找有没有FindState
没有则自己创建一个。然后调用initForSubscriber
来初始化信息
void initForSubscriber(Class> subscriberClass) {
this.subscriberClass = clazz = subscriberClass;
skipSuperClasses = false;
subscriberInfo = null;
}
保存当前的订阅者
- ②通过
getSubscriberInfo(findState);
根据当前的findState来获取订阅者的信息。
private SubscriberInfo getSubscriberInfo(FindState findState) {
//新创建的subscriberInfo为null
if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) {
SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();
if (findState.clazz == superclassInfo.getSubscriberClass()) {
return superclassInfo;
}
}
//判断有没有使用注解处理器,如果有使用,则在编译器时候通过读取@Subscribe()注解并解析保存到subscriberInfoIndexes中了。
if (subscriberInfoIndexes != null) {
for (SubscriberInfoIndex index : subscriberInfoIndexes) {
SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
if (info != null) {
return info;
}
}
}
return null;
}
可以说这个方法主要判断有没有使用注解处理器,默认的情况下是没有使用的,关于注解处理器这个后面会讲。
- ③调用
findUsingReflectionInSingleClass(findState);
方法,根据当前findState来反射获取方法。
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();
//判断当前方法的类型是否是public,以及有没有使用注解@Subscribe
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];
//调用checkAdd进行检查
if (findState.checkAdd(method, eventType)) {
//获取模式
ThreadMode threadMode = subscribeAnnotation.threadMode();
//创建一个SubscribeMethod并保存到findState里面。
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");
}
}
}
可以看到整个反射的流程很清晰,先反射获取方法,然后进行方法判断,不满足情况则抛出异常,接着则获取方法信息保存在findState中
通过反射将获取到的方法信息保存在FindState
后,之后调用getMethodsAndRelease(findState);
方法来从FindState
中获取方法集合。
之后回到register方法,调用subscrib
public void register(Object subscriber) {
Class> subscriberClass = subscriber.getClass();
List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
前面都是调用findSubscriberMethods
来获取一个类中的注解方法,接下来则是调用了subscribe
方法;
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
//①根据方法和订阅者构建一个Subscrption,将记录保存在subcriptionByEventType
Class> eventType = subscriberMethod.eventType;
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
CopyOnWriteArrayList 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;
}
}
//③保存事件类型(参数类型)到typesBySubscriber
List> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
//④粘性事件的处理
if (subscriberMethod.sticky) {
if (eventInheritance) {
Set, Object>> entries = stickyEvents.entrySet();
for (Map.Entry, 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);
}
}
}
subscribe方法我们分为4部分
①第一部分比较好理解,根据方法和订阅者构造一个
Subsciption
用来保存订阅者信息,然后将信息保存在subscriptionsByEventType
中,而这里的key则是eventType,关于这个eventType说成是事件类型可能大多数很懵比,但如果说成是方法的参数类型的话就好理解了。EventBus的接收事件则是根据这个eventType来判断是否是同个接收事件的,如果两个方法的参数名一样,那么都会接收到事件。②第二部分则是根据在@Subscribe注解中设定的优先级去整理
subscriptions
里面的顺序问题③第三部分则是把事件类型(参数类型)保存到typesBySubscriber。之后就可以根据Post的参数从typesBySubscriber中拿到对应的eventType,再到
subscriptionsByEventType
根据key eventType拿到订阅方法区调用。④
if (subscriberMethod.sticky)
这里面则是关于粘性事件的处理,关于粘性事件的设置处理情况我们在第一篇文章中也说过了,会设置粘性的情况在于发送事件的时机早于注册时机,此时如果使用了postSticky发送并且@Subscribe注解指定sticky = true
的话则可以在注册后触发最近的一个事件,而该事件的触发就是在这里。接下来我们重点看这个if里面
if (subscriberMethod.sticky) {
if (eventInheritance) {
// Existing sticky events of all subclasses of eventType have to be considered.
// 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).
Set, Object>> entries = stickyEvents.entrySet();
for (Map.Entry, 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);
}
在这里eventInheritance
默认是true,表示事件要考虑子类问题。找到对应的类后最后调用的是这来两个方法
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
stickyEvents
在开头介绍3个成员变量时候说到,它是用来保存粘性事件的,那么它的添加Put是在哪里呢,没错就是postSticky
public void postSticky(Object event) {
synchronized (stickyEvents) {
stickyEvents.put(event.getClass(), event);
}
// Should be posted after it is putted, in case the subscriber wants to remove immediately
post(event);
}
这里stickyEvents
的key则是为参数类型,所以对于粘性事件中同个参数类型的话只会保留最后使用的那个。这点在第一篇文件也有提及到。
获取到事件后看它是怎么处理的。
private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
if (stickyEvent != null) {
postToSubscription(newSubscription, stickyEvent, Looper.getMainLooper() == Looper.myLooper());
}
}
先做判空,之后发送给订阅者,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 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来选择对应的发送模式,关于ThreadMode之间的用法区别可以参照第一篇文章。
这里我们就挑选MainThread来讲吧。
MAIN:这里会先判断当前的执行线程是否是主线程,如果是则直接执行invokeSubscriber
直接反射调用方法,否则则调用mainThreadPoster.enqueue
等待事件发送到主线程调用。
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
比较简单,最终是调用的方法的反射调用。
对于mainThreadPoster.enqueue(subscription, event);
mainThreadPoster是一个HandlerPoster对象,这个HandlerPoster对象则是继承与Handler,内部维护了一个事件订阅调用队列,把每一个事件调用都放到这个队列中,等待调度执行。
private final HandlerPoster mainThreadPoster;
HandlerPoster里面也比较好容易理解
final class HandlerPoster extends Handler {
private final PendingPostQueue queue;
private final EventBus eventBus;
...
HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage{
super(looper);
...
}
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");
}
}
}
}
@Override
public void handleMessage(Message msg) {
boolean rescheduled = false;
try {
long started = SystemClock.uptimeMillis();
while (true) {
PendingPost pendingPost = queue.poll();
...
}
eventBus.invokeSubscriber(pendingPost);
...
}
} finally {
handlerActive = rescheduled;
}
}
}
它最终也只是在handlerMessage调用了invokeSubscriber
方法,但是它是怎么运行在主线程的呢?这个时候如果你懂得Handler消息传递机制的话就应该知道关键在于Looper,我们看EventBus中它的创建
EventBus(EventBusBuilder builder) {
subscriptionsByEventType = new HashMap<>();
typesBySubscriber = new HashMap<>();
stickyEvents = new ConcurrentHashMap<>();
mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
...
}
在这里传递的是MainLooper,而Looper里面则有一个Thread对象,这里对应则是主线程。
到这里register部分也讲完,就讲下unregister方法吧,而post方法和注解处理器则放到下一篇。
public synchronized void unregister(Object subscriber) {
List> subscribedTypes = typesBySubscriber.get(subscriber);
if (subscribedTypes != null) {
for (Class> eventType : subscribedTypes) {
unsubscribeByEventType(subscriber, eventType);
}
typesBySubscriber.remove(subscriber);
} else {
Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
记得我们之前提到的三个Map
private final Map, CopyOnWriteArrayList> subscriptionsByEventType;
private final Map
前面有说到typesBySubscriber
和subscriptionsByEventType
的关系,typesBySubscriber
是根据当前订阅者作为Key,value则为事件类型eventType,而subscriptionsByEventType
则是根据事件类型eventType作为key,value为方法。关系调用则为
Subscriber -> typesBySubscriber
-> eventType -> subscriptionsByEventType
-> method
这unregister这里根据当前订阅者找到所有的事件类型(参数类型),
然后调用unsubscribeByEventType
方法
private void unsubscribeByEventType(Object subscriber, Class> eventType) {
List 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--;
}
}
}
}
可以看到,根据前面的eventType,作为key然后一步一步的移除注册。
unregister方法也比较好理解。
流程图小结
小结register流程