停停走走又幾天,斷斷續(xù)續(xù)這一年。時間還是如此之快,轉(zhuǎn)眼間又到了17年的末尾了。趁著這些天還能靜下心來看看代碼,趕緊記錄下自己的心得。
這一周,工作任務不多,總是想要看點什么但又不清楚該看啥,有些迷茫~不過既然自己還想要在Android路上繼續(xù)前行,那么請深入探索吧!
OK,閑話不多說。今天打算記錄下關于EventBus的原理以及如何實現(xiàn)。
1. 關于EventBus的使用
還依稀記得第一份工作中,那是還是個零基礎的菜鳥。修修補補那個項目,項目中就用到了EventBus,當時并沒有能力深入了解。當時版本的EventBus還沒有使用注解的形式,還需要自定義(記得是重寫onEvent方法還是啥)。
不過,現(xiàn)在EventBus的版本已經(jīng)到了3.+了,使用方法也和原來大不相同了。總體而言,代碼越來越簡潔,耦合度也越來越低了。說下簡單用法吧:
// 把當前的Activity或Fragment注冊到EventBus
// 相應的,需要在頁面銷毀是調(diào)用unRegister方法
EventBus.getDefault().register(this);
// 通過注解訂閱方法
// threadMode :方法執(zhí)行的線程
// sticky:是否接受粘性事件
// priority:優(yōu)先級
@Subscribe(threadMode = ThreadMode.POSTING, sticky = false, priority = 1)
public onEvent(EventMessage eventMessage){
// do something
}
// 使用:通過調(diào)用post或postSticky方法使用,在訂閱的方法中執(zhí)行要執(zhí)行的代碼
EventBus.getDefault().post(EventMessage);
其實使用是很簡單的,不過3.x的版本中新加了Subscriber Index,使用起來比較麻煩,但是據(jù)說效率要高很多。這篇不算是教程,所以還是簡單的來看基本的使用吧。
2. EventBus的register/unregister過程
如果不看源碼的話,要我說這個注冊過程是把對象保存起來的過程,而取消注冊則是把對象釋放的過程。那么,源碼中如何實現(xiàn)的呢?下面進入源碼階段:
- EventBus.getDefault():
// 單例模式
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
通過單例模式獲得EventBus對象,對象的創(chuàng)建過程:
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
// 保存事件參數(shù)類和Subscription List的Map
private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
// 保存注冊對象和事件參數(shù)類
private final Map<Object, List<Class<?>>> typesBySubscriber;
// 粘性事件
private final Map<Class<?>, Object> stickyEvents;
public class EventBusBuilder {
private final static ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool();
// 異常的設置
boolean logSubscriberExceptions = true;
boolean logNoSubscriberMessages = true;
boolean sendSubscriberExceptionEvent = true;
boolean sendNoSubscriberEvent = true;
boolean throwSubscriberException;
boolean eventInheritance = true;
// 是否忽略生成的Index,默認為false,不忽略
boolean ignoreGeneratedIndex;
boolean strictMethodVerification;
ExecutorService executorService = DEFAULT_EXECUTOR_SERVICE;
List<Class<?>> skipMethodVerificationForClasses;
List<SubscriberInfoIndex> subscriberInfoIndexes;
EventBusBuilder() {
}
}
public EventBus() {
this(DEFAULT_BUILDER);
}
EventBus(EventBusBuilder builder) {
subscriptionsByEventType = new HashMap<>();
typesBySubscriber = new HashMap<>();
stickyEvents = new ConcurrentHashMap<>();
// 主線程發(fā)送器
mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
// 后臺線程發(fā)送器
backgroundPoster = new BackgroundPoster(this);
// 異步線程發(fā)送器
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;
}
EventBus是通過Builder形成創(chuàng)建,可以通過構(gòu)建EventBusBuilder來設置參數(shù),這里只記錄下默認的情況。這里面最重要的是三個Map對象:
- subscriptionsByEventType:用來保存訂閱的方法參數(shù)類型和Subscription List
- typesBySubscriber:用來保存訂閱者對象和被訂閱方法的參數(shù)類型
- stickyEvents:粘性事件
當然,在創(chuàng)建EventBus時創(chuàng)建了三個不同類型的線程類型發(fā)送器,可以匹配注解中的threadMode字段。對象創(chuàng)建完成后需要調(diào)用register方法去將Activity/Fragment注冊:
Eventbus.java:
public void register(Object subscriber) {
// 獲得注冊對象的class
Class<?> subscriberClass = subscriber.getClass();
// 查找訂閱者被訂閱的方法
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
// 訂閱
subscribe(subscriber, subscriberMethod);
}
}
}
register
方法,先要去查找訂閱者的方法,subscriberMethodFinder
在創(chuàng)建對象的時候就被創(chuàng)建。接著調(diào)用subscriberMethodFinder.findSubscriberMethods(subscriberClass)
方法,通過傳入注冊對象的class來查找其需要訂閱的方法:
SubscriberMethodFinder.java:
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
// 從方法緩存中查找
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
// 找到直接返回
if (subscriberMethods != null) {
return subscriberMethods;
}
// 是否忽略生成的Index(高級用法會通過編譯時注解生成,這里不會)
// ignoreGeneratedIndex默認為false,進入下面的代碼
if (ignoreGeneratedIndex) {
// 通過反射獲取方法
subscriberMethods = findUsingReflection(subscriberClass);
} else {
// 通過subscriberInfo獲取方法
subscriberMethods = findUsingInfo(subscriberClass);
}
// 如果沒有找到方法,拋出異常(常見的這個異常,沒有@Subscribe方法)
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;
}
}
首先從緩存中檢查是否擁有當前訂閱類,如果沒有則需要根據(jù)ignoreGeneratedIndex
來判斷是否直接通過反射獲取。這里ignoreGeneratedIndex
默認值為false
,那么需要調(diào)用findUsingInfo
:
SubscriberMethodFinder.java:
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
// 從池中或創(chuàng)建或使用
FindState findState = prepareFindState();
// 初始化
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
// 獲得subscriberInfo,
findState.subscriberInfo = getSubscriberInfo(findState);
// 如果沒有找到(不使用Subscribe Index的話也不會找到)
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);
}
這里通過池取出或創(chuàng)建FindState
對象,并且調(diào)用getSubscriberInfo
查找是否有添加的SubscriberInfoIndex
,這個是需要添加編譯器支持,并且在編譯器自動生成,需要手動添加的。這里默認的情況下是不存在的,所以就會進入findUsingReflectionInSingleClass
又通過反射來調(diào)用(應該就是這里影響了效率,需要遍歷每個方法并且判斷。使用SubscriberInfoIndex 的話會標記類和訂閱的方法,所以速度會比這種方法快很多),接著調(diào)用moveToSuperclass
獲得其父類(結(jié)束條件clazzName.startsWith("java.") || clazzName.startsWith("javax.") || clazzName.startsWith("android.")
),最后獲得這些方法并將findState
回收:
SubscriberMethodFinder.java:
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();
// 如果方法的修飾符是public并且不是 Modifier.ABSTRACT | Modifier.STATIC | BRIDGE | SYNTHETIC
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
// 獲得參數(shù)類型數(shù)組
Class<?>[] parameterTypes = method.getParameterTypes();
// 如果參數(shù)是一個
if (parameterTypes.length == 1) {
// 判斷該方法是否被Subscribe注解修飾
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
// 如果還有Subscribe注解,則將參數(shù)類型
if (subscribeAnnotation != null) {
Class<?> eventType = parameterTypes[0];
// 檢驗下是否之前已經(jīng)添加了,具體下面分析
// 這里返回false,說明其父類也有這個方法,這里只需要子類的threadMode
if (findState.checkAdd(method, eventType)) {
// 添加后獲得threadMode,findState.subscriberMethods中添加SubscriberMethod對象
ThreadMode threadMode = subscribeAnnotation.threadMode();
findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
}
}
} else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
// 拋出參數(shù)過多的異常
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");
}
}
}
通過反射來獲取主要分為:
- 獲得類的方法
- 遍歷類,并且檢查:
2.1 檢查方法修飾符是不是public
2.2 檢查參數(shù)數(shù)量是不是等于1
2.3 檢查方法是否還有Subscribe注解
三個條件都滿足的話則需要檢查方法和參數(shù)類型是否已經(jīng)存在,是否此方法需要添加threadMode(我的理解是主要針對于子類覆蓋了父類的方法) - 根據(jù)檢查結(jié)果來判斷是否添加threadMode
關于checkAdd
方法,這里面挺有意思的,看下吧:
SubscriberMethodFinder.java:
boolean checkAdd(Method method, Class<?> eventType) {
// 2 level check: 1st level with event type only (fast), 2nd level with complete signature when required.
// Usually a subscriber doesn't have methods listening to the same event type.
// 分兩步檢查:
// 1. 檢查參數(shù)類型是不是已經(jīng)存在
// 2. 通過方法簽名(可以說是名稱拼接)來判斷
Object existing = anyMethodByEventType.put(eventType, method);
// 不存在接收該參數(shù)類型的方法
if (existing == null) {
return true;
} else {
// 存在接收該參數(shù)類型的方法,這里應該比較少見。應該就是不同的方法但是都有相同的參數(shù)
if (existing instanceof Method) {
if (!checkAddWithMethodSignature((Method) existing, eventType)) {
// Paranoia check
throw new IllegalStateException();
}
// Put any non-Method object to "consume" the existing Method
anyMethodByEventType.put(eventType, this);
}
return checkAddWithMethodSignature(method, eventType);
}
}
SubscriberMethodFinder.java:
private boolean checkAddWithMethodSignature(Method method, Class<?> eventType) {
methodKeyBuilder.setLength(0);
methodKeyBuilder.append(method.getName());
methodKeyBuilder.append('>').append(eventType.getName());
// 名稱拼接 methodName>eventTypeName這種形式
// 如果有相同方法名稱的話,那么需要將原methodClassOld保存
// 這里可以理解為方法的重寫,子類重寫了可以手動調(diào)用(super.method()),而此時只保留子類中的方法
String methodKey = methodKeyBuilder.toString();
Class<?> methodClass = method.getDeclaringClass();
Class<?> methodClassOld = subscriberClassByMethodKey.put(methodKey, methodClass);
if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) {
// Only add if not already found in a sub class
return true;
} else {
// Revert the put, old class is further down the class hierarchy
subscriberClassByMethodKey.put(methodKey, methodClassOld);
return false;
}
}
從上面所講的可以知道,這個遍歷是從子類到其父類的一個遍歷過程。這里假設當前Activity中有兩個相同參數(shù)的方法a(Object)
和b(Object)
,并且其中一個方法是覆蓋了父類的a(Object)
方法,而且父類的這個a(Object)
方法也有注解@Subscribe
。
當?shù)谝淮翁砑恿四硞€類型的參數(shù),其添加值為b(Object)
,此時existing
還為null
。
接著會添加a(Object)
方法,此時existing
存在,并且為第一次添加的方法b(Object)
,所以此時進入if (!checkAddWithMethodSignature((Method) existing, eventType))
代碼塊,這里面第一次并沒有并且將b(Object)
添加到subscriberClassByMethodKey
中,此時subscriberClassByMethodKey
保存了b(Object)
。并且anyMethodByEventType.put(eventType, this)
將findState對象作為該參數(shù)類型的值。通過這里來判斷有多個需要訂閱方法具有相同的參數(shù)類型。
接著又會調(diào)用checkAddWithMethodSignature
方法,將a(Object)
添加到subscriberClassByMethodKey
中。
當添加其父類的a(Object)
時,此時methodClassOld
存在并且是當前methodClass
的子類methodClassOld.isAssignableFrom(methodClass)
返回false
,將原methodClassOld
和methodKey
重新保存到subscriberClassByMethodKey
中,并返回false,告訴上級,這里是其父類的方法,不需要根據(jù)這里的threadMode來添加。
這里判斷確實挺復雜,但是可以從這里看出寫一個框架的嚴謹性,所以情況都需要考慮到。
最后是將方法返回以及findState
的回收:
SubscriberMethodFinder.java:
private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {
// 創(chuàng)建List并返回
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;
}
現(xiàn)在需要訂閱的方法已經(jīng)找到,接下來我們需要將方法訂閱:
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
// 參數(shù)類型
Class<?> eventType = subscriberMethod.eventType;
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
// subscriptionsByEventType檢查是否存在
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
// 已經(jīng)存在的話,拋出異常
if (subscriptions.contains(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
}
}
//優(yōu)先級排序
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;
}
}
// 把對象和subscribedEvents保存起來
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
// 添加新的參數(shù)類型
subscribedEvents.add(eventType);
// 如果是粘性
if (subscriberMethod.sticky) {
// 構(gòu)造是默認為true
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();
// 參數(shù)類型是存儲的父類或者相同
if (eventType.isAssignableFrom(candidateEventType)) {
Object stickyEvent = entry.getValue();
// 如果有值的話發(fā)送事件
// 意味著如果粘性事件的參數(shù)類型存在,并且對應的值也存在的話將發(fā)送事件
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
- 訂閱的過程是一個對方法的遍歷過程,每個方法需要單獨訂閱。如果訂閱過程中該方法已經(jīng)訂閱,則拋出異常。
- 根據(jù)優(yōu)先級對訂閱的方法進行添加。
- 把訂閱者和訂閱方法的參數(shù)類型保存起來,需要時則可以根據(jù)類型來調(diào)用訂閱的方法。
- 檢查訂閱方法的是否為粘性方法,如果是的話,則會調(diào)用此方法。也就是說粘性方法在訂閱的時候會如果有粘性事件,那么會調(diào)用一次該訂閱的方法。
整個register的過程結(jié)束。其中最重要的還是將訂閱者和其訂閱方法的參數(shù)類型保存、訂閱方法參數(shù)類型和訂閱的方法保存以及對粘性方法的調(diào)用。
看完了register的過程,我想對應unregister的過程也應該有了猜想。嗯~沒錯,就是將保存的數(shù)據(jù)移除即可。下面簡單看下:
public synchronized void unregister(Object subscriber) {
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
if (subscribedTypes != null) {
for (Class<?> eventType : subscribedTypes) {
unsubscribeByEventType(subscriber, eventType);
}
// 將訂閱者訂閱的方法參數(shù)類型列表移除,防止內(nèi)存泄漏
typesBySubscriber.remove(subscriber);
} else {
Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
/** Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber. */
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
// 獲得保存此參數(shù)類型的Subscription
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);
// 如果Subscription的訂閱者就是取消訂閱這個對象,那么將其移除
if (subscription.subscriber == subscriber) {
subscription.active = false;
subscriptions.remove(i);
i--;
size--;
}
}
}
}
這些操作就是對訂閱者所訂閱的方法的移除,防止內(nèi)存泄漏。
3. post做了什么
訂閱完成后如果想要發(fā)送事件的話,一般使用
EventBus.getDefault().post(Object);
即可,這里看下post()
的源碼:
public void post(Object event) {
// 使用ThreadLocal來確保線程中局部變量PostingThreadState
PostingThreadState postingState = currentPostingThreadState.get();
// 獲取事件隊列
List<Object> eventQueue = postingState.eventQueue;
// 將最新的事件添加到隊尾
eventQueue.add(event);
// 如果當前沒有進行中的事件
if (!postingState.isPosting) {
// 設置當前線程是否為主線程
postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper();
// 設置正在發(fā)送狀態(tài)
postingState.isPosting = true;
if (postingState.canceled) {
throw new EventBusException("Internal error. Abort state was not reset");
}
try {
// 隊列循環(huán),將所有事件全部執(zhí)行完成
while (!eventQueue.isEmpty()) {
// 發(fā)送事件
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
// 使用ThreadLocal來確保線程中局部變量PostingThreadState
private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
@Override
protected PostingThreadState initialValue() {
return new PostingThreadState();
}
};
final static class PostingThreadState {
final List<Object> eventQueue = new ArrayList<Object>();
boolean isPosting;
boolean isMainThread;
Subscription subscription;
Object event;
boolean canceled;
}
在調(diào)用post
方法時,會通過使用ThreadLocal
來確保線程中存在一個PostingThreadState
局部變量,關于ThreadLocal
Handler中也使用了ThreadLocal
,有興趣可以看下。接著將此次事件加入到PostingThreadState
的eventQueue
中,并且隊列循環(huán),依次調(diào)用postSingleEvent
發(fā)送處于隊首的事件。
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++) {
// 對查找后的結(jié)果遍歷
Class<?> clazz = eventTypes.get(h);
// 為每個class發(fā)送事件
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));
}
}
}
在postSingleEvent
中,根據(jù)參數(shù)類型在緩存中查找此參數(shù)類型的所有類包括接口(沒有的話會放入緩存,并將類返回),遍歷這些類,調(diào)用postSingleEventForEventType
方法:
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
// 根據(jù)事件類型獲得Subscription列表
subscriptions = subscriptionsByEventType.get(eventClass);
}
if (subscriptions != null && !subscriptions.isEmpty()) {
// 遍歷
for (Subscription subscription : subscriptions) {
// 事件賦值以及訂閱賦值(postingState.subscription = subscription)
postingState.event = event;
postingState.subscription = subscription;
boolean aborted = false;
try {
// 發(fā)送事件
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;
}
根據(jù)傳入的參數(shù)類型從subscriptionsByEventType
中獲取訂閱信息CopyOnWriteArrayList<Subscription> subscriptions
,如果存在的話,對postingState
的event
和subscription
賦值,并且調(diào)用postToSubscription
方法,最后將event
和subscription
置為null
:
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
// 根據(jù)訂閱方法的threadMode來判斷如何執(zhí)行
switch (subscription.subscriberMethod.threadMode) {
// 默認,在哪個線程發(fā)送在哪個線程執(zhí)行
case POSTING:
invokeSubscriber(subscription, event);
break;
// 最終執(zhí)行在主線程中
case MAIN:
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
mainThreadPoster.enqueue(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);
}
}
根據(jù)訂閱信息的線程模型來判斷在哪個線程執(zhí)行此訂閱方法,下面看一個BackgroundPoster
后臺線程發(fā)送器吧:
BackgroundPoster.java:
final class BackgroundPoster implements Runnable {
private final PendingPostQueue queue;
private final EventBus eventBus;
private volatile boolean executorRunning;
BackgroundPoster(EventBus eventBus) {
this.eventBus = eventBus;
queue = new PendingPostQueue();
}
public void enqueue(Subscription subscription, Object event) {
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
synchronized (this) {
// 入隊操作
queue.enqueue(pendingPost);
if (!executorRunning) {
executorRunning = true;
// 這里用到了線程池去執(zhí)行
eventBus.getExecutorService().execute(this);
}
}
}
@Override
public void run() {
try {
try {
// 出隊,執(zhí)行隊列中所有的事件
while (true) {
PendingPost pendingPost = queue.poll(1000);
if (pendingPost == null) {
synchronized (this) {
// Check again, this time in synchronized
// 再次檢測,如果還沒有則執(zhí)行結(jié)束
pendingPost = queue.poll();
if (pendingPost == null) {
executorRunning = false;
return;
}
}
}
// 存在PendingPost對象,調(diào)用EventBus去執(zhí)行訂閱者訂閱的方法
eventBus.invokeSubscriber(pendingPost);
}
} catch (InterruptedException e) {
Log.w("Event", Thread.currentThread().getName() + " was interruppted", e);
}
} finally {
executorRunning = false;
}
}
}
// executorService的默認值是下面的線程池
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
BackgroundPoster這里面維護了一個隊列,如果此發(fā)送器正在執(zhí)行,則將此次事件的PendingPost
(將要發(fā)送的事件)對象加入隊列的尾部,在run
方法中通過PendingPostQueue.poll()
方法獲得第一個PendingPost
對象并且調(diào)用eventBus.invokeSubscriber(pendingPost)
去執(zhí)行訂閱的方法。
EventBus.java:
void invokeSubscriber(PendingPost pendingPost) {
Object event = pendingPost.event;
Subscription subscription = pendingPost.subscription;
PendingPost.releasePendingPost(pendingPost);
if (subscription.active) {
// 如果沒有取消注冊,這個變量是volatile修飾,每次都會取最新值
// 調(diào)用invokeSubscriber方法執(zhí)行訂閱的方法
invokeSubscriber(subscription, event);
}
}
EventBus.java:
void invokeSubscriber(Subscription subscription, Object event) {
try {
// 執(zhí)行訂閱者訂閱的方法,參數(shù)是event
subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
} catch (InvocationTargetException e) {
handleSubscriberException(subscription, event, e.getCause());
} catch (IllegalAccessException e) {
throw new IllegalStateException("Unexpected exception", e);
}
}
到這里后臺線程執(zhí)行任務到這里結(jié)束,其他的最終也是調(diào)用invokeSubscriber
去執(zhí)行訂閱的方法,殊途同歸,不多贅述。
4. 總結(jié)
EventBus的源碼挺簡單的,雖然簡單,但是涉及的知識也挺多的,處理了同步問題,而且各種檢測也是很細致。自己也根據(jù)原理寫過一個框架,雖然代碼少,但是咱們問題多啊!這里還是再說下原理:將訂閱者和訂閱的方法以及方法參數(shù)保存,根據(jù)發(fā)送的事件類型即可獲得所有訂閱此參數(shù)類型的訂閱者和方法,最后可以通過Method.invok執(zhí)行方法。
附美女一張~