EventBus源码理解
简介:本博客是按照调用的思路,来对源码的一些方法和属性进行解释的。
首先我们看一下 EventBus对于事件的处理。
EventBus.getDefault()
/** Convenience singleton for apps using a process-wide EventBus instance. */
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
就是获得EventBus的实例,从这个方法开始看,那么可以看出这个方法是一个双重锁的单例,
双重锁的这个单例是从jdk1.5之后开始实行的
一、注册
EventBus.register(Object subscriber)
public void register(Object subscriber) {
//subscriber就是注册的类,给谁注册就把谁传过来
Class<?> subscriberClass = subscriber.getClass();
//从订阅类获取中获取所有的订阅方法信息
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
就是注册,注册的时候需要一个类(也就是我们要对一个类进行注册)
拿到注册的类,之后在看注册方法,会通过 SubscriberMethodFinder(用户查找器方法)里的
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass)
(找到用户的方法)
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
//不是null的话就说明找到了,是null的话,我们就继续往下走
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;
}
}
List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass)
之后我们看看这个,从注解器中获取订阅方法信息的方法
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
//从FIND_STATE_POOL数组中查找FindState,找到返回找不到就new
FindState findState = prepareFindState();
//之后我们对传进来的这个类进行初始化
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
findState.subscriberInfo = getSubscriberInfo(findState);
//findState.subscriberInfo 默认为null
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);
}
void findUsingReflectionInSingleClass(FindState findState)
这个方法呢就是 核心,我们看看,他是怎么实现的。
private void findUsingReflectionInSingleClass(FindState findState) {
Method[] methods;
try {
// This is faster than getMethods, especially when subscribers are fat classes like Activities
//先获取所有,公有的方法,也就是说我们,所有带public修饰的方法,也是我们订阅方法具备的修饰符
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来修饰的)
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];
//校验是否添加该方法
if (findState.checkAdd(method, eventType)) {
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");
}
}
}
可以看出,首先会得到订阅类的Class对象,并且通过反射获取订阅类中的所有方法信息,然后通过筛选获取到订阅方法的集合,最后我们获取到了所有订阅方法的信息。
帅选完呢就会返回到我们的List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass)这个方法,所以我们继续往下看。
会通过这个方法,找到这个类所有带@Subscribe()(订阅方法)注解的方法
最后会put到我们的这个集合里。
Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>();
以类为Key以集合为Value的这么一个Map集合,也就是说,Map的每一个键值队,可以理解为这个类(key)
所有带有注解或订阅的方法(Value)
在我们进行对这个集合处理的时候呢,在回到我们的注册方法register()
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
注册方法里他遍历了我们的通过List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass)这个方法返回的list集合。之后呢我们在看一下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);
}
}
}
我们可以可以看一下带有注解的@Subscribe()的 SubscriberMethod 类有
final Method method; //带注解的方法对象
final ThreadMode threadMode;//线程模式
final Class<?> eventType;//就是我们订阅观察的对象
final int priority;//优先级
final boolean sticky;//是否粘性
/** Used for efficient comparison 用于高效的比较 */
String methodString;//方法名称
最后我们,我们订阅完成了,我们在说一下粘性事件。
粘性事件
何为黏性事件呢?简单讲,就是在发送事件之后再订阅该事件也能收到该事件。Android中就有这样的实例,也就是Sticky Broadcast,即粘性广播。正常情况下如果发送者发送了某个广播,而接收者在这个广播发送后才注册自己的Receiver,这时接收者便无法接收到 刚才的广播,为此Android引入了StickyBroadcast,在广播发送结束后会保存刚刚发送的广播(Intent),这样当接收者注册完 Receiver后就可以接收到刚才已经发布的广播。这就使得我们可以预先处理一些事件,让有消费者时再把这些事件投递给消费者.
EventBus也提供了这样的功能,有所不同是EventBus会存储所有的Sticky事件,如果某个事件在不需要再存储则需要手动进行移除。用户通过Sticky的形式发布事件,而消费者也需要通过Sticky的形式进行注册,当然这种注册除了可以接收 Sticky事件之外和常规的注册功能是一样的,其他类型的事件也会被正常处理。
使用场景
我们要把一个Event发送到一个还没有初始化的Activity/Fragment,即尚未订阅事件。那么如果只是简单的post一个事件,那么是无法收到的,这时候,你需要用到粘性事件,它可以帮你解决这类问题.
二、取消注册
我们注册完了之后,就说明,任何对象用eventbus发送消息的时候,我们的类都会接收到此消息,那么,我们使用完了一定要进行解除注册,不然就会造成,内存泄漏。导致无用的资源,一直被持有被服务着。
解除注册的方法
synchronized void unregister(Object subscriber)
我们先进入解除注册的这个方法,看看。
public synchronized void unregister(Object subscriber) {
//通过 subscriber 获取该订阅者的所有事件类型
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
if (subscribedTypes != null) {
for (Class<?> eventType : subscribedTypes) {
unsubscribeByEventType(subscriber, eventType);
}
//最后移除
typesBySubscriber.remove(subscriber);
} else {
logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
通过传进来的类,在通过这个类,拿到所有的事件类型,一个一个取消注册
取消注册的方法
unsubscribeByEventType(Object subscriber, Class<?> eventType)
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--;
}
}
}
}
这样就解除注册了。
有很多朋友问,源码太复杂,有很多朋友问,我直接使用typesBySubscriber直接把集合移除不就好了么,这里我想说一句题外话,如果这样的话会导致内存溢出的,导致内存溢出,其中一个原因就是,当一个集合,中的对象都被引用这,那么集合的引用虽然断了,但是集合中的对象,一直被引用着,导致永远也无法被gc回收,所以,造成了系统资源的泄漏(ps:源码大家都使用,在写工具类时一定要注意,1.内存的优化,防止内存泄漏,2.类复用,防止相同的对象重复创建,3.提高代码的安全性,介意大家也经常读一读源码,这样会提高大家的编码档次吧)
三、发送
void post(Object event)
EventBus的发送方法post(Object event)
public void post(Object event) {
//获取当前发送的状态
PostingThreadState postingState = currentPostingThreadState.get();
//获取当前的事件队列
List<Object> eventQueue = postingState.eventQueue;
eventQueue.add(event);
//如果这个postingState正在发送事件,那么就等待
if (!postingState.isPosting) {
postingState.isMainThread = isMainThread();
postingState.isPosting = true;
if (postingState.canceled) {
throw new EventBusException("Internal error. Abort state was not reset");
}
try {
//如果事件队列不为空
while (!eventQueue.isEmpty()) {
//发送事件
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
走到while循环这里,他发送的事件的方法我们看一看他是如何发送事件的。
void postSingleEvent(Object event, PostingThreadState postingState)
发送事件的方法
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
//首先得到事件类型
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
//是否出发订阅了该事件(eventclass)的父类,以及接口的类的响应方法
if (eventInheritance) {
List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
int countTypes = eventTypes.size();
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));
}
}
}
我们拿到了事件类型之后 我们看一看他是如何发的。
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
//根据事件类型获取所有的订阅者,根据类型存放到这个集合里,那么我们就可以根据eventClass拿到所有的订阅者。
subscriptions = subscriptionsByEventType.get(eventClass);
}
//像每个订阅者发布事件
if (subscriptions != null && !subscriptions.isEmpty()) {
//循环发送
for (Subscription subscription : subscriptions) {
postingState.event = event;
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;
}
此时我们拿到了,所有订阅者,然后,我们就可以通过,不同方式,发送给订阅者。
在通过我们的threadMode来发出去。
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 MAIN_ORDERED:
if (mainThreadPoster != null) {
mainThreadPoster.enqueue(subscription, event);
} else {
// temporary: technically not correct as poster not decoupled from subscriber
invokeSubscriber(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);
}
}
我们在往下看就是反射的invoke()方法,通过反射来调用的我们的方法,把消息发送过去。
在说一下threadMode有哪些
- POSTING:默认,表示事件处理函数的线程跟发布事件的线程在同一个线程。
- MAIN:表示事件处理函数的线程在主线程(UI)线程,因此在这里不能进行耗时操作。
- BACKGROUND:表示事件处理函数的线程在后台线程,因此不能进行UI操作。如果发布事件的线程是主线程(UI线程),那么事件处理函数将会开启一个后台线程,如果果发布事件的线程是在后台线程,那么事件处理函数就使用该线程。
- ASYNC:表示无论事件发布的线程是哪一个,事件处理函数始终会新建一个子线程运行,同样不能进行UI操作。
总结:EventBus可以看出来,主要是通过注解模式,和反射获取,类中的方法以及方法的属性,注册好了之后,然后发送以反射的invoke()方法,来调用,将参数传到订阅者的形参上
最后
如果有大佬觉得,哪里有不对的地方,欢迎您留言评论——————————————