参考链接:https://blog.csdn.net/qq_38859786/article/details/80285705
https://www.jianshu.com/p/e1d52c7f2581
1.register方法分析:
先上源码
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
//找到订阅者的方法.找出传进来的订阅者的所有订阅方法,然后遍历订阅者的方法.
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
//订阅者的注册
subscribe(subscriber, subscriberMethod);
}
}
}
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass); //首先从METHOD_CACHE中读取缓存下来的订阅方法列表
if (subscriberMethods != null) {
return subscriberMethods;
}
// ignoreGeneratedIndex属性表示是否忽略注解器生成的MyEventBusIndex。如何生成MyEventBusIndex类以及他的使用,可以本文中apt部分的详细说明。ignoreGeneratedIndex的默认值为false,可以通过EventBusBuilder来设置它的值
if (ignoreGeneratedIndex) {
// 利用反射来获取订阅类中所有订阅方法信息
subscriberMethods = findUsingReflection(subscriberClass);
} else {
// 从注解器生成的MyEventBusIndex类中获得订阅类的订阅方法信息
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;
}
}
这一步就是利用反射或者索引找到当前的所有订阅方法,组建成SubscriberMethod列表返回,并且此处会对注册类的所有SubscriberMethod做一次缓存,以加快下一次注册的速度
接下来回到register方法里,对获取到的SubscriberMethod列表进行for循环订阅subscribe
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
// 获取订阅方法的事件类
Class<?> eventType = subscriberMethod.eventType;
// 创建订阅类
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
// 获取订阅了此事件类的所有订阅者信息列表,如果不存在则将其加入订阅者信息列表,如果已经存在此订阅者信息则抛出已注册的异常
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);
// 判断是否是粘性事件,对粘性事件立即进行分发
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<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);
}
}
}
上面的方法描述为:
1.用当前订阅对象和当前订阅方法组合成订阅(Subscription)对象
2.EventBus里有一个以EventType(即事件类,Class类型)为key,CopyOnWriteArrayList<Subscription>为值的HashMap subscriptionsByEventType,这样就可以通过一个类型找到它的所有订阅(Subscription)
3.从subscriptionsByEventType里取出List<Subscription>集合,没有则新建一个CopyOnWriteArrayList并以当前SubscriberMethod的订阅类型(EventType)为键put到subscriptionsByEventType中
4.第三步 取出来/新建的 CopyOnWriteArrayList<Subscription>集合判断是否已经有第1步建立的订阅对象,如果有的话则抛出异常报错,没有则将其 按照当前订阅方法(SubscriberMethod)的优先级add到集合中
5.从Eventbus里的typesBySubscriber(HashMap,以注册者对象为key,以注册者订阅类型集合为键)获取/新建一个 当前注册对象的 所有 订阅类型 的集合(List<Class<?>>),并将当前订阅方法的事件类型(EventType)添加到集合中。后面unregister时就会很方便
6.粘性事件处理,如果当前订阅方法的类型是粘性事件 (subscriberMethod.sticky),则进入粘性事件分发流程,EventBus有一个以事件类型(EventType)为key,发送了的粘性事件对象为键的ConcurrentHashMap<Class<?>, Object> stickyEvents,粘性事件处理时会先从stickyEvents里查找是否有当前订阅方法 事件类型(EventType)的粘性事件,如果有则通过 不同的threadMode(标识在哪个线程执行,有POSTING,MAIN,BACKGROUND,ASYNC 四种模式)分发到不同的线程并用当前注册对象反射调用当前的订阅方法
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);
}
}
可见粘性是在register时调用的,如果此时Activity或者Fragment的view没有准备好则会出现奔溃
2.unregister方法分析
先上源码
public synchronized void unregister(Object subscriber) {
// 获取订阅对象的所有订阅事件类列表
//typesBySubscriber上面5.提到的map
List<Class<?>> 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());
}
}
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
// 获取事件类的所有订阅信息列表,将订阅信息从订阅信息集合中移除,同时将订阅信息中的active属性置为FALSE
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; // 将订阅信息激活状态置为FALSE
subscriptions.remove(i); // 将订阅信息从集合中移除
i--;
size--;
}
}
}
}
找到当前注册对象的所有订阅事件类型,然后一个个的调用unsubscribeByEventType方法,将当前注册对象从其订阅类型的订阅者集合表移除,即找到 当前订阅者的 所有 订阅类型(EventType),通过 订阅类型 找到该订阅类型 的 订阅集合List<Subscription> ,将当前注册者从集合中移除
最后从typesBySubscriber(<订阅者,订阅类型>map)里将当前订阅者移除
总结:
- register
- 通过注解找到当前类所有订阅方法(被@Subscribe注解的方法),并对当前注册类的所有订阅方法做缓存
- for循环注册当前注册者的所有订阅方法,每个方法的订阅如下
- 用当前订阅对象和订阅方法组合成一个订阅信息对象,然后将其依据优先级加入到订阅者列表中。并将列表put到以订阅类型为key的map中方便后面查找。
- 将当前订阅事件类型添加到当前注册者的订阅类型集合,该集合保存在一个以订阅者为key的map中,方便后边注销(unregistered)
- 粘性事件处理。如果当前订阅方法是一个粘性事件,则进入粘性事件分发流程(在粘性事件列表中找到有没有人发布过该粘性事件,有的话就调用相关订阅方法)
- 注:订阅方法的调用就是通过放射调用了注册的方法
- unregister
- 用缓存了所有订阅者订阅信息的map(以订阅者为key,以该订阅者的所有订阅信息为集合为value)总找到当前订阅者的所有订阅类型集合,对所有的订阅形象进行注销
- 即根据事件类型找到其订阅者列表,将该订阅者从列表中移除
- 订阅者订阅信息map中移除该订阅者