简介
EventBus是针对Android优化的发布-订阅事件总线,简化了Android组件间的通信。EventBus以其简单易懂、优雅、开销小等优点而备受欢迎,基本在每一个APP都会用到,Eventbus源码较为简单,可作为开源框架源码学习入门教材
如何使用
gradle中添加依赖:
implementation 'org.greenrobot:eventbus:3.2.0'
在需要发布事件的组件里调用如下代码即可实现事件发送
EventBus.getDefault().post(TestEvent())
在需要订阅事件的页面进行注册,并添加相应@Subscribe注解即可实现事件订阅
override fun onStart() {
super.onStart()
EventBus.getDefault().register(this)
}
override fun onStop() {
super.onStop()
EventBus.getDefault().unregister(this)
}
@Subscribe(threadMode = ThreadMode.MAIN)
public fun onRecEvent(event:TestEvent) {
Log.e(TAG, "收到事件:$event")
}
源码分析(register和unregister)
EventBus.getDefault() 由字面感觉是个单例,点进去看看:
static volatile EventBus defaultInstance;
/** Convenience singleton for apps using a process-wide EventBus instance. */
//可以看到是采用双重锁的一个单例模式
public static EventBus getDefault() {
EventBus instance = defaultInstance;
if (instance == null) {
synchronized (EventBus.class) {
instance = EventBus.defaultInstance;
if (instance == null) {
instance = EventBus.defaultInstance = new EventBus();
}
}
}
return instance;
}
register注册
/**
* Registers the given subscriber to receive events. Subscribers must call {@link #unregister(Object)} once they
* are no longer interested in receiving events.
*
* Subscribers have event handling methods that must be annotated by {@link Subscribe}.
* The {@link Subscribe} annotation also allows configuration like {@link
* ThreadMode} and priority.
*/
public void register(Object subscriber) {
//获取订阅者对象类型
Class> subscriberClass = subscriber.getClass();
//获取该订阅者的所有订阅方法(即被打上 @Subscribe()注解的方法)
List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
//上锁
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
//遍历逐一订阅每一个方法
subscribe(subscriber, subscriberMethod);
}
}
}
上述流程,第一步先是获取了subscriber的对象类型,然后通过findSubscriberMethods()方法获取了该subscriber的所有订阅方法,并将每个方法包装为SubscriberMethod对象,最后遍历逐一调用subscribe()去订阅每一个方法。
这里看一下findSubscriberMethods()是如何查找订阅者所订阅的方法并将其包装为SubscriberMethod返回的:
//缓存map,key是订阅者对象类型,value是该订阅者下的所有订阅方法
private static final Map, List> METHOD_CACHE = new ConcurrentHashMap<>();
List findSubscriberMethods(Class> subscriberClass) {
//1.先从缓存获取该订阅者下的所有订阅方法,获取到的话直接返回
List subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
//是否忽略注解器生成的MyEventBusIndex
//默认ignoreGeneratedIndex为false,即忽略,走findUsingInfo分支
if (ignoreGeneratedIndex) {
subscriberMethods = findUsingReflection(subscriberClass);
} else {
subscriberMethods = findUsingInfo(subscriberClass);
}
//找不到@Subscribe()注解的方法,抛出异常,这就是平时调用EventBus.getDefault().register(this)然后没有注册接收事件闪退的原因
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;
}
}
ignoreGeneratedIndex不设置的话默认为false,所以这里直接走到了findUsingInfo()方法,看一下findUsingInfo()具体是怎么查找订阅方法的:
private List findUsingInfo(Class> subscriberClass) {
//这里通过FindState来储存找到的方法信息
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
// 循环,从当前类开始遍历该类的所有父类
while (findState.clazz != null) {
//获取订阅者的信息,因为没有默认没有使用MyEventBusIndex,这里获取到会是null,走findUsingReflectionInSingleClass分支
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 {
//正常没有使用MyEventBusIndex来到这里
findUsingReflectionInSingleClass(findState);
}
// 将findState.clazz设置为当前的findState.clazz的父类
findState.moveToSuperclass();
}
//获取methodsList并释放资源
return getMethodsAndRelease(findState);
}
static class FindState {
//某个订阅者下的所有订阅方法(这个list就是我们最终要拿到返回回去到第一步的)
final List subscriberMethods = new ArrayList<>();
//map集合,key为EventType,value为订阅者的某一个方法
final Map anyMethodByEventType = new HashMap<>();
//map集合,key为订阅方法名等参数拼接起来的string,value为方法的getDeclaringClass()
final Map subscriberClassByMethodKey = new HashMap<>();
final StringBuilder methodKeyBuilder = new StringBuilder(128);
Class> subscriberClass;
Class> clazz;
boolean skipSuperClasses;
SubscriberInfo subscriberInfo;
void initForSubscriber(Class> subscriberClass) {
this.subscriberClass = clazz = subscriberClass;
skipSuperClasses = false;
subscriberInfo = null;
}
//省略
.....
}
上一步findUsingInfo()主要是走到了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) {
// Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
try {
methods = findState.clazz.getMethods();
} catch (LinkageError error) { // super class of NoClassDefFoundError to be a bit more broad...
String msg = "Could not inspect methods of " + findState.clazz.getName();
if (ignoreGeneratedIndex) {
msg += ". Please consider using EventBus annotation processor to avoid reflection.";
} else {
msg += ". Please make this class visible to EventBus annotation processor to avoid reflection.";
}
throw new EventBusException(msg, error);
}
findState.skipSuperClasses = true;
}
//遍历找到的方法数组
for (Method method : methods) {
//获取方法声明的修饰符,如private,public,protected等
int modifiers = method.getModifiers();
//进行位运算,取出用public修饰的方法,这就是为什么我们声明接收事件必须要使用public的原因
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];
// 检查是否需要添加进findState的订阅方法列表,后面看怎么较验
if (findState.checkAdd(method, eventType)) {
//获取注解的threadMode 信息,即我们常使用的@Subscribe(threadMode = ThreadMode.MAIN)
ThreadMode threadMode = subscribeAnnotation.threadMode();
//添加进订阅方法列表
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");
}
}
}
由上面代码可以知道,findUsingReflectionInSingleClass()确实是使用了很多反射方法,第一步先调用getDeclaredMethods获取了Method[]数组,然后遍历该方法数组,取出用public修饰符修饰且只有单个参数的方法,紧接着取出该方法下的注解(必须是Subscribe类型的注解),然后通过调用findState.checkAdd(method, eventType)方法,判断是否需要将该方法添加进findState.subscriberMethods数组中,这个subscriberMethods数组也就是我们最终要返回回第一步的数据源。
看一下findState.checkAdd(method, eventType)方法是如何判断是如何较验这个订阅方法是否有效的
static class FindState {
//省略
.....
boolean checkAdd(Method method, Class> eventType) {
// 2 level check: 1st level with event type only (fast), 2nd level with complete signature when required.
// Usually a subscriber doesn't have methods listening to the same event type.
//map集合,key为EventType,value为订阅者的某一个method方法
Object existing = anyMethodByEventType.put(eventType, method);
if (existing == null) {
//之前没有其他方法订阅了该事件类型,返回true,表示需要添加进subscriberMethods数组
return true;
} else {
if (existing instanceof Method) {
//之前已经有方法订阅过该事件类型,例如void1 void2都订阅接受String类型,则此时会走到这个分支,进行二次较验
if (!checkAddWithMethodSignature((Method) existing, eventType)) {
// Paranoia check
throw new IllegalStateException();
}
// Put any non-Method object to "consume" the existing Method
//放进map
anyMethodByEventType.put(eventType, this);
}
return checkAddWithMethodSignature(method, eventType);
}
}
//二次较验
private boolean checkAddWithMethodSignature(Method method, Class> eventType) {
//key为方法名+"<"+事件类型
methodKeyBuilder.setLength(0);
methodKeyBuilder.append(method.getName());
methodKeyBuilder.append('>').append(eventType.getName());
String methodKey = methodKeyBuilder.toString();
Class> methodClass = method.getDeclaringClass();
Class> methodClassOld = subscriberClassByMethodKey.put(methodKey, methodClass);
if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) {
// Only add if not already found in a sub class
// 首次注册methodClassOld为null,符合条件,之后methodClassOld不为空
//使用methodClassOld.isAssignableFrom(methodClass)判断methodClassOld是否为methodClass的父类,然后之前了解到我们是从子类开始遍历到父类的,
//显然这个条件不满足,这也是使用EventBus的时候子类不能重写父类的函数原因??????
return true;
} else {
// Revert the put, old class is further down the class hierarchy
subscriberClassByMethodKey.put(methodKey, methodClassOld);
return false;
}
}
//省略
.....
}
到这里获取订阅方法的流程就已经结束了,获取到的订阅方法已经被打包成 SubscriberMethod对象并添加到findState.subscriberMethods数组里面去了,接下来只要把它取出来就好了。
回到我们刚开始分析注册进来的地方,我们在一系列操作后拿到了订阅者所订阅的所有方法List
public void register(Object subscriber) {
Class> subscriberClass = subscriber.getClass();
List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
subscribe(subscriber, subscriberMethod)代码如下:
// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
//获取该订阅方法的订阅类型
Class> eventType = subscriberMethod.eventType;
//通过订阅者和订阅方法构建Subscription 对象
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
//通过事件类型,获取该事件类型下的所有订阅事件
CopyOnWriteArrayList subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<>();
//放入map缓存
subscriptionsByEventType.put(eventType, subscriptions);
} else {
//如果重复订阅则会报错,这也是为什么有时我们多次调用 EventBus.getDefault().register(this)闪退的原因
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);
...省略粘性事件相关代码
}
简单讲,订阅流程就是将获取到的subscriberMethod数组逐一添加到subscriptionsByEventType和typesBySubscriber这两个map中去,为了给在事件发送的时候使用,以此形成通信。
这里比较复杂的就是获取方法的流程,简单总结调用顺序如下:
findSubscriberMethods()->findUsingInfo()->包装成FindState对象存储相关信息和映射关系->findUsingReflectionInSingleClass()
unregister解注册
/** Unregisters the given subscriber from all event classes. */
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());
}
}
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--;
}
}
}
}
取消订阅的方法中跟订阅反着来,subscriptionsByEventType和typesBySubscriber有没有很熟悉!!!首先通过获取typesBySubscriber map获取该订阅者所订阅的所有订阅类型,接着通过subscriptionsByEventType map获取每一个订阅类型下的所有属于该订阅者的订阅方法,然后逐一移除掉。
以上就是订阅/取消订阅的流程,关于订阅后是如何接受到事件的,EventBus.getDefault().post(TestEvent())是如何发送给订阅者的,下一篇讲。。
下一篇地址:https://www.jianshu.com/p/4792c5a30c30