事件总线:为了简化组件之间以及组件和后台线程之间的通信,在保证其高效性的同时降低耦合度。比如Activity
、Fragment
、Thread
和Service
等组件的通信。
EventBus
简介
EventBus
是一款针对Andriod
优化的发布-订阅事件总线。其优点是开销小,代码优雅,以及降低发送者和接收者之间的耦合性。
组件之间的交互也可以用广播来处理,但是使用广播比较麻烦,效率效率不高。如果传递的数据是实体类,需要序列化,传递成本有点高。
EventBus
的三要素
publisher [ˈpʌblɪʃər] 出版者,出版商;发行人 subscriber [səbˈskraɪbər] 订户;签署者;捐献者
Event
:事件,可以是任意类型的对象。Subscriber
:事件订阅者。在EventBus 3.0
之前消息处理的方式只能限定于onEvent
、onEventMainThread
、onEventBackground
和onEventAsync
,它们分别代表4
种线程模型。而在EventBus 3.0
之后,事件处理的方式可以随便起名,但是只需要添加一个注解@Subscribe
,并且要指定线程模型(默认为POSTING
)- **
Publisher
:事件发布者,可以在任意线程的任意位置发送事件。**直接调用EventBus.post(Object)
方法,根据参数类型,会自动调用订阅相应类型事件的函数
@Subscribe
注解
subscribe [səbˈskraɪb] 订阅;捐款;认购;赞成;签署
sticky [ˈstɪki] 粘的;粘性的
priority [praɪˈɔːrəti] n. 优先;优先权;[数] 优先次序;优先考
以下是@Subscribe
的源码:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
// 线程模式,即在哪个线程处理事件,默认为POSITION
ThreadMode threadMode() default ThreadMode.POSTING;
// 是否支持黏性事件,默认为false
boolean sticky() default false;
// 优先级,默认为0,先处理优先级高的事件
int priority() default 0;
}
EventBus
的4
种线程模型(ThreadMode
)
ThreadMode.POSTING
:默认线程模型,事件在哪个线程中发布就在哪个线程中处理。 避免了线程切换,效率高。ThreadMode.MAIN
:不管事件在哪个线程中发布,都会在UI
线程中处理。如果发布线程是主线程,将阻塞post
调用,如果是子线程,事件在队列中排队等待处理,是非阻塞的。Thread.MAIN_ORDERED
:不管事件在哪个线程中发布,都会在UI
线程中处理。不同于ThreadMode.MAIN
,事件始终排队等待处理,这就确保了post
调用是非阻塞的。ThreadMode.BACKGROUND
:如果事件是在UI
线程中发布出来的,那么该事件的处理函数会在新的线程中运行;如果事件是从子线程中发布出来的,那么该事件处理函数直接在发布事件的线程中执行。ThreadMode.ASYNC
:无论事件在哪个线程中发布,该事件处理函数都会在新的子线程中执行。
阻塞:调用结果返回之前,线程会被挂起。非阻塞:在没有得到结果之前,不会阻塞当前线程
EventBus
基本用法
EventBus
使用起来分为5个步骤:
- 自定义一个事件类:
public class MessageEvent {
}
- 在需要订阅事件的地方注册事件,一般在
onStart
方法中注册订阅事件:
@Override
protected void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
- 发送事件:
Event.getDefault().post(messageEvent);
- 处理事件:消息处理的方法可以随便取名,但是需要添加一个注解
@Subscribe
,并且需要执行线程模型(默认为POSTING
)
@Subscribe (threadMode = ThreadMode.MAIN)
public void XXX (MessageEvent messageEvent) {
}
- 取消事件订阅,一般在
onStop
事件中取消订阅事件:
@Override
protected void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}
EventBus
应用举例
1. 添加依赖库,配置gradle
:
implementation 'org.greenrobot:eventbus:3.2.0'
2. 定义消息事件类
public class MessageEvent {
private String message;
public MessageEvent(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
3. 注册和取消订阅事件
在MainActivity
中注册和取消订阅事件:
public class TestActivity extends AppCompatActivity {
private TextView tv_message;
private Button btn_message;
private Button btn_subscribe;
@Override
protected void onCreate(@Nullable @org.jetbrains.annotations.Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv_message = findViewById(R.id.tv_message);
btn_message = findViewById(R.id.btn_message);
btn_subscribe = findViewById(R.id.btn_subscribe);
tv_message.setText("MainActivity");
btn_message.setOnClickListener(v -> startActivity(new Intent(TestActivity.this, SecondActivity.class)));
btn_subscribe.setOnClickListener(v -> EventBus.getDefault().register(TestActivity.this));
}
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
}
4. 事件订阅者处理事件
在TestActivity
中定义方法来处理事件,ThreadMode
设置为MAIN
,事件的处理会在UI
线程中执行,用TextView
来展示收到的事件消息:
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMoonEvent(MessageEvent messageEvent) {
tv_message.setText(messageEvent.getMessage());
}
5. 事件发布者发布事件
创建SecondActivity
来发布消息:
public class SecondActivity extends AppCompatActivity {
private Button btn_message;
private TextView tv_message;
@Override
protected void onCreate(@Nullable @org.jetbrains.annotations.Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
tv_message = findViewById(R.id.tv_message);
btn_message = findViewById(R.id.btn_message);
tv_message.setText("SecondActivity");
btn_message.setOnClickListener(v -> {
EventBus.getDefault().post(new MessageEvent("Welcome to EventBus"));
finish();
});
}
}
点击MainActivity
中的“注册事件”按钮来注册事件,然后点击“跳转到SECONDACTIVITY”按钮,跳转到SecondActivity
中,接下来点击“发送事件”这个按钮。
6. ProGuard
混淆规则
-keepattributes *Annotation*
-keepclassmembers class * {
@org.greenrobot.eventbus.Subscribe <methods>;
}
-keep enum org.greenrobot.eventbus.ThreadMode { *; }
# And if you use AsyncExecutor:
-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {
<init>(java.lang.Throwable);
}
EventBus
黏性事件
黏性事件:就是在发送事件之后再订阅该事件也能接收到,和黏性广播类似。
1. 订阅者处理黏性事件
在MainActivity
中重新写一个方法来处理黏性事件:
@Subscribe(threadMode = ThreadMode.POSTING, sticky = true)
public void onMoonEvent(MessageEvent messageEvent) {
tv_message.setText(messageEvent.getMessage());
}
2. 发送黏性事件
在SecondActivity
中定义一个Button
来发送黏性事件:
btn_message.setOnClickListener(v -> {
EventBus.getDefault().postSticky(new MessageEvent("黏性事件"));
finish();
});
在MainActivity
中,不用点击"注册事件"按钮,而是直接跳转到SecondActivity
中点击“发送黏性事件”。这时界面回到MainActivity
中,TextView
仍旧显示MainActivity
的字段,这是因为还没有订阅事件,就点击“注册事件”按钮,TextView
内容发生改变,显示“黏性事件”,说明事件被成功接收到了。
3. 取消注册
@Override
protected void onDestroy() {
super.onDestroy();
// 移除黏性事件
EventBus.getDefault().removeAllStickyEvents();
EventBus.getDefault().unregister(this);
}
源码解析EventBus
1. EventBus
的构造方法
volatile [ˈvɑːlətl] 不稳定的;反复无常的 synchronized [ˈsɪŋkrənaɪzd] 同步的;同步化的
在使用EventBus
的时候,首先会调用EventBus.getDefault()
来获取EventBus
的实例:
public class EventBus {
static volatile EventBus defaultInstance;
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;
}
}
**这是一个单例模式,采用了双重检查模式(Double check the lock DCL
)。**以下是EventBus
的构造方法:
public class EventBus {
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
public EventBus() {
this(DEFAULT_BUILDER);
}
EventBus(EventBusBuilder builder) {
logger = builder.getLogger();
subscriptionsByEventType = new HashMap<>();
typesBySubscriber = new HashMap<>();
stickyEvents = new ConcurrentHashMap<>();
mainThreadSupport = builder.getMainThreadSupport();
mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
backgroundPoster = new BackgroundPoster(this);
asyncPoster = new AsyncPoster(this);
indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
builder.strictMethodVerification, builder.ignoreGeneratedIndex);
logSubscriberExceptions = builder.logSubscriberExceptions;
logNoSubscriberMessages = builder.logNoSubscriberMessages;
sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
throwSubscriberException = builder.throwSubscriberException;
eventInheritance = builder.eventInheritance;
executorService = builder.executorService;
}
}
通过构造一个EventBusBuilder
来对EventBus
进行配置,这里采用了建造者模式。
2. 订阅者注册
获取EventBus
后,可以将订阅者注册到EventBus
中:
public class EventBus {
private final SubscriberMethodFinder subscriberMethodFinder;
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
// 1.查找一个SubscriberMethod的集合,也就是传进来的订阅者的所有订阅方法
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
// 2. 遍历订阅者的所有订阅方法来完成订阅者的注册操作
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
}
// SubscriberMethod封装了Mehtod对象、线程模式、事件类型、优先级、是否是黏性事件等
public class SubscriberMethod {
final Method method;
final ThreadMode threadMode;
final Class<?> eventType;
final int priority;
final boolean sticky;
String methodString;
}
在register
方法中做了两件事:一是查找订阅者的订阅方法,另一件事是遍历订阅者的订阅方法来完成注册。
1. 查找订阅者的订阅方法
以下是SubscriberMehodFinder.findSubscriberMethods
方法:
class SubscriberMethodFinder {
private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>();
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
// 1. 从缓存中查找是否有订阅方法的集合,如果找到了就立即返回
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
// 2. 如果缓存中没有,则根据ignoreGeneratedIndex属性来选择采用何种方法来查找订阅方法的集合,ignoreGeneratedIndex属性表示是否忽略注解器生成的MyEventBusIndex,如何生成MyEventBusIndex类以及它的使用,默认值是false,可以通过EventBusBuilder来设置它的值。
if (ignoreGeneratedIndex) {
subscriberMethods = findUsingReflection(subscriberClass);
} else {
// 3. 通过EventBus单例模式来获取默认的EventBus对象,也就是ignoreGeneratedIndex=false的情况
subscriberMethods = findUsingInfo(subscriberClass);
}
if (subscriberMethods.isEmpty()) {
} else {
// 4. 找到订阅方法的集合后,放入缓存,以免下次继续查找
METHOD_CACHE.put(subscriberClass, subscriberMethods);
return subscriberMethods;
}
}
// 3.1 获取订阅者信息
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(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 {
// 3.2 通过反射来查找订阅事件
findUsingReflectionInSingleClass(findState);
}
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
}
2. 订阅者的注册过程
以下是EventBus.subscribe
方法:
public class EventBus {
private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
private final Map<Object, List<Class<?>>> typesBySubscriber;
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
// 1. 得到当前订阅了事件的方法的参数类型
Class<?> eventType = subscriberMethod.eventType;
// 2. 会根据subscriber(订阅者)和subscribeMethod(订阅方法)创建一个Subscription(订阅对象)
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
// 3. 根据eventType(事件类型)获取Subscription(订阅对象集合)
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
// 4. 创建subscriptions对象,并将subscriptions根据EventType保存在subscriptionsByEventType(Map集合)
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
}
int size = subscriptions.size();
for (int i = 0; i <= size; i++) {
if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
// 5. 按照订阅方法的优先级插入到订阅对象集合中,完成订阅方法的注册
subscriptions.add(i, newSubscription);
break;
}
}
// 6. 通过subscriber获取subscribeEvents(事件类型集合)
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
// 7. 创建subscribedEvents,根据subscriber添加到typesBySubscriber(Map集合),
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
// 8. 如果是黏性事件,则从stickyEvents事件保存队列中取出该事件类型的事件发送给当前订阅者
if (subscriberMethod.sticky) {
if (eventInheritance) {
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);
}
}
}
}
EventBus.subscribe
方法做了两件事:一件是将subscriptions
根据eventType
封装到subscriptionsByEventTyps
中,将subscribedEvent
根据subscriber
封装到typesBySubscriber
中;另一件事对黏性事件的处理。
3. 事件的发送
在获取EventBus
对象后,可以通过post
方法来对进行对事件的提交,以下是EventBus.post
方法的源码:
public class EventBus {
public void post(Object event) {
// 保存着事件队列和线程状态信息
PostingThreadState postingState = currentPostingThreadState.get();
// 1.获取事件队列,并将当前事件插入事件队列
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 {
// 2. 处理队列中的所有事件,并移除该事件
while (!eventQueue.isEmpty()) {
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
}
首先从PostingThreadState
对象中取出事件队列,然后将当前的事件插入事件队列。最后将队列中的事件依次交由postSingleEvent
方法进行处理,并移除该事件。 以下是postSingleEvent
方法的源码:
public class EventBus {
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
// eventInheritance表示是否向上查找事件的父类,默认为true,可以通过EventBuilder中进行配置。
if (eventInheritance) {
// 找到所有父类事件并保存在List中,然后通过postSingleEventForEventType方法对事件逐一处理
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));
}
}
}
}
eventInheritance
表示是否向上查找事件的父类,它的默认值是true
,可以通过EventBusBuilder
中进行配置。当eventInheritance
为true
时,则通过lookupAllEventTypes
找到所有父类事件并存在List
中,然后通过postSingleEventForEventType
方法对事件逐一处理。postSingleEventForEventType
的源码如下:
public class EventBus {
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
// 1
subscriptions = subscriptionsByEventType.get(eventClass);
}
if (subscriptions != null && !subscriptions.isEmpty()) {
// 2
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;
}
}
注释1
处同步取出事件对应的subscriptions
(订阅对象集合),注释2
处遍历subscriptions
,将事件event
和对应的subscription
(订阅对象)传递给postingState
并调用postToSubscription
方法对事件进行处理, 以下是postToSubscription
方法:
public class EventBus {
private final Poster mainThreadPoster;
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将我们订阅事件添加到主线程队列中
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);
}
}
}
取出订阅方法的threadMode
(线程模式),之后根据threadMode
来分别处理。如果threadMode
是MAIN
,若提交事件的线程是主线程,则通过反射直接运行订阅的方法;若其不是主线程,则需要mainThreadPoster
将订阅的事件添加到主线程队列中。
4. 订阅者取消注册
取消注册需要调用EventBus.unregister
方法:
public class EventBus {
public synchronized void unregister(Object subscriber) {
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber); // 1.
if (subscribedTypes != null) {
for (Class<?> eventType : subscribedTypes) {
unsubscribeByEventType(subscriber, eventType); // 2.
}
typesBySubscriber.remove(subscriber); // 3.
} else {
}
}
}
typesBySubscriber
是一个map
集合,注释1
出通过subscriber
主动到subscribedTypes
(事件类型集合)。注释3
处将subscriber
对应的eventType
从typesBySubscriber
中移除。 注释2
处遍历subscribedTypes
,并调用unsubscribeByEventType
方法:
public class EventBus {
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
List<Subscription> subscriptions = subscriptionsByEventType.get(eventType); // 1
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--;
}
}
}
}
}
注释1
出通过eventType
来得到对应的subscriptions
(订阅对象集合),并在for
循环中判断如果subscription
(订阅对象)的subscriber
(订阅者)属性等于传进来的subscriber
,则从subscriptions
中移除该subscription
。
5. 黏性事件
注册
在EventBus.register
的subscribe
方法中,有一段代码是用来处理黏性事件的:
public class EventBus {
private final Map<Class<?>, Object> stickyEvents;
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
if (subscriberMethod.sticky) {
// 1. 默认为true,表示是否向上查找事件的父类
if (eventInheritance) {
// 2. 发送黏性事件时,保存了事件类型和对应事件
Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
for (Map.Entry<Class<?>, Object> entry : entries) {
Class<?> candidateEventType = entry.getKey();
if (eventType.isAssignableFrom(candidateEventType)) {
// 3. 获取对应的事件
Object stickyEvent = entry.getValue();
// 4. 处理黏性事件
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
}
处理黏性事件就是遍历stickyEvents
,如果当前要注册的事件订阅方法是黏性的,并且该方法接收的事件类型和stickyEvents
中某个事件类型相同或者是其父类,则取出stickyEvent
中对应事件类型的具体事件,做进一步处理。
subscribe
方法的核心是checkPostStickyEventToSubscription
:
public class EventBus {
private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
if (stickyEvent != null) {
postToSubscription(newSubscription, stickyEvent, isMainThread());
}
}
}
发送
EventBus.postSticky
源码如下:
public class EventBus {
private final Map<Class<?>, Object> stickyEvents;
public void postSticky(Object event) {
synchronized (stickyEvents) {
stickyEvents.put(event.getClass(), event);
}
post(event);
}
}
postSticky()
方法主要做了两件事:先将事件类型和对应事件保存到stickyEvents
中,方便使用。然后执行post(event)
继续发送事件,这个post
方法就是之前普通事件的发送方法,所以,如果在发送黏性事件之前,已经有了对应类型事件的订阅者,即使它是非黏性的,也可以接收到发送出的黏性事件。
总结
参考
https://github.com/greenrobot/EventBus/
https://segmentfault.com/a/1190000020052249?utm_source=tag-newest
https://zhuanlan.zhihu.com/p/51357583
https://segmentfault.com/a/1190000018706349/