使用篇
1.添加依賴庫
compile 'org.greenrobot:eventbus:3.0.0'
2.注冊、訂閱、取消訂閱事件
EventBus.getDefault().register(obj);//注冊
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)//sticky:true為黏性事件,false為一般事件
public void onMessageReceive(Object obj) {//訂閱
//onMessageReceive這個方法名可以任意取,obj為發布者發布的事件類型
//省略...
}
EventBus.getDefault().unregister(obj);//取消訂閱
3.事件發布者發布事件
EventBus.getDefault().post(obj);//一般事件
EventBus.getDefault().postSticky(obj);//黏性事件
4.混淆
-keepattributes *Annotation*
-keepclassmembers class **{
@org.greenrobot.eventbus.Suscribe<methods>;
}
-keep enum org.greenrobot.eventbus.ThreadMode{*;}
#only required if you use AsyncExecutor
-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent{
<init>(java.lang.Throwable);
}
源碼解析篇
當我們使用EventBus時,首先會調用EventBus.getDefault()來獲取EventBus實例,下面看getDefault方法:
EventBus#getDefault
static volatile EventBus defaultInstance;//EventBus實例
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
很明顯,這是采用DCL雙重檢查的單例設計模式,下面看它的構造方法:
//EventBus的建造者模式builder,通過它為EventBus設置各種默認屬性
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
public EventBus() {
this(DEFAULT_BUILDER);//DEFAULT_BUILDER為EventBus的建造者模式builder
}
EventBus(EventBusBuilder builder) {
//private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
//訂閱者與訂閱事件的集合,注意:一個訂閱者可以訂閱多個訂閱事件
//因此是一個訂閱者和若干個訂閱事件
subscriptionsByEventType = new HashMap<>();
//private final Map<Object, List<Class<?>>> typesBySubscriber;
//訂閱事件類型,根據訂閱對象輕松找到訂閱者類,用于取消訂閱時
typesBySubscriber = new HashMap<>();
//private final Map<Class<?>, Object> stickyEvents;
//訂閱者和訂閱事件,黏性事件
stickyEvents = new ConcurrentHashMap<>();
//這是一個handler,用于排隊發布事件與取出發布事件來消費
mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
//后臺線程
backgroundPoster = new BackgroundPoster(this);
//異步線程
asyncPoster = new AsyncPoster(this);
indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
//private final SubscriberMethodFinder subscriberMethodFinder;
//訂閱者方法的查找類,這個類很重要,用于查找訂閱者的訂閱方法(集合)
subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
builder.strictMethodVerification, builder.ignoreGeneratedIndex);
//省略...
}
通過構造方法,它調用了this(DEFAULT_BUILDER),這里的DEFAULT_BUILDER就是EventBusBuilder,可以看出,它通過建造者模式對EventBus進行各種配置。這里就不貼EventBusBuilder的代碼了,可自行閱讀EventBusBuilder源碼。
在獲取了EventBus實例后,會調用注冊方法,把訂閱者注冊到EventBus中。下面看這個注冊方法:
EventBus#register
public void register(Object subscriber) {
//獲取訂閱者類class
Class<?> subscriberClass = subscriber.getClass();
//通過訂閱者方法查找類的查找方法,返回訂閱方法SubscriberMethod集合
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {//遍歷訂閱方法集合
subscribe(subscriber, subscriberMethod);//對每個訂閱者進行注冊
}
}
}
通過閱讀register源碼,我們不難看出它整體思路:通過訂閱者class,獲取該訂閱者里面所有的訂閱方法,然后開啟同步鎖并遍歷訂閱方法,對每一個訂閱方法進行注冊操作。
這里要補充說明一下SubscriberMethod(訂閱方法)里面的幾個屬性:
public class SubscriberMethod {
final Method method;//訂閱者定義的方法
//ThreadMode為枚舉類型,取值如下:POSTING,MAIN,BACKGROUND,ASYNC
final ThreadMode threadMode;//訂閱方法工作的線程
final Class<?> eventType;//EventBus發布(post)的事件類型,即訂閱方法接收的數據類型
final int priority;//訂閱方法優先級
final boolean sticky;//是否為黏性方法
//其余部分省略...
}
下面看看訂閱者方法查找類究竟是如何查找訂閱方法的?
SubscriberMethodFinder#findSubscriberMethods
//METHOD_CACHE是緩存,key是訂閱者,value是訂閱方法集合
private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>();
//是否忽略注解器生成的XxxEventBus
private final boolean ignoreGeneratedIndex;
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
//從緩存中獲取訂閱者類class的訂閱方法
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {//一旦在緩存中找到,就不再查找,直接返回訂閱方法集合
return subscriberMethods;
}
//ignoreGeneratedIndex默認為false,也可以通過EventBusBuilder來設置。
//這里因為我們是通過EventBus.getDefault()方法來獲取的實例
//所以ignoreGeneratedIndex是默認false的情況,即執行else情況
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 {
//把訂閱者類class與訂閱方法集合存入緩存,方便下次調用不再繼續查找,直接緩存讀取
METHOD_CACHE.put(subscriberClass, subscriberMethods);
return subscriberMethods;
}
}
閱讀上面源碼可知:首先從緩存METHOD_CACHE中查找訂閱方法集合,找到立即返回,后面不會再執行;否則,根據ignoreGeneratedIndex是否為true,執行查找獲取對應的訂閱方法集合;接著對找到的訂閱方法集合判空,如果是空,則拋出異常,不為空時,則把訂閱者與對應的訂閱方法集合存入緩存,方便下次調用不需重新查找,最后返回訂閱方法集合。
從上面源碼可知,查找訂閱方法由findUsingInfo方法執行,下面看該方法:
SubscriberMethodFinder#findUsingInfo
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
FindState findState = prepareFindState();//返回FindState實例
findState.initForSubscriber(subscriberClass);//初始化FindState
while (findState.clazz != null) {//當findState.clazz == null時結束循環
//獲取訂閱者信息,并賦值給findState.subscriberInfo
findState.subscriberInfo = getSubscriberInfo(findState);
if (findState.subscriberInfo != null) {//對findState.subscriberInfo進行判空
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);
}
觀察findUsingInfo方法,首先執行的是prepareFindState方法,返回查找狀態類FindState的實例。
這里需補充一下說明FindState,它是SubscriberMethodFinder的靜態內部類:
SubscriberMethodFinder靜態內部類FindState
static class FindState {
//訂閱方法
final List<SubscriberMethod> subscriberMethods = new ArrayList<>();
final Map<Class, Object> anyMethodByEventType = new HashMap<>();
final Map<String, Class> subscriberClassByMethodKey = new HashMap<>();
final StringBuilder methodKeyBuilder = new StringBuilder(128);
Class<?> subscriberClass;//訂閱者class
Class<?> clazz;//訂閱者class
boolean skipSuperClasses;//是否跳過檢查父類class
SubscriberInfo subscriberInfo;//訂閱者信息
//其他略...
}
下面看prepareFindState方法是如何返回查找狀態類FindState實例的:
SubscriberMethodFinder#prepareFindState
private static final int POOL_SIZE = 4;
//定義一個容量為4的查找狀態數組
private static final FindState[] FIND_STATE_POOL = new FindState[POOL_SIZE];
private FindState prepareFindState() {
synchronized (FIND_STATE_POOL) {//以數組作為同步鎖
for (int i = 0; i < POOL_SIZE; i++) {//對數組進行遍歷
FindState state = FIND_STATE_POOL[i];
if (state != null) {//一旦發現查找狀態不為空,立即返回,并把數組內查找狀態置為空
FIND_STATE_POOL[i] = null;
return state;
}
}
}
return new FindState();//在數組內的查找狀態均為空時,新建一個查找狀態實例返回
}
在獲得FindState實例后,調用initForSubscriber初始化該狀態:
FindState#initForSubscriber
void initForSubscriber(Class<?> subscriberClass) {
this.subscriberClass = clazz = subscriberClass;//把訂閱者class賦值給subscriberClass和clazz
skipSuperClasses = false;
subscriberInfo = null;
}
之后在findState.clazz不為空的條件下循環,直到findState.clazz為空結束循環,并通過調用getMethodsAndRelease方法返回List<SubscriberMethod>。
先看循環體內邏輯,調用getSubscriberInfo方法,返回查找狀態的訂閱者信息findState.subscriberInfo,依據該信息是否為空,執行相應的操作。還是看看getSubscriberInfo方法:
SubscriberMethodFinder#getSubscriberInfo
private List<SubscriberInfoIndex> subscriberInfoIndexes;
private SubscriberInfo getSubscriberInfo(FindState findState) {
//通過FindState#initForSubscriber方法 => subscriberInfo = null可知,不會執行if語句塊
if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) {
SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();
if (findState.clazz == superclassInfo.getSubscriberClass()) {
return superclassInfo;
}
}
//subscriberInfoIndexes到目前為止,均未被賦值,因此也是null,因此也不會執行if語句塊
if (subscriberInfoIndexes != null) {
for (SubscriberInfoIndex index : subscriberInfoIndexes) {
SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
if (info != null) {
return info;
}
}
}
return null;//有上面可知,最終執行返回null
}
該方法,首先是判空:在傳入的查找狀態findState中存儲的訂閱信息不為空且其父類定閱信息不為空的條件下,獲取其父類訂閱信息superclassInfo,然后在傳入的訂閱信息與其父類訂閱信息一致的情況下,返回該父類訂閱信息;第二次判空:當subscriberInfoIndexes不為空時,對數組subscriberInfoIndexes進行遍歷,在訂閱者信息不為空時返回,不在往下執行;在兩次判空均為null時,直接返回null(訂閱者信息)。
對于getSubscriberInfo方法,首先傳入的findState就是上面通過初始化之后的狀態,即findState.subscriberInfo為空,接著由EventBus的構造方法subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,builder.strictMethodVerification, builder.ignoreGeneratedIndex)可知,subscriberInfoIndexes也是空,所以最終返回的訂閱者信息是空,所以findUsingInfo方法的循環體里面的判斷語句執行else語句:findUsingReflectionInSingleClass(findState)。
SubscriberMethodFinder#findUsingReflectionInSingleClass
private void findUsingReflectionInSingleClass(FindState findState) {
Method[] methods;
try {
//通過反射獲取訂閱者所有方法(public + private)
methods = findState.clazz.getDeclaredMethods();
} catch (Throwable th) {
//通過反射獲取訂閱者所有方法(public)
methods = findState.clazz.getMethods();
findState.skipSuperClasses = true;
}
for (Method method : methods) {//遍歷所有的方法
int modifiers = method.getModifiers();//獲取方法的修飾符
//只有方法修飾符是public且不是abstract或static才能進入執行if語句塊
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
//獲取方法的參數類型數組
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length == 1) {//只有當方法的參數為一個時,才能執行if語句塊
//滿足上面所有條件的方法獲取方法注解@Subscribe
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
if (subscribeAnnotation != null) {//當獲取的注解不是空時,此方法才是訂閱者類的訂閱方法
//獲取方法參數類型,即post(Object event)的event類型
Class<?> eventType = parameterTypes[0];
if (findState.checkAdd(method, eventType)) {//當訂閱者與訂閱方添加成功后
ThreadMode threadMode = subscribeAnnotation.threadMode();
//把訂閱方法添加到findState.subscriberMethods里面
findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
}
}
//默認情況下strictMethodVerification=false,因此if語句塊不會執行。
} 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);
}
//默認情況下strictMethodVerification=false,因此if語句塊不會執行。
} 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");
}
}
}
通過閱讀findUsingReflectionInSingleClass源碼,我們知道:首先通過反射獲取訂閱者類的所有方法,然后對方法進行遍歷,只有當方法修飾符為public、方法參數只有一個且該方法存在@Subscribe注解時,該方法才是訂閱者類的訂閱方法,接著獲取@Subscribe注解實例(不為空時),把訂閱方法(包括方法,訂閱者,線程模式,優先級,是否黏性事件)添加到findState.subscriberMethods里面。
分析完findUsingReflectionInSingleClass源碼,接著就是findState.moveToSuperclass:
FindState#moveToSuperclass
void moveToSuperclass() {
if (skipSuperClasses) {//跳過父類方法檢測
clazz = null;
} else {//檢測父類方法時
clazz = clazz.getSuperclass();
String clazzName = clazz.getName();
/** Skip system classes, this just degrades performance. */
if (clazzName.startsWith("java.") || clazzName.startsWith("javax.") || clazzName.startsWith("android.")) {
clazz = null;
}
}
}
從這段源碼可知:當skipSuperClasses為true,或者父類方法是系統方法時,置clazz為null,其他時候置clazz為clazz.getSuperclass()。
最后就是跳出循環體,通過調用getMethodsAndRelease方法,返回訂閱方法集合了。
SubscriberMethodFinder#getMethodsAndRelease
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新建一個訂閱者方法集合并最終返回,接著重置findState,并把之前的findState設置到FIND_STATE_POOL數組中。
分析完查找訂閱者的訂閱方法之后,回到EventBus的register方法,采用同步鎖并遍歷訂閱方法集合,對找到的每一個訂閱方法與訂閱者進行注冊操作。
EventBus#subscribe
// Must be called in synchronized block必須在同步代碼塊中調用
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
//首先獲取EventBus的post數據類型
Class<?> eventType = subscriberMethod.eventType;
//創建一個訂閱事件(包括訂閱者和訂閱方法)
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
//Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType
//以發布的數據類型為key,獲取線程安全的發布事件集合
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions == null) {//當集合為空時,new一個,并把發布數據類型與發布事件存入緩存
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;
}
}
//Map<Object, List<Class<?>>> typesBySubscriber
//從緩存中根據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) {//默認是true,事件是否有繼承性
// 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 {
//Map<Class<?>, Object> stickyEvents
//緩存中根據發布的事件類型class,取出原發布事件
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
剖析該方法前,先補充說明一下訂閱事件Subscription這個類:
final class Subscription {
final Object subscriber;//訂閱者類
final SubscriberMethod subscriberMethod;//訂閱方法(方法,線程,發布事件類型,優先級,黏性等)
//一旦EventBus#unregister被調用,active就會變成false,
//這將通過隊列事件傳遞EventBus#invokeSubscriber來檢查,以防止競態條件。
volatile boolean active;
}
了解完訂閱事件Subscription后,再閱讀subscribe方法源碼:首先是根據訂閱方法,獲取發布數據類型class,接著創建訂閱事件,根據訂閱事件集合是否為空,空則創建集合,并把發布數據class與訂閱事件集合存入緩存;不為空則拋重復注冊異常;接著根據訂閱事件的優先級或是否到達末尾,把訂閱事件插入適當的位置;然后根據發布數據class集合是否為空,空則創建,并把訂閱者與發布事件集合存入緩存,不為空時,把發布的事件存入發布事件集合。最后根據是否為黏性事件完成事件注冊。
下面看checkPostStickyEventToSubscription方法:
EventBus#checkPostStickyEventToSubscription
private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
if (stickyEvent != null) {//stickyEvent必不為空
postToSubscription(newSubscription, stickyEvent, Looper.getMainLooper() == Looper.myLooper());
}
}
//根據訂閱事件的線程模式執行不同的方法,第三個參數是判斷是否為主線程
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
case POSTING://在哪個線程post就在哪個線程接收事件并處理
invokeSubscriber(subscription, event);//反射調用method方法
break;
case MAIN://僅在主線程接收處理
if (isMainThread) {
invokeSubscriber(subscription, event);//反射調用method方法
} else {
mainThreadPoster.enqueue(subscription, event);//handler處理
}
break;
case BACKGROUND://僅在后臺程接收處理
if (isMainThread) {
backgroundPoster.enqueue(subscription, event);//線程池處理
} else {
invokeSubscriber(subscription, event);//反射調用method方法
}
break;
case ASYNC://另啟線程(workthread)接收處理
asyncPoster.enqueue(subscription, event);//線程池處理
break;
default://不屬于以上線程,直接拋未知線程異常
throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
}
}
至此,EventBus的register方法分析完畢,下面看post方法:
EventBus#post
public void post(Object event) {
//獲取發送線程的實例postingState
PostingThreadState postingState = currentPostingThreadState.get();
//獲取派送事件隊列,并把事件加入隊列
List<Object> eventQueue = postingState.eventQueue;
eventQueue.add(event);
if (!postingState.isPosting) {//事件未派送狀態
//判斷是否UI線程
postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper();
postingState.isPosting = true;//事件派送狀態標志位置為true
if (postingState.canceled) {//正在派送狀態,如果被取消,拋內部異常
throw new EventBusException("Internal error. Abort state was not reset");
}
try {
while (!eventQueue.isEmpty()) {
//當派送事件隊列不為空時,對第一個事件進行派送,并移除該事件
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {//最終都會把是否在派送標志位和UI線程標志位置為false
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
//ThreadLocal可保證不同線程值多樣性(線程獨享),這里僅創建了一個PostingThreadState實例
private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
@Override
protected PostingThreadState initialValue() {
return new PostingThreadState();
}
};
//字面理解:發送線程的狀態
//eventbus備注*For ThreadLocal, much faster to set (and get multiple values)*
final static class PostingThreadState {
final List<Object> eventQueue = new ArrayList<Object>();//事件隊列
boolean isPosting;//標志位,是否在派送
boolean isMainThread;//標志位,是否主線程
Subscription subscription;//訂閱事件處理器:訂閱者+訂閱方法+是否激活標志位
Object event;//事件
boolean canceled;//標志位,是否被取消
}
post方法很簡單,大體思路:首先獲取派送事件狀態,然后把要派送的是按放入該派送事件狀態的事件隊列中,根據標志位未派送狀態,對事件隊列中的事件逐一取出并進行派送,并且最終把標志位是否被派送和UI線程置為false,下面看postSingleEvent方法是如何進行事件派送的:
EventBus#postSingleEvent
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;//默認訂閱處理器未發現處理方法
//默認為true,表示訂閱事件具有繼承性,即該事件的父類或與該事件實現同一接口的訂閱者都會處理該事件
if (eventInheritance) {
//查找所有可處理該事件相關類或接口
List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
int countTypes = eventTypes.size();
for (int h = 0; h < countTypes; h++) {//遍歷所有事件派送到的類
Class<?> clazz = eventTypes.get(h);
//檢查這些可處理事件的類是否有處理對應事件的方法,返回值為boolean類型
subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
}
} else {
subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
}
if (!subscriptionFound) {//未發現可處理該派送事件的類(即沒有訂閱者)
if (logNoSubscriberMessages) {
Log.d(TAG, "No subscribers registered for event " + eventClass);
}
if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
eventClass != SubscriberExceptionEvent.class) {
post(new NoSubscriberEvent(this, event));
}
}
}
//查找所有事件類
private static List<Class<?>> lookupAllEventTypes(Class<?> eventClass) {
//private static final Map<Class<?>, List<Class<?>>> eventTypesCache = new HashMap<>();
synchronized (eventTypesCache) {//同步鎖
List<Class<?>> eventTypes = eventTypesCache.get(eventClass);
if (eventTypes == null) {
eventTypes = new ArrayList<>();
Class<?> clazz = eventClass;
//直到clazz為空跳出循環
while (clazz != null) {//查找派送事件的所有父類以及實現同一接口的類
eventTypes.add(clazz);
addInterfaces(eventTypes, clazz.getInterfaces());
clazz = clazz.getSuperclass();
}
//使用緩存把派送事件類型及其父類或實現同一接口類進行緩存
eventTypesCache.put(eventClass, eventTypes);
}
return eventTypes;
}
}
//檢查所有可處理該派送事件的類
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {//根據相關List<?>取值subscriptions
subscriptions = subscriptionsByEventType.get(eventClass);
}
//如果訂閱事件處理器不為空,且有值時,進行遍歷,否則返回false
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) {//如果被取消,則跳出循環,返回false
break;
}
}
return true;
}
return false;
}
postSingleEvent方法首先根據發布的事件event來獲取該發布事件的class,然后根據發布事件具有繼承性,查找所有可消費處理該事件的相關類(訂閱者)來處理該事件,過程中又回到前面已經分析過的postToSubscription方法,根據不同線程,調用不同方法對事件進行派發.至此,post方法也分析完畢.
EventBus#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);
}
補充說明下黏性事件,因為黏性事件其實也是調用post方法,只是在post之前先緩存了黏性事件而已.
最后分析一下反注冊unregister方法
EventBus#unregister
public synchronized void unregister(Object subscriber) {
//private final Map<Object, List<Class<?>>> typesBySubscriber;
//從集合中取出該訂閱者所有的訂閱事件
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
if (subscribedTypes != null) {//如果訂閱事件集合不為空時
for (Class<?> eventType : subscribedTypes) {//遍歷,反注冊
unsubscribeByEventType(subscriber, eventType);
}
//從集合中移除該訂閱者
typesBySubscriber.remove(subscriber);
} else {//為空時不需要反注冊
Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
//解除訂閱者與訂閱事件之間的關系
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
//private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> 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;//標志位置為false
subscriptions.remove(i);//移除訂閱事件處理器
i--;
size--;
}
}
}
}
反注冊的邏輯也是很簡單:首先根據訂閱者從集合中取出該訂閱者的所有訂閱事件,然后在該訂閱事件不為空時,進行遍歷,并逐一進行解除.
OK,至此,EventBus源碼分析完結!
T.T~