一、简介
EventBus是Android和Java的发布/订阅消息总线。它简化了组件之间的通信,将事件的发送者和接受者进行分离、避免了复杂且容易出错的依赖关系和生命周期问题、并且体积小、效率高、使用简单。
二、使用
1.添加依赖
implementation 'org.greenrobot:eventbus:3.1.1'
2.注册
和android四大组件的broadcast receiver一样,需要对其进行注册和解除。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_event_bus);
EventBus.getDefault().register(this);
}
@Override
protected void onDestroy() {
super.onDestroy();
if (EventBus.getDefault().isRegistered(this))
EventBus.getDefault().unregister(this);
}
3.定义消息对象
消息对象是普通的旧Java对象,没有任何特殊要求。
public class MessageObj {
private volatile static MessageObj instance;
public String message;
private MessageObj(){}
public static MessageObj getInstance(){
if (instance == null){
synchronized (MessageObj.class) {
if (instance == null) {
return new MessageObj();
}
}
}
return instance;
}
}
4.订阅者
订阅者实现消息处理,使用@Subscribe注释定义的。
@Subscribe(threadMode = ThreadMode.MAIN)
public void getMessageObj(MessageObj messageObj){
//TODO 处理获取的消息对象
}
其内传入了ThreadMode,ThreadMode是一个枚举对象,有四种线程模式,分别是;:
-
POSTING:默认,消息的处理和发布在同一个线程中,避免了线程间的切换,开销最小。
-
MAIN:将在Andoid的主线程中调用订阅者,为了防止ANR,因此在这里不能进行耗时操作。
-
BACKGROUND:将在Android的后台线程中调用订阅者,因此不能进行UI操作。如果发布消息的线程是主线程(UI线程),那么事件处理函数将会开启一个后台线程,如果果发布消息的线程是在后台线程,那么消息处理函数就使用该线程。
-
ASYNC:订阅者将在单独的线程中被调用,它始终独立发布消息的线程和主线程,因此同样不能进行UI操作。
5.发布消息
从代码的任何部分发布消息。所有与该消息类型匹配的当前注册的订阅者都将收到它。
EventBus.getDefault().post(MessageObj.getInstance());
6.黏性事件
某些事件包含事件发布后感兴趣的信息。例如,事件表示某些初始化已完成。或者,如果您有一些传感器或位置数据,并且您希望保留最新值。您可以使用粘性事件,而不是实现自己的缓存。所以EventBus将某种类型的最后一个粘性事件保存在内存中。然后,粘性事件可以传递给订阅者或明确查询。因此,您不需要任何特殊逻辑来考虑已有的数据。总结一句话就是:黏性事件可以先发布、后注册,在注册期间,所有粘性订阅者方法将立即获得先前发布的粘性事件。
//发布事件
EventBus.getDefault().post(MessageObj.getInstance()); //普通方式
EventBus.getDefault().postSticky(MessageObj.getInstance()); //黏性事件
//订阅者--普通方式
@Subscribe(threadMode = ThreadMode.MAIN)
public void getMessageObj(MessageObj messageObj){
//TODO 处理获取的消息对象
}
//订阅者--黏性事件
@Subscribe(threadMode = ThreadMode.MAIN,sticky = true)
public void getMessageObj(MessageObj messageObj){
//TODO 处理获取的消息对象
}
7.优先级
虽然EventBus的大多数用例不需要优先级,但在某些特殊情况下它们可能会派上用场。您可以通过在注册期间为订户提供优先级来更改事件传递的顺序。在同一传递线程(ThreadMode)中,较高优先级的订户将在优先级较低的其他订户之前接收事件。注意:优先级不会影响具有不同ThreadModes的订阅者的传递顺序!
//订阅者--黏性事件
@Subscribe(threadMode = ThreadMode.MAIN,sticky = true,priority = 1)
public void getMessageObj(MessageObj messageObj){
//TODO 处理获取的消息对象
}
priority参数就是决定优先级的参数,它是一个整数类型的值,默认是0,其值越大表示优先级越高,这个订阅者会优先接受到事件。
三、分析
1.getDefault
通过单例模式double-checked locking获取对象,并在构造函数中获取EventBusBuilder构造器,并初始化必须的参数和集合。
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
...
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
...
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;
}
2.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);
}
}
}
首先获取订阅者class对象,SubscriberMethodFinder调用findSubscriberMethods方法查询到SubscriberMethod集合,并通过subscribe使订阅订阅者订阅集合所有的SubscriberMethod。SubscriberMethod类中,主要保存订阅方法的Method对象、线程模式、事件类型、优先级、是否是粘性事件等属性。
这里看一下findSubscriberMethods集合的查询方式。
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
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;
}
}
这里有一个缓存机制、优先使用缓存中的数据、如果缓存中数据为空,ignoreGeneratedIndex表示是否忽略注解器生成的MyEventBusIndex,在EventBusBuilder构造器中默认为false,所以这里通过findUsingInfo方法查找集合并重新存入缓存中。
下面我们来分析下findUsingInfo方法。
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
//获得订阅者的信息,一开始会返回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 {
//通过反射来查找订阅方法
findUsingReflectionInSingleClass(findState);
}
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
首先它初始化了FindState对象,该对象是SubscriberMethodFinder的静态内部类,它保存了SubscriberMethod集合以及对其的校验。如果我们通过EventBusBuilder配置了MyEventBusIndex,便会获取到subscriberInfo,调用subscriberInfo的getSubscriberMethods方法便可以得到SubscriberMethod相关的信息,否则调用findUsingReflectionInSingleClass(findState)通过注解进行获取订阅方法。
private void findUsingReflectionInSingleClass(FindState findState) {
Method[] methods;
try {
// This is faster than getMethods, especially when subscribers are fat classes like Activities
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();
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");
}
}
}
这里通过Java反射机制获取所有的method,然后通过注解接口Subscribe筛选订阅方法,并将相关信息保存到FindState中。
最终上面提到的findUsingInfo方法调用getMethodsAndRelease(findState)返回SubscriberMethod集合。
private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {
List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods);
findState.recycle();
synchronized (FIND_STATE_POOL) {
for (int i = 0; i < POOL_SIZE; i++) {
if (FIND_STATE_POOL[i] == null) {
FIND_STATE_POOL[i] = findState;
break;
}
}
}
return subscriberMethods;
}
从findState获取subscriberMethods,放进新的ArrayList。需要注意的是这里使用了一个复用池FIND_STATE_POOL。
3.subscribe
在register中,最终调用了subscribe方法来使subscriber订阅subscriberMethod。
// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
//获取订阅事件类型的class类
Class<?> eventType = subscriberMethod.eventType;
//订阅者和订阅方法 封装成Subscription
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
//获取当前订阅事件中Subscription的List集合
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);
}
}
//根据优先级来设置放进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;
}
}
//根据subscriber(订阅者)来获取它的所有订阅事件
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);
}
}
}
首先根据subscriber和subscriberMethod创建一个Subscription对象,该对象封装了一个active。然后通过事件类型获取Subscription集合,如果该集合不存在,则创建集合并将上面的Subscription对象存入集合中。如果集合中包含了上述Subscription对象,则抛出异常表示该事件已经注册过了。接着根据优先级来设置放进subscriptions的位置。最后将订阅事件类型用同样的方式存入事件类型的集合中。至此事件订阅成功,至于后面的黏性事件处理,后面在一起分析。
4.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);
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;
}
}
}
首先通过currentPostingThreadState获取当前线程的postingState,rrentPostingThreadState是一个ThreadLocal,不同的线程之间不会相互影响。得到当前线程的消息队列,并将当前事件event放入消息队列eventQueue中,然后判断postingState是否有消息正在分发,设置是否在主线程中分发,最后调用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));
}
}
}
eventInheritance表示是否向上查找事件的父类,默认值为true,当为true时,通过lookupAllEventTypes找到所有的父类的事件并存在List中,最后则遍历事件通过postSingleEventForEventType处理事件。
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 = 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;
}
首先获取Subscription集合,并遍历集合,将subscription封装进PostingState中,最后调用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);
}
}
postToSubscription中获取线程模式,之后根据所设置的线程模式来选择线程执行订阅方法的线程。invokeSubscriber方法主要是通过订阅方法的反射来执行。而mainThreadPoster主要是使用消息队列的形式Handler分发。
5.unregister
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());
}
}
首先获取所有的订阅事件类型,然后遍历该集合并调用unsubscribeByEventType()方法从事件类型中移除订阅者。
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--;
}
}
}
}
获取Subscription集合并遍历,通过Subscription中的subscriber属性比较订阅者,判断出要注销的订阅者,设置active为false并从移除Subscription。