一、簡介
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。