目录
一、添加gradle依赖
二、基础操作
1、EventBus.getDefault();
2、EventBus.getDefault().register(this);
3、EventBus.getDefault().post(object);
4、EventBus.getDefault().unregister();
二、四个onEvent方法
三、EventBus3.0
四、原理简述
EventBus是一款在 Android 开发中使用的发布、订阅事件总线框架,基于观察者模式,将事件的接收者和发送者分开,可以在Activity、Fragment、Service等组件交互通信,使用简单、效率高、体积小。
可以替代Intent、Broadcast、Thread+Handle异步线程间传递消息。
githup地址:https://github.com/greenrobot/EventBus
implementation 'org.greenrobot:eventbus:3.1.1'
getDefault是一个单例方法,保证返回唯一的EventBus实例对象。
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
public void register(Object subscriber) {
// 反射获取当前注册类的Class对象
Class> subscriberClass = subscriber.getClass();
// 1、查找:根据Class查找当前类中订阅了事件的方法集合
List subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
// 2、注册:循环遍历订阅了事件的方法集合,以完成注册
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
主要分为查找和注册两部分,首先来看查找的过程,从
findSubscriberMethods()
开始:
List findSubscriberMethods(Class> subscriberClass) {
// METHOD_CACHE是一个ConcurrentHashMap,直接保存了subscriberClass和对应SubscriberMethod的集合,以提高注册效率,赋值重复查找。
List subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
// 由于使用了默认的EventBusBuilder,则ignoreGeneratedIndex属性默认为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;
}
}
先从缓存中查找,如果找到则直接返回,否则去做下一步的查找过程,然后缓存查找到的集合,根据上边的注释可知
findUsingInfo()
方法会被调用:
private List findUsingInfo(Class> subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
// 初始状态下findState.clazz就是subscriberClass
while (findState.clazz != 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 {
// 通过反射查找订阅事件的方法
findUsingReflectionInSingleClass(findState);
}
// 修改findState.clazz为subscriberClass的父类Class,即需要遍历父类
findState.moveToSuperclass();
}
// 查找到的方法保存在了FindState实例的subscriberMethods集合中。
// 使用subscriberMethods构建一个新的List
// 释放掉findState
return getMethodsAndRelease(findState);
}
findUsingInfo()
方法会在当前要注册的类以及其父类中查找订阅事件的方法,这里出现了一个FindState
类,它是SubscriberMethodFinder
的内部类,用来辅助查找订阅事件的方法,具体的查找过程在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
methods = findState.clazz.getMethods();
findState.skipSuperClasses = true;
}
// 循环遍历当前类的方法,筛选出符合条件的
for (Method method : methods) {
// 获得方法的修饰符
int modifiers = method.getModifiers();
// 如果是public类型,但非abstract、static等
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
// 获得当前方法所有参数的类型
Class>[] parameterTypes = method.getParameterTypes();
// 如果当前方法只有一个参数
if (parameterTypes.length == 1) {
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
// 如果当前方法使用了Subscribe注解
if (subscribeAnnotation != null) {
// 得到该参数的类型
Class> eventType = parameterTypes[0];
// checkAdd()方法用来判断FindState的anyMethodByEventType map是否已经添加过以当前eventType为key的键值对,没添加过则返回true
if (findState.checkAdd(method, eventType)) {
// 得到Subscribe注解的threadMode属性值,即线程模式
ThreadMode threadMode = subscribeAnnotation.threadMode();
// 创建一个SubscriberMethod对象,并添加到subscriberMethods集合
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");
}
}
}
到此
register()
方法中findSubscriberMethods()
流程就分析完了,我们已经找到了当前注册类及其父类中订阅事件的方法的集合。接下来分析具体的注册流程,即register()
中的subscribe()
方法:
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
// 得到当前订阅了事件的方法的参数类型
Class> eventType = subscriberMethod.eventType;
// Subscription类保存了要注册的类对象以及当前的subscriberMethod
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
// subscriptionsByEventType是一个HashMap,保存了以eventType为key,Subscription对象集合为value的键值对
// 先查找subscriptionsByEventType是否存在以当前eventType为key的值
CopyOnWriteArrayList subscriptions = subscriptionsByEventType.get(eventType);
// 如果不存在,则创建一个subscriptions,并保存到subscriptionsByEventType
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);
}
}
// 添加上边创建的newSubscription对象到subscriptions中
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;
}
}
// typesBySubscribere也是一个HashMap,保存了以当前要注册类的对象为key,注册类中订阅事件的方法的参数类型的集合为value的键值对
// 查找是否存在对应的参数类型集合
List> subscribedEvents = typesBySubscriber.get(subscriber);
// 不存在则创建一个subscribedEvents,并保存到typesBySubscriber
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, 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()
方法主要是得到了subscriptionsByEventType
、typesBySubscriber
两个 HashMap。我们在发送事件的时候要用到subscriptionsByEventType
,完成事件的处理。当取消 EventBus 注册的时候要用到typesBySubscriber
、subscriptionsByEventType
,完成相关资源的释放。
public void post(Object event) {
//事件队列和线程模式等信息
PostingThreadState postingState = currentPostingThreadState.get();
List
post()
方法先将发送的事件保存的事件队列,然后通过循环出队列,将事件交给postSingleEvent()
方法处理:
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class> eventClass = event.getClass();
boolean subscriptionFound = false;
// eventInheritance默认为true,表示是否向上查找事件的父类
if (eventInheritance) {
// 查找当前事件类型的Class,连同当前事件类型的Class保存到集合
List> eventTypes = lookupAllEventTypes(eventClass);
int countTypes = eventTypes.size();
// 遍历Class集合,继续处理事件
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));
}
}
}
postSingleEvent()
方法中,根据eventInheritance
属性,决定是否向上遍历事件的父类型,然后用postSingleEventForEventType()
方法进一步处理事件:
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class> eventClass) {
CopyOnWriteArrayList subscriptions;
synchronized (this) {
// 获取事件类型对应的Subscription集合
subscriptions = subscriptionsByEventType.get(eventClass);
}
// 如果已订阅了对应类型的事件
if (subscriptions != null && !subscriptions.isEmpty()) {
for (Subscription subscription : subscriptions) {
// 记录事件
postingState.event = event;
// 记录对应的subscription
postingState.subscription = subscription;
boolean aborted = false;
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;
}
postSingleEventForEventType()
方法核心就是遍历发送的事件类型对应的Subscription集合,然后调用postToSubscription()
方法处理事件。
public synchronized void unregister(Object subscriber) {
// 得到当前注册类对象 对应的 订阅事件方法的参数类型 的集合
List> subscribedTypes = typesBySubscriber.get(subscriber);
if (subscribedTypes != null) {
// 遍历参数类型集合,释放之前缓存的当前类中的Subscription
for (Class> eventType : subscribedTypes) {
unsubscribeByEventType(subscriber, eventType);
}
// 删除以subscriber为key的键值对
typesBySubscriber.remove(subscriber);
} else {
logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
private void unsubscribeByEventType(Object subscriber, Class> eventType) {
// 得到当前参数类型对应的Subscription集合
List subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions != null) {
int size = subscriptions.size();
// 遍历Subscription集合
for (int i = 0; i < size; i++) {
Subscription subscription = subscriptions.get(i);
// 如果当前subscription对象对应的注册类对象 和 要取消注册的注册类对象相同,则删除当前subscription对象
if (subscription.subscriber == subscriber) {
subscription.active = false;
subscriptions.remove(i);
i--;
size--;
}
}
}
}
在
unregister()
方法中,释放了typesBySubscriber
、subscriptionsByEventType
中缓存的资源。
代表这个方法会在UI线程执行
代表这个方法会在当前线程执行
代表非UI线程会在同线程执行,是的话在后台执行,一个一个调用。
代表加入后台任务,使用线程池调用,不是一个一个调用。
开始使用
Subscribe
注解配置事件订阅方法,可以指定threadMode
、sticky
、priority
三个属性。源码如下
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
// 指定事件订阅方法的线程模式,即在那个线程执行事件订阅方法处理事件,默认为POSTING
ThreadMode threadMode() default ThreadMode.POSTING;
// 是否支持粘性事件,默认为false
boolean sticky() default false;
// 指定事件订阅方法的优先级,默认为0,如果多个事件订阅方法可以接收相同事件的,则优先级高的先接收到事件
int priority() default 0;
}
其中,threadMode属性有如下几个可选值:
默认的线程模式,在那个线程发送事件就在对应线程处理事件,避免了线程切换,效率高。
如在主线程(UI线程)发送事件,则直接在主线程处理事件;如果在子线程发送事件,则先将事件入队列,然后通过 Handler 切换到主线程,依次处理事件。
无论在那个线程发送事件,都先将事件入队列,然后通过 Handler 切换到主线程,依次处理事件。
如果在主线程发送事件,则先将事件入队列,然后通过线程池依次处理事件;如果在子线程发送事件,则直接在发送事件的线程处理事件。
无论在那个线程发送事件,都将事件入队列,然后通过线程池处理。
EventBus内部封装4个onEvent方法,然后post发送事件的时候,根据post参数类型自动匹配执行的方法。register(this)订阅在当前类,通过反射调用遍历当前类所有的方法,找到onEvent开发的方法,然后进行Map存储,key为class类型,值为对象实例。