EventBus 3.0 source code analysis
Introduction
EvenntBus is an open source library for event distribution in Android development. The core of its work is the publish/subscriber pattern, which can realize the communication between multiple components with very little code. For communication between android components, we can't help but think of the handler message mechanism and the broadcast mechanism, through which we can also communicate, but using them for communication requires a lot of code, and it is easy to generate coupled references between components. Regarding the working mode of EventBus, here is an official diagram to help understand.
Why choose to use EventBus for communication?
- Simplifies the way components communicate
- Decoupling both parties of event communication
- Worker threads can be specified flexibly and conveniently, through ThreadMode
- fast speed and good performance
- The library is relatively small, about 60k, which has no effect on the package size
- There are many apps using this library and it is authoritative
- Multiple functions, easy to use
The use of EventBus is also very simple, with three important roles
1:Publisher event publisher
2:Subscriber event subscriber
3:Event event
After the Publisher posts the event, the Subscriber will automatically receive the event (the subscription method will be actively called and the event will be delivered).
use
//倒入gradle 依赖
implementation 'org.greenrobot:eventbus:3.3.1'
1: Define the event type
public static class MessageEvent { /* Additional fields if needed */ }
2: Register EventBus in the module that needs to subscribe to events, and pay attention to logout when the page is destroyed
@Override
protected void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
3: Register the event type that needs to be accepted // Note that the same event type cannot be registered repeatedly. Otherwise it will crash, and the subscription method must be of public type.
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {
// Do something
}
4. Send events
EventBus.getDefault().post(new MessageEvent());
这是步骤3种的方法就会收到MessageEvent事件的回调
Source code analysis
There are less than 600 lines of code in the main EventBus class, which is very streamlined. EventBus uses the singleton model provided externally, and the internal construction uses the Build mode.
1 register
public void register(Object subscriber) {
if (AndroidDependenciesDetector.isAndroidSDKAvailable() && !AndroidDependenciesDetector.areAndroidComponentsAvailable()) {
// Crash if the user (developer) has not imported the Android compatibility library.
throw new RuntimeException("It looks like you are using EventBus on Android, " +
"make sure to add the \"eventbus\" Android library to your dependencies.");
}
//1从这里开始看,获取调用者的类对象。
Class<?> subscriberClass = subscriber.getClass();
//2 找到订阅类中的订阅方法
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
//3遍历订阅方法,将订阅者和其中的订阅方法绑定。
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
Step1
Look at the findSubscriberMethods method of subscriberMethodFinder in step 2
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
//1首先从缓存map中拿到订阅类的订阅方法列表,使用了缓存提高性能,nice,不出所料METHOD_CACHE的类型是Map<Class<?>, List<SubscriberMethod> >
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
//2 如果不为空,说明之前该类曾经注册过,该类的新对象不必重新做绑定了,因为此时的操作是类层面的
if (subscriberMethods != null) {
return subscriberMethods;
}
//如果subscriberMethods 为null,说明该类是第一次注册,需要将其中的接收方法保存起来,
//ignoreGeneratedIndex 默认为false
if (ignoreGeneratedIndex) {
subscriberMethods = findUsingReflection(subscriberClass);
} else {
subscriberMethods = findUsingInfo(subscriberClass);
}
//如果subscriberMethods为null,说明当前类对象没有生命订阅方法,抛出异常
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;
}
}
Step2
Find out the registered method list of the class from step 2, then traverse the list, call the following method, and bind the class object to the registered method.
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
//1 找到订阅方法的事件类型,即发送事件的MessageEvent.class
Class<?> eventType = subscriberMethod.eventType;
//2 将订阅者类对象和订阅事件绑定成一个对象
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
//subscriptionsByEventType 这个集合肯定是用来放置同一事件类型的订阅集合的,因为一个事件可能会有多个订阅的。
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
//如果一个订阅者多次订阅了一个事件(@Subscribe注解的方法的参数是同一类型),抛出异常
if (subscriptions.contains(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
}
}
//3 按照订阅方法中@Subscribe中的priority参数进行排序,默认为最低优先级0。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;
}
}
// typesBySubscriber类型Map<Object, List<Class<?>>>,Key 为订阅者,value为订阅者中的订阅方法,用来记录每个订阅者内部都订阅了哪些事件类型
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);
}
}
}
2 post
/** Posts the given event to the event bus. */
public void post(Object event) {
//1首先获取当前线程的工作状态
PostingThreadState postingState = currentPostingThreadState.get();
//2获取当前线程的任务队列
List<Object> eventQueue = postingState.eventQueue;
//3 将事件加入到事件队列
eventQueue.add(event);
//4 如果当前线程的工作状态没有正在发送事件
if (!postingState.isPosting) {
//标记postingState 的是否是主线程,并将工作状态isPosting 设为true
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;
}
}
}
/**
* 发送事件
* @param event 事件
* @param postingState 当前线程相关的配置
*/
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
//如果使用继承事件的父类/接口,比如你发送了MessageEvent 事件,如果该事件继承了BaseEvent和Ievent接口,那么当你发送 MessageEvent 事件时,系统也会发送BaseEvent和Ieven事件
if (eventInheritance) {
//遍历父类,将事件的父类/接口统统加入到eventTypes中
List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
int countTypes = eventTypes.size();
for (int h = 0; h < countTypes; h++) {
Class<?> clazz = eventTypes.get(h);
//遍历eventTypes,依次发送调用事件
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);
}
//EventBus 内部也适用EventBus 发送了一个异常事件
if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
eventClass != SubscriberExceptionEvent.class) {
post(new NoSubscriberEvent(this, event));
}
}
}
postSingleEventForEventType
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
//1获取该事件的所有订阅关系列表
subscriptions = subscriptionsByEventType.get(eventClass);
}
//2 遍历订阅关系列表,依次将事件发送到订阅者
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;
}
postToSubscription
Here is the key point, and finally to the place where the event is distributed.
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
//1 首先判断订阅关系中订阅方法的线程,就是声明线程时使用@Subcribe注解时传入的threadMode字段的值
switch (subscription.subscriberMethod.threadMode) {
case POSTING: //直接发送事件
invokeSubscriber(subscription, event);
break;
case MAIN: //在主线程相应事件
//事件发出线程是否是主线程
if (isMainThread) { 是,直接发送
invokeSubscriber(subscription, event);
} else {不是通过mainThreadPoster发送
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转发
backgroundPoster.enqueue(subscription, event);
} else { 非主线程发送事件,直接发送
invokeSubscriber(subscription, event);
}
break;
case ASYNC: //异步线程相应事件
//通过asyncPoster发送事件
asyncPoster.enqueue(subscription, event);
break;
default:
throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
}
}
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);
}
}
There are three important roles in postToSubscription mainThreadPoster, backgroundPoster, asyncPoster
The type of mainThreadPoster is HandlerPoster. In fact, it is Handler. call its enqueu() method
Both backgroundPoster and asyncPoster are essentially Runnable
3 unregister
The unbinding method is much simpler, the important thing is to delete the subscriber from the two important geometries mentioned in the register
/** Unregisters the given subscriber from all event classes. */
public synchronized void unregister(Object subscriber) {
//1 从typesBySubscriber找到订阅者所订阅的事件类型列表
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
if (subscribedTypes != null) {
//2 遍历列表,依次解绑订阅者和事件类型。应该是从post分析里的订阅事件集合subscriptionsByEventType里移除对应事件类型的该订阅者
for (Class<?> eventType : subscribedTypes) {
unsubscribeByEventType(subscriber, eventType);
}
//3移除订阅者
typesBySubscriber.remove(subscriber);
} else {
logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
/**
* 解绑订阅者和事件类型
* @param subscriber 订阅者
* @param eventType 订阅的事件类型
*/
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
//从subscriptionsByEventType里获取该订阅事件的订阅者集合。
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);
//重要。不然会抛出ConcurrentModifyException
i--;
size--;
}
}
}
}
4 Subscribe annotations
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
ThreadMode threadMode() default ThreadMode.POSTING;
/**
* If true, delivers the most recent sticky event (posted with
* {@link EventBus#postSticky(Object)}) to this subscriber (if event available).
*/
boolean sticky() default false;
/** Subscriber priority to influence the order of event delivery.
* Within the same delivery thread ({@link ThreadMode}), higher priority subscribers will receive events before
* others with a lower priority. The default priority is 0. Note: the priority does *NOT* affect the order of
* delivery among subscribers with different {@link ThreadMode}s! */
int priority() default 0;
}
5 ThreadMode
//定义事件回调方法工作线程的类
public enum ThreadMode {
/**
* 直接在发送事件的线程里调用Subscriber,这个是默认的设置,事件交付开销最小,
* 因为它避免了线程切换。因此它是那种很快完成的单任务
* 默认的的线程工作模型。使用该模型的事件必须很快完成,因为当发布线程是主线程时
* 它可能阻塞主线程。
/
POSTING,
/**
* 在Android平台,订阅者将会在Android的主线程调用。如果发布线程时主线程,订阅方
* 法将会被直接调用。进而阻赛发布线程,如果发布线
* 程不是主线程。事件将会排队等待分发。使用这种模式的订阅者必须快速完成任务,
* 避免阻赛主线程。
* 非Android平台和Posting一样
*/
MAIN,
/**
* 在Android平台,订阅者将会在Android的主线程调用。不同于MAIN,事件将会有序分
* 发。确保了post调用时非阻赛的。
*/
MAIN_ORDERED,
/**
* 在Android平台,订阅者将会在后台线程被调用,如果发布线程不是主线程,订阅者将会
* 直接调用,如果发布线程时主线程,那么EventBus 使用后台线程,进而有序分发所有
* 事件,使用此模式的Subscribers应该快速完成任务以免阻赛后台线程。非Android平台
* 总是用后台线程相应事件
*/
BACKGROUND,
/**
* 订阅者将会在单独的线程被调用,总是独立于发布线程和主线程。发布事件从不会等待
* 使用这种模式的订阅方法。如果订阅方法执行耗时任务,则应该使用此模式。
* 比如;网络访问。避免同时触发大量的长时间运行的异步订阅方法,从而限制并发的线程
* 数量。EventBus 使用线程池来
* 高效的服用已完成异步订阅通知的线程
*/
ASYNC
}
6 What is the difference between EventBus2.0 and EventBus3.0?
This is one of the most frequently asked questions by interviewers during the interview process.
There are two major differences between EventBus 2.0 and 3.0:
1. In EventBus 2.0, when we write the subscription method, the name must start with onEvent, and then different thread modes are distinguished by different names. For example, the corresponding posting is named onEvent(), and onEventMainThread() is corresponding to main, etc. In 3.0, you can use any name as the method name. You only need to annotate the method name with the @Subscribe annotation, and then use threadMode to set the thread where events are received and processed.
2.EventBus2.0 uses reflection to find all subscription methods, while 3.0 finds all subscription methods by annotation processor at compile time. In terms of performance, 3.0 is much higher than 2.0.
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。