整体流程阅读
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
对象。
#EventBus
public 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>() {
@Override
protected 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 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);
}
}
这段代码比较简单就是根据订阅者中注解@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();
}
@Override
public 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");
}
}
}
}
@Override
public 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) {
...
}
@Override
public 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 synchronized
pendingPost = 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 immediately
post(event);
}
从上面代码可以看到,先把事件放在了stickyEvents
列中中,然后调用了post
,也就是上面我们解析过的流程。让我们再来回顾下注册的代码:
// Must be called in synchronized block
private 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 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);
}
}
所以,在注册的时候,如果你订阅者的接收方法加了sticky
注解,那么在注册的时候就会看下订阅的事件之前有没有通过postSticky
发送过,如果有就会立马收到这个事件。
以上是我们平时使用过程的源码解析,码字不易,喜欢就点赞收藏啊。
学有余力的朋友可以再看下一篇,源码细节阅读EventBus 开源库学习(三)文章来源:https://www.toymoban.com/news/detail-629048.html
参考文章:
EventBus 原理解析文章来源地址https://www.toymoban.com/news/detail-629048.html
到了这里,关于EventBus 开源库学习(二)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!