整体流程阅读
EventBus在使用的时候基本分为以下几步:
1、注册订阅者
EventBus.getDefault().register(this);
2、订阅者解注册,否者会导致内存泄漏
EventBus.getDefault().unregister(this);
3、在订阅者中编写注解为Subscribe的事件处理函数
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true, priority = 1)
public void onMsgEventReceived(MsgEvent event) {Toast.makeText(this, event.getMsg(), Toast.LENGTH_LONG).show();
}
4、事件发送
EventBus.getDefault().post("msg1 - coming!!!");
我们先按使用的流程大体看下源码逻辑,源码版本3.3.1:
注册源码逻辑
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;}
EventBus 使用了双重校验锁的单例设计模式,保证用到的对象是唯一的,首次使用对象为空的时候通过下面构造创建一个。
public EventBus() {this(DEFAULT_BUILDER);}
DEFAULT_BUILDER是一个final
常量,在加载的时候就进行初始化,赋一个EventBusBuilder
对象如下面代码所示。
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
EventBusBuilder
是EventBus
的建造类,里面参数在加载的时候进行了初始化。
public class EventBusBuilder {private final static ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool();boolean logSubscriberExceptions = true;boolean logNoSubscriberMessages = true;boolean sendSubscriberExceptionEvent = true;boolean sendNoSubscriberEvent = true;boolean throwSubscriberException;boolean eventInheritance = true;boolean ignoreGeneratedIndex;boolean strictMethodVerification;ExecutorService executorService = DEFAULT_EXECUTOR_SERVICE;List<Class<?>> skipMethodVerificationForClasses;List<SubscriberInfoIndex> subscriberInfoIndexes;Logger logger;MainThreadSupport mainThreadSupport;...
}
如果有需要的话,我们也可以通过配置EventBusBuilder
来更改EventBus
的属性,在EventBus
中有一个静态方法直接返回一直新的EventBusBuilder
对象,设置完参数后调用build()
来以新的配置来新建一个EventBus
对象。
#EventBuspublic static EventBusBuilder builder() {return new EventBusBuilder();}#EventBusBuilder/** Builds an EventBus based on the current configuration. */public EventBus build() {return new EventBus(this);}
然后通过下面的调用来设置:
EventBus.builder().eventInheritance(false).logSubscriberExceptions(false).build().register(this);
拿到EventBus
对象以后,我们可以调用其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);}}}
首先获取订阅者类subscriber
,然后通过findSubscriberMethods
方法获取该类中以@Subscribe
注解的函数,由于一个类中可能监听多个事件,因此获取的方法可能是多个,所有的方法赋值到一个List列表中,然后遍历这个列表进行注册。
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);....}
上面就是注册最主要的代码,步骤解析如下:
-
根据注解方法获取监听事件的类型
eventType
,并对订阅者subscriber
和订阅函数subscriberMethod
建立一个订阅关系对象newSubscription
; -
根据
eventType
在subscriptionsByEventType(HashMap)中获取所有该事件类型的订阅关系列表subscriptions
; -
如果订阅关系列表
subscriptions
为空就新建一个,然后以key
为eventType
,value
为newSubscription
添加进去; -
如果订阅关系列表
subscriptions
不为空,判断是否存在newSubscription
,如果存在,说明之前已经注册过,抛出异常; -
如果订阅关系列表
subscriptions
不为空,列表页没有订阅关系newSubscription
,我们遍历添加进去,这里通过订阅函数的priority
来决定存放在列表中的位置,从这里也能看出priority
越大,存放位置越靠前,和上一篇中分析的:值越大,优先级越高,越优先接收到事件。我们可以猜出是通过遍历这个表来进行事件发送的,在表里的位置越靠前,越先收到事件。 -
然后通过订阅者
subscriber
在另一个HashMap
- typesBySubscriber中获取该订阅者订阅的所有事件,因为一个订阅者可以订阅多个不同的事件,因此获取的是个List列表subscribedEvents
; -
首先判断
subscribedEvents
列表是不是空的,如果是空说明以前没有订阅过任何事件,新建一个List,然后以key
为subscriber
,value
为subscribedEvents
添加到typesBySubscriber
; -
然后在新建的
subscribedEvents
中添加我们订阅的事件eventType
。
这里出现了两个HashMap:
subscriptionsByEventType
与typesBySubscriber
,通过上面的解析可以知道:
subscriptionsByEventType
:一个事件可能有多个订阅者,key
是事件,value
是所有订阅该事件的所有的订阅者;
typesBySubscriber
:一个订阅者可能订阅多个事件,key
是订阅者,value
是订阅者订阅的所有事件;
看命名就知道By后面的是key
值,前面的是value
值,一个好的命名就是这样吧。
解注册源码逻辑
在使用EventBus时,注册完后我习惯接着去写解注册的代码,怕后面会忘,因此按照写代码习惯在讲下解注册的源码实现。
/** Unregisters the given subscriber from all event classes. */public synchronized void unregister(Object 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());}}
如果是我们自己写这部分代码,根据前面注册的过程,我们肯定是要把添加到两个HashMap
中的值移除掉。好了,看上面源码,首先在typesBySubscriber
中获取该订阅者订阅的事件列表subscribedTypes
;
如果为空说明该订阅者没有订阅任何事件,无任何操作;如果不为空,遍历所有事件调用unsubscribeByEventType
方法进行解注册,然后在typesBySubscriber
中移除这个订阅者subscriber
。
/** Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber. */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
这个订阅者移除了,那么unsubscribeByEventType
函数就是遍历事件所有的订阅者,然后把解注册的订阅者在subscriptionsByEventType
中给移除掉。上面的逻辑就是干这个事情。不过一边遍历一边移除是有风险的,这个大家要注意,index
需要也跟着进行减少。
总结下:注册和解注册就是往两个HashMap
添加和移除数据的过程。
事件发送post源码逻辑
/** Posts the given event to the event bus. */public void post(Object event) {PostingThreadState postingState = currentPostingThreadState.get();List<Object> eventQueue = postingState.eventQueue;eventQueue.add(event);····}
上面是部分post
代码,第一行先解释这个变量currentPostingThreadState
及内部类PostingThreadState
。
private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {@Overrideprotected PostingThreadState initialValue() {return new PostingThreadState();}};/** For ThreadLocal, much faster to set (and get multiple values). */final static class PostingThreadState {final List<Object> eventQueue = new ArrayList<>();boolean isPosting;boolean isMainThread;Subscription subscription;Object event;boolean canceled;}
currentPostingThreadState
是一个ThreadLocal
,相当于是线程的私有财产,里面维护的变量只属于当前线程,线程间不会共享。维护的变量是一个自定义类PostingThreadState
,用来保存发送线程的发送状态信息:当前线程是否为主线程,是否在发送事件,发送的事件列表、接收事件的订阅者等。
/** Posts the given event to the event bus. */public void post(Object event) {PostingThreadState postingState = currentPostingThreadState.get();List<Object> eventQueue = postingState.eventQueue;eventQueue.add(event);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;}}}
现在继续看post
逻辑。首先,获取到当前线程的发送状态postingState
,然后拿到事件列表,并把需要post
的事件加入到列表中。判断是否启动了事件发送流程,如果已经启动了,不在做处理,加入列表中的事件会轮到处理。如果没有启动就启动处理流程,并将isPosting
赋值为true
。循环从事件列表中获取事件,通过postSingleEvent
进行处理。
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {Class<?> eventClass = event.getClass();boolean subscriptionFound = false;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));}}}
在postSingleEvent
中,有一个变量的判断eventInheritance
,这个变量表示是否查找发送事件的父类或接口的订阅者,默认是true
。例如,发送事件MsgEvent
这个对象,MsgEvent
继承至Event
这个抽象类,发送后,不光所有订阅MsgEvent
的订阅者可以收到事件,所有订阅Event
的这个事件的订阅者也会收到事件。前面有分析这个字段的值是可以重新配置的,代码如下。
EventBus.builder().eventInheritance(false) //发送的时候不考虑事件父类.logSubscriberExceptions(false).build().register(this);
eventInheritance
为true
,通过lookupAllEventTypes
,向上找到所有父类事件类,然后遍历找到所有事件的订阅者,并发送事件,如果为false
,直接将当前事件发送给订阅者。
postSingleEvent
函数主要是找到需要post
的所有相关事件,然后进一步调用postSingleEventForEventType
发送给订阅者,因此函数后面的逻辑是判断postSingleEventForEventType
的返回值subscriptionFound
,即判断当前事件有没有订阅者进行处理,如果没有处理的,会发送一个NoSubscriberEvent
。例如:如果我在上一节的例子中post
一个新的对象student
,但是没有订阅者和接收函数,如果监听了NoSubscriberEvent
,会收到一个NoSubscriberEvent
的事件,告知调用者你post
的对象没有订阅者。
@Subscribe()
public void onMsgEventReceived(NoSubscriberEvent event) {Log.i(TAG, "NoSubscriberEvent : " + event);
}Student student = new Student(1,"jane");
EventBus.getDefault().post(student);
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {CopyOnWriteArrayList<Subscription> subscriptions;synchronized (this) {subscriptions = subscriptionsByEventType.get(eventClass);}if (subscriptions != null && !subscriptions.isEmpty()) {for (Subscription subscription : subscriptions) {postingState.event = event;postingState.subscription = subscription;boolean aborted;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
这个函数里面,首先在subscriptionsByEventType
中找到所有订阅该事件的订阅者subscriptions
,subscriptions
为空或者个数是0则返回false
。有订阅者的话,遍历订阅者,然后通过postToSubscription
进行发送,并返回true
。
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 subscriberinvokeSubscriber(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);}}
这段代码比较简单就是根据订阅者中注解@Subscribe
中threadMode
值来分别进行处理,上一节也讲过,POSTING
表示在哪个线程发送就在哪个线程接收处理,因此直接调用invokeSubscriber
通过反射来调用订阅者中的接收事件的方法。
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);}}
如果threadMode
是MAIN
,而当前线程是子线程,通过 mainThreadPoster.enqueue(subscription, event)
将事件加入主线程队列。
mainThreadSupport = builder.getMainThreadSupport();
mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;public interface MainThreadSupport {boolean isMainThread();Poster createPoster(EventBus eventBus);
}public class DefaultAndroidMainThreadSupport implements MainThreadSupport {public boolean isMainThread() {return Looper.getMainLooper() == Looper.myLooper();}@Overridepublic Poster createPoster(EventBus eventBus) {return new HandlerPoster(eventBus, Looper.getMainLooper(), 10);}
}
mainThreadPoster
是由mainThreadSupport
创建的,mainThreadSupport
是MainThreadSupport
实例,而MainThreadSupport
是一个接口,实现类为DefaultAndroidMainThreadSupport
,因此,最终是调用到的DefaultAndroidMainThreadSupport
中的createPoster
,新建了一个HandlerPoster
。
public class HandlerPoster extends Handler implements Poster {private final PendingPostQueue queue;private final int maxMillisInsideHandleMessage;private final EventBus eventBus;private boolean handlerActive;public HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) {super(looper);this.eventBus = eventBus;this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage;queue = new PendingPostQueue();}public void enqueue(Subscription subscription, Object event) {PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);synchronized (this) {queue.enqueue(pendingPost);if (!handlerActive) {handlerActive = true;if (!sendMessage(obtainMessage())) {throw new EventBusException("Could not send handler message");}}}}@Overridepublic void handleMessage(Message msg) {...}
}
从上面的源码可以看到,HandlerPoster
就是一个Handler
,当执行mainThreadPoster.enqueue(subscription, event)
时,会将订阅者和事件封装成一个PendingPost
,然后加入到PendingPostQueue
这个队列中,如果handlerActive
为true
表示当前Handler
正常处理事件,将入队列的事件等着被处理即可。如果为false
则启动处理,调用sendMessage
发送消息。
public class HandlerPoster extends Handler implements Poster {private final PendingPostQueue queue;private final int maxMillisInsideHandleMessage;private final EventBus eventBus;private boolean handlerActive;public HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) {super(looper);...}public void enqueue(Subscription subscription, Object event) {...}@Overridepublic void handleMessage(Message msg) {boolean rescheduled = false;try {long started = SystemClock.uptimeMillis();while (true) {PendingPost pendingPost = queue.poll();if (pendingPost == null) {synchronized (this) {// Check again, this time in synchronizedpendingPost = queue.poll();if (pendingPost == null) {handlerActive = false;return;}}}eventBus.invokeSubscriber(pendingPost);long timeInMethod = SystemClock.uptimeMillis() - started;if (timeInMethod >= maxMillisInsideHandleMessage) {if (!sendMessage(obtainMessage())) {throw new EventBusException("Could not send handler message");}rescheduled = true;return;}}} finally {handlerActive = rescheduled;}}
}
在handleMessage()
方法将中将PendingPost
对象循环出队列,交给invokeSubscriber()
方法进一步处理。这样就把线程通过Handler
切回了主线程。
backgroundPoster.enqueue()
和asyncPoster.enqueue
也类似,内部都是先将事件入队列,然后再出队列,但是会通过线程池去进一步处理事件。
粘性事件发送postSticky源码逻辑
public void postSticky(Object event) {synchronized (stickyEvents) {stickyEvents.put(event.getClass(), event);}// Should be posted after it is putted, in case the subscriber wants to remove immediatelypost(event);}
从上面代码可以看到,先把事件放在了stickyEvents
列中中,然后调用了post
,也就是上面我们解析过的流程。让我们再来回顾下注册的代码:
// Must be called in synchronized blockprivate void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {Class<?> eventType = subscriberMethod.eventType;Subscription newSubscription = new Subscription(subscriber, subscriberMethod);CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);...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);}}}
注册源码分析的时候,就分析到subscriberMethod.sticky
这个句上面,现在让我们看看下面的逻辑,同样先判断eventInheritance
的值,然后将之前放在stickyEvents
中的事件拿出来,执行checkPostStickyEventToSubscription
。
private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {if (stickyEvent != null) {// If the subscriber is trying to abort the event, it will fail (event is not tracked in posting state)// --> Strange corner case, which we don't take care of here.postToSubscription(newSubscription, stickyEvent, isMainThread());}}
checkPostStickyEventToSubscription
中的代码比较水,又调用了postToSubscription
,这个方法上面贴出来了,这里方面看再贴一遍,熟悉的味道:
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 subscriberinvokeSubscriber(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);}}
所以,在注册的时候,如果你订阅者的接收方法加了sticky
注解,那么在注册的时候就会看下订阅的事件之前有没有通过postSticky
发送过,如果有就会立马收到这个事件。
以上是我们平时使用过程的源码解析,码字不易,喜欢就点赞收藏啊。
参考文章:
EventBus 原理解析