EventBus
作为Android开发中使用频率相当高的项目,帮助我们简化了很多复杂场景下的数据传递,当然也是Android初中高级
面试中必不可少的问题。
但是对于EventBus
使用,可能绝大多数人并没有正确的使用它,今天就让我来带大家正确的使用它,并带大家从源代码层面真正搞懂EventBus
。
EventBus项目地址: https://github.com/greenrobot/EventBus
第一步:注册
//注册
EventBus.getDefault().register(this)
//反注册
EventBus.getDefault().unregister(this)
注册和反注册操作,需要注意使用确保生命周期步调一致,比如你在onResume
中注册,就应该在onPause
中反注册。
第二步:设置方法,并添加注解
@Subscribe(threadMode = ThreadMode.MAIN)
fun onReceiverEventTwoNormal(event: FragmentEvent) {
if (event.isOne()) {
bindView {
tvReceiver.text = "接收到的内容:${
event.eventMsg}"
}
}
}
第三步:使用post
方法事件
EventBus.getDefault().post(FragmentEvent.postOne())
到这里,相信和大家使用的也都是一致的,我见过的大部分项目也都是这么设置的。
但是如果只说这些的话,相信我也就不需要写这篇文章了。
你面试有没有遇到过这样的问题呢?
req: EventBus 2.x和eventbus 3.x有什么区别?
相信很多背过面试题的肯定能脱口而出,EventBus2.x实现使用反射,而3.0是使用注解方法,并加入了索引进行了优化。
好~,看起来好像很perferct,但是你索引是怎么实现的?而且上面的代码真的是相比2.0代码性能更优化了吗?
如果我告诉你,上面的处理方式,性能相比EventBus 2.x
更差,不知道你作何感想。直接上图。
这里先放一下正确的使用方式,后面分析完源代码相信大家也就理解了。
在项目或者模块下的build.gradle
文件中添加:
kapt {
arguments {
arg('eventBusIndex', 'org.fireking.event.MyEventBusIndex')
}
}
dependencies{
kapt 'org.greenrobot:eventbus-annotation-processor:3.0.0'
...
}
重新rebuild一下代码,会在build/generated/source/kapt/debug/
下生成org.fireking.event.MyEventBusIndex
文件,打开之后发现其实就是我们使用subscribe注解标识过的类。
...
private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;
static {
SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();
putIndex(new SimpleSubscriberInfo(org.fireking.laboratory.eventbus.EventBusOneFragment.class, true,
new SubscriberMethodInfo[] {
new SubscriberMethodInfo("onReceiverEventTwoNormal", org.fireking.laboratory.eventbus.FragmentEvent.class,
ThreadMode.MAIN),
}));
putIndex(new SimpleSubscriberInfo(org.fireking.laboratory.eventbus.EventBusTwoFragment.class, true,
new SubscriberMethodInfo[] {
new SubscriberMethodInfo("onReceiverEventTwoNormal", org.fireking.laboratory.eventbus.FragmentEvent.class,
ThreadMode.MAIN),
}));
}
...
聪明的你肯定想到了,这里是直接使用kapt
,将之前注解的类和方法关系直接生成文件保存在map中了,以后使用应该直接就可以从这里面取了,不再需要反射查找了。至于具体是不是这样,后面源码会说明。
在application中,新增设置
//将上面生成的方法注册到默认的eventBus配置项中,后续使用就可以和原来一样直接用了。
EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();
对于Event
实现分析,我们直接根据常用的方法流程进行分析。
EventBus.getDefault().register(Object subscriber)
public void register(Object subscriber) {
//根据当前传入的对象,找到类
Class<?> subscriberClass = subscriber.getClass();
//根据当前类对象找到该类对象下的所有订阅方法SubscriberMethod
List<SubscriberMethod> subscriberMethods
=subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
//遍历所有的订阅方法
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
subscriberMethodFinder#findSubscriberMethods
//用来保存类和类里面所有订阅方法关系的集合
private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE
= new ConcurrentHashMap<>();
...
//SubscriberMethod是保存的使用Subscriber注解的方法信息
public class SubscriberMethod {
//使用Subscriber注解的方法
final Method method;
//对应注解中的threadMode
final ThreadMode threadMode;
//对应方法传递的参数,EventBus限制只要一个参数,这里对应的就是第一个参数
final Class<?> eventType;
//事件优先级
final int priority;
//是否粘性
final boolean sticky;
...
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
//从Method-Cachae中查找是否已经存在缓存数据
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
//如果找到已经缓存过,直接返回
if (subscriberMethods != null) {
return subscriberMethods;
}
//ignoreGeneratedIndex 用来区分是否忽略Annotation Processor生成的索引
//如果忽略的话,则默认会使用反射方式,否则使用生成的索引方式查找
if (ignoreGeneratedIndex) {
//使用反射方式查找
subscriberMethods = findUsingReflection(subscriberClass);
} else {
//使用索引方式查找,如果索引内查找不到,则使用反射再次查找
subscriberMethods = findUsingInfo(subscriberClass);
}
//如果设置了EventBus.getDefault().register(),
//但是类里面没有使用@Subscriber的方法,则会抛出异常
if (subscriberMethods.isEmpty()) {
throw new EventBusException("Subscriber " + subscriberClass
+ " and its super classes have no public methods with the @Subscribe annotation");
} else {
//检索完成之后,将类和@Subscriber方法关系保存在Method-Cacahe中
METHOD_CACHE.put(subscriberClass, subscriberMethods);
//返回subscriberMethods
return subscriberMethods;
}
}
private List findUsingReflection(Class> subscriberClass)
反射方式private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
//从大小为4的缓存池中获取FindState对象
FindState findState = prepareFindState();
//初始化FindState对象状态
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
//使用反射方式查找
findUsingReflectionInSingleClass(findState);
//从父类查找
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
核心查找方法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) {
//获取方法修饰符 什么都不加 是0 , public 是1 ,private 是 2 ,protected 是 4,static 是 8 ,final 是 16。
int modifiers = method.getModifiers();
//修饰符 为public
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
//获取参数的类对象类型
Class<?>[] parameterTypes = method.getParameterTypes();
//eventBus的参数只能使用一个,超过一个则抛出异常
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();
//讲查找到的方法数据保存到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");
}
}
}
父类查找,可以看之前代码,这里会将clazz方法替换为父类对象,继续在while循环中使用`findUsingReflectionInSingleClass`和`moveToSuperclass`进行逐层查找。
```java
void moveToSuperclass() {
//如果跳过查找父类,返回null
if (skipSuperClasses) {
clazz = null;
} else {
//返回父类
clazz = clazz.getSuperclass();
String clazzName = clazz.getName();
// Skip system classes, this degrades performance.
// Also we might avoid some ClassNotFoundException (see FAQ for background).
//跳过java和android类
if (clazzName.startsWith("java.") || clazzName.startsWith("javax.") ||
clazzName.startsWith("android.") || clazzName.startsWith("androidx.")) {
clazz = null;
}
}
}
}
最后返回查找到的方法集合,并清除缓存池getMethodsAndRelease
`findState`缓存池在使用时候,需要释放,以供其他操作使用
```java
private List getMethodsAndRelease(FindState findState) {
List 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;
}
因为查找方式有两种,分别是反射方式和apt方式,接下来会讲解apt方式,至于查找到时候使用方式,下面也会做说明。
private List findUsingInfo(Class> subscriberClass)
优先apt方式,查找不到会反射查找private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
//从apt生成的索引信息中查找
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 {
//索引中没有找到,则通过反射进行查找
findUsingReflectionInSingleClass(findState);
}
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
getSubscriberInfo
private SubscriberInfo getSubscriberInfo(FindState findState) {
if (findState.subscriberInfo != null &&
findState.subscriberInfo.getSuperSubscriberInfo() != null) {
//在父类中查找
SubscriberInfo superclassInfo =
findState.subscriberInfo.getSuperSubscriberInfo();
//如果找到,则直接返回
if (findState.clazz == superclassInfo.getSubscriberClass()) {
return superclassInfo;
}
}
//SubscriberInfoIndex 是一个接口,我们使用apt生成的类,就是继承自它
if (subscriberInfoIndexes != null) {
for (SubscriberInfoIndex index : subscriberInfoIndexes) {
SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
//如果从apt生成的索引中找到,则直接返回
if (info != null) {
return info;
}
}
}
return null;
}
subscribe(subscriber, subscriberMethod)
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
//得到@Subscribe的方法参数
Class<?> eventType = subscriberMethod.eventType;
//这里构造出一个Subscription对象,该对象是对订阅的类和类里面的@Subscribe方法的一个封装,将这两个封装到了一个类里面
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
//subscriptionsByEventType是一个以@Subscribe方法参数作为key,订阅方法集合作为value的Map
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
//检查是否已经注册过,重复注册抛出异常
if (subscriptions == null) {
//这里使用的是一个线程安全的读写队列
subscriptions = new CopyOnWriteArrayList<>();
//如果没有注册过,则将改eventType的所有方法,统计到一个集合中
//这里的eventType是@Subscribe的参数,所以可以理解为将所有注册了改类型的放在一个集合中,
// 在unRegister的时候,会从集合中移除,所以可以实现通知到所有已经注册的方法的功能
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;
}
}
//其实和subscriptionsByEventType类型的一个集合,
// 发现只是用来协助isRegistered方法判断是否已经注册过
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).
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);
}
}
}
下面简单总结下EventBus#register(Object subscriber)
的工作流程
使用subscriberMethodFinder.findSubscriberMethods
,以注册参数subscriber
作为参数查询是该类里面的所有使用了@Subscribe注解
的方法。
查找逻辑如下:
如果在METHOD_CACHE
中找到,则直接返回。找不到的话,则判断是否使用apt
方式在编译期生成,如果没有使用apt
方式,则使用反射方式
查找。
在查找到之后,则保存在METHOD_CACHE
中,并返回后续使用。
之后对于查找到的@Subscribe
注解的方法集合进行遍历,并以方法的参数作为key,所有使用了该方法参数的方法集合作为value,保存在subscriptionsByEventType
中。
进行了一顿操作,其实核心思想就是对当前注册的类里面的所有的
@Subscibe
注解的方法,以事件对象
(也就是参数)作为key,对代码中所有的找到的类里面使用该事件对象的@Subscibe
注解方法进行一个统计到一个集合中,方便后续post
的时候进行事件的分发。其实就是一个非常典型的订阅-消费
模式,也可以说是观察者模式
。
unregister(Object subscriber)
public synchronized void unregister(Object subscriber) {
//上面说过,typesBySubscriber其实只是协助判断是否注册过的
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
//如果是已经注册过的
if (subscribedTypes != null) {
//在register的时候,会以类为key,类中的所有@Subscribe注解方法参数(事件对香港)作为value
for (Class<?> eventType : subscribedTypes) {
//遍历集合,从subscriptionsByEventType中移除保存的数据
unsubscribeByEventType(subscriber, eventType);
}
//从map中,将该类对象数据key、value移除
typesBySubscriber.remove(subscriber);
} else {
logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
unsubscribeByEventType(Object subscriber, Class> eventType)
register
方法中的分析,相信这个很容易就看懂了,就不多说明了private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
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;
subscriptions.remove(i);
i--;
size--;
}
}
}
}
下面对于Event#unregister(Object subscriber)
方法做一下简单的总结:
这里用到两个map集合typesBySubscriber
和subscriptionsByEventType
typesBySubscriber
集合实际上就是保存了subscriber
下面所有的@Subscribe注解的事件
集合,其中key是使用类,value就是事件集合。subscriptionsByEventType
是保存的所有的事件对
象的方法集合,事件和方法是一对多关系
。unRegister
操作就是将register
方法中找到的类和事件集合关系
、事件和事件对象方法关系
,进行一个清理,防止对象引用导致内存泄漏
。
因为篇幅原因,post
、粘性消息等内容,将拆分到另一篇文章中