摘要:第二種是通過方法來檢查,源碼如下通過以及來生成一個,來存儲方法所在的類。先看下的方法繼承了,調用方法后會向事件隊列中插入一個事件,然后將標記位設置為表示正在處理事件,然后調用發送消息通知處理事件。
首先從訂閱開始
EventBus.getDefault().register(this);
register方法會獲取傳入的object對象的class,通過findSubscriberMethods方法來查找這個class中訂閱的方法,如下
public void register(Object subscriber) {
Class<");synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
findSubscriberMethods方法實現如下,其中有一個ConcurrentHashMap類型的靜態對象METHOD_CACHE,是用來緩存對應類的訂閱方法的,以便后續再次訂閱時不用重新去findMethods,可以直接從緩存中讀取。
List findSubscriberMethods(Class<"); {
List 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字段分為兩種方式
第一種findUsingReflection是通過反射來查找,找到被@Subscribe注解修飾的方法,并且根據具體的注解以及方法參數生成一個SubscriberMethod對象:
findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode, subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
第二種findUsingInfo是通過apt的方式,提前找到訂閱的方法,可以避免通過反射查找方法帶來的耗時。
具體使用方法:在gradle配置apt,rebuild項目,會生成一個注解方法索引類,在EventBusBuilder中通過addIndex方法新建一個該類的對象傳入即可。
這邊還有一個問題,對于子類重寫父類的訂閱方法如何處理。在上面的兩種方式中在查找完子類的訂閱方法后都會繼續去查找父類的訂閱方法,都通過一個叫做checkAdd的方法進行支撐,該方法返回true表示可以添加到訂閱方法的集合中去。
boolean checkAdd(Method method, Class<"); {
// 2 level check: 1st level with event type only (fast), 2ndlevelwith complete signature when required.
// Usually a subscriber doesn"t have methods listening to thesameevent type.
Object existing = anyMethodByEventType.put(eventType, method);
if (existing == null) {
return true;
} else {
if (existing instanceof Method) {
if (!checkAddWithMethodSignature((Method)existing,eventType)) {
// Paranoia check
throw new IllegalStateException();
}
// Put any non-Method object to "consume" theexistingMethod
anyMethodByEventType.put(eventType, this);
}
return checkAddWithMethodSignature(method, eventType);
}
}
checkAdd中設置了兩種檢查方式,第一種是通過eventType也就是訂閱方法的入參來檢查,這種方式比較快,只需要看下之前有沒有這種入參的方法就可以了。注釋中也指出了通常一個類不會有多個入參相同的訂閱方法。
第二種是通過checkAddWithMethodSignature方法來檢查,源碼如下:
private boolean checkAddWithMethodSignature(Method method,Class<"); {
methodKeyBuilder.setLength(0);
methodKeyBuilder.append(method.getName());
methodKeyBuilder.append(">").append(eventType.getName());
String methodKey = methodKeyBuilder.toString();
Class<");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 theclasshierarchy
subscriberClassByMethodKey.put(methodKey, methodClassOld);
return false;
}
}
通過method以及eventType來生成一個key,來存儲方法所在的類。其中methodClassOld == null ||methodClassOld.isAssignableFrom(methodClass)這個判斷條件對應著兩種情況,methodClassOld == null說明是入參相同但是方法名不同的方法正在被添加,直接返回true就可以了methodClassOld.isAssignableFrom(methodClass)這個條件是為了過濾掉父類被子類重寫的方法,前面說過了查找訂閱方法是從子類開始遍歷的,此時如果子類重寫了父類的訂閱方法,那么methodClassOld對應的是子類,methodClass對應的是父類,顯然這個判斷就會為false,之后進入下面的else分支return false,也就是忽略掉父類被子類重寫的方法。
查找訂閱方法基本就這么點,查找完畢之后需要執行訂閱操作,也就是register方法中的subscribe(subscriber, subscriberMethod);操作,直接看下該方法的實現:
private void subscribe(Object subscriber, SubscriberMethodsubscriberMethod) {
Class<");new Subscription(subscriber,subscriberMethod);
CopyOnWriteArrayList 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);
}
}
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;
}
}
Listif (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
if (subscriberMethod.sticky) {
if (eventInheritance) {
// Existing sticky events of all subclasses of eventTypehave to be considered.
// Note: Iterating over all events may be inefficientwith lots of sticky events,
// thus data structure should be changed to allow a moreefficient lookup
// (e.g. an additional map storing sub classes of superclasses: Class -> List).
Setfor (Map.Entryif (eventType.isAssignableFrom(candidateEventType)) {
Object stickyEvent = entry.getValue();
checkPostStickyEventToSubscription(newSubscriptin, stickyEvent);
}
}
} else {
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription,stickyEvent);
}
}
}
subscriptionsByEventType這個Map是將eventType作為key保存其所對應的訂閱方法的集合。該方法將剛查找到的方法添加到對應的集合中去,添加時有這樣一層判斷i == size || subscriberMethod.priority >subscriptions.get(i).subscriberMethod.priority這表示這個集合里的方法會按照所設定的優先級進行排序。緊接著又出現了個MaptypesBySubscriber將訂閱者作為key保存一個Class的集合,暫時看不出有啥用,就先不管,最后再檢查下是不是粘性事件,如果是粘性事件就根據所保存的粘性事件來執行該方法。eventInheritance也是在bulider中設置的,如果為true則會考慮事件的繼承性,如果現在有eventType為正在訂閱的方法的eventType的子類的粘性事件存在,那么這個粘性事件也會被正在訂閱的方法接收到,直接說可能比較繞,舉個栗子,現在我有兩個事件,其中一個是另一個的子類,并且有兩個粘性訂閱方法,如下:
class EventMessage {
}
class SubEventMessage extends EventMessage {
}
@Subscribe(sticky = true)
public void onEvent(EventMessage message) {
// do something
}
@Subscribe(sticky = true)
public void onSubEvent(SubEventMessage message) {
// do something
}
當執行register時,如果內存中存在著一個類型為SubEventMessage的事件,那么訂閱的時候onEvent方法會被執行,入參是內存中類型為SubEventMessage的事件。
現在register大致就分析完了,再來看下unregister方法:
public synchronized void unregister(Object subscriber) {
Listif (subscribedTypes != null) {
for (Class<");else {
logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
unregister方法十分簡單,typesBySubscriber是剛才在進行訂閱的時候不知道用來干什么的Map,現在知道是在取消訂閱時用到的,這個Map將訂閱者作為key,將其所有的訂閱方法的eventType存入到對應的List中去,取消訂閱時將這個List取出來,遍歷去移除對應的訂閱方法,具體實現在unsubscribeByEventType中,也十分簡單,就不贅述了。
訂閱和取消訂閱都看過了,還差個發送事件,發送事件分為post和postSticky兩種,先看post:
public void post(Object event) {
PostingThreadState postingState = currentPostingThreadState.get();
List
currentPostingThreadState是個ThreadLocal,然后從中取出當前線程的postingState,也就是說每個線程都會維護一個自己的posting狀態,之后會有個循環將事件隊列清空,通過postSingleEvent方法來進一步處理:
private void postSingleEvent(Object event, PostingThreadStatepostingState) throws Error {
Class<");boolean subscriptionFound = false;
if (eventInheritance) {
Listint countTypes = eventTypes.size();
for (int h = 0; h < countTypes; h++) {
Class<");else {
subscriptionFound = postSingleEventForEventType(event,postingState, eventClass);
}
if (!subscriptionFound) {
if (logNoSubscriberMessages) {
logger.log(Level.FINE, "No subscribers registered forevent " + eventClass);
}
if (sendNoSubscriberEvent && eventClass !=NoSubscriberEvent.class &&
eventClass != SubscriberExceptionEvent.class) {
post(new NoSubscriberEvent(this, event));
}
}
}
同樣是通過eventInheritance來判斷是否要涉及eventType的父類,之后再通過postSingleEventForEventType方法的返回值來得到該事件是否被處理,如果沒有被處理,那么會返回false進入下一個分支,logNoSubscriberMessages和sendNoSubscriberEvents都是在builder中傳入的,前者用于沒有訂閱者處理事件時打印日志,后者用于沒有訂閱者處理事件時發送一個NoSubscriberEvent類型的事件,所以具體是怎么處理事件的還要繼續看postSingleEventForEventType方法:
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<"); {
CopyOnWriteArrayList 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;
}
postSingleEventForEventType方法從subscriptionsByEventType中去獲取對應事件類型的所有訂閱者,如果沒有訂閱者就返回false表示事件沒有被處理,否則就遍歷所有的訂閱者,通過postToSubscription方法來處理事件,接著往里看:
private void postToSubscription(Subscription subscription, Objectevent, 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 notdecoupled 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分別代表什么意思:
Mode | 含義 |
---|---|
POSTING | 在當前線程執行 |
MAIN | 在主線程執行 |
MAIN_ORDERED | 在主線程有序執行 |
BACKGROUND | 在后臺線程執行 |
ASYNC | 在新的線程執行 |
可以看到有幾個差不多,那具體有什么區別呢?直接從代碼里看,先說明幾個東西,invokeSubscriber就是直接調用訂閱方法,還有幾個后綴為poster的變量暫時先理解為調用了enqueue方法后,訂閱方法就會在某個時間被執行,后面再詳細講。
現在可以看代碼了,POSTING沒什么好說的,直接調用invokeSubscriber,也就是說在調用eventBus.post的線程執行。
MAIN和MAIN_ORDERED都是在主線程執行,后者的ORDERED體現在什么地方呢,先看下MAIN的分支,其中通過mainThreadPoster.enqueue插入的事件會在主線程執行,判斷當前線程是否是主線程來決定直接調用訂閱方法還是通過mainThreadPoster來發布,這里應該沒什么疑惑的,主要是MAIN_ORDERED:
if (mainThreadPoster != null) {
mainThreadPoster.enqueue(subscription, event);
} else {
// temporary: technically not correct as poster notdecoupled from subscriber
invokeSubscriber(subscription, event);
}
mainThreadPoster不為空時,通過mainThreadPoster來發布事件,為空時直接調用訂閱方法,說好的在主線程調用呢?這里注釋也說明了是不正確的,實際上mainThreadPoster為空本身就是種異常情況,具體可以看下它的初始化過程,這里就不細說了。所以下面的else分支就先不管了,那么為什么說通過mainThreadPoster發布的事件就是“有序”的呢,實際上mainThreadPoster內部實現是個handler,可以將事件post到主線程中去執行,所以說是有序的,這里簡單說明下原因:
主線程維護著一個消息隊列,循環從里面取出消息來處理,我們知道可以通過view的post方法來獲取它繪制完成之后的寬高,原因是post方法里的事件會被插入到消息隊列的尾部,而view的measure,layout,draw都在新插入的消息的前面,所以當post的方法執行時,view肯定已經繪制好了。
而handler通過sendMessage發送的消息也會被插入到主線程消息隊列的尾部,這就是“有序”,比如現在有一個ImageView,在它的onMeasure中去發布一個事件,如果訂閱方法的模式是MAIN那么會在onMeasure中調用訂閱方法,而如果模式是MAIN_ORDERED那么會在ImageView繪制完成后調用訂閱方法。
再來看下BACKGROUND和ASYNC的區別:
case BACKGROUND:
if (isMainThread) {
backgroundPoster.enqueue(subscription, event);
} else {
invokeSubscriber(subscription, event);
}
break;
case ASYNC:
asyncPoster.enqueue(subscription, event);
break;
其中backgroundPoster和asyncPoster都會開啟一個新線程來執行訂閱方法,暫時當成是一樣的就行,那么區別就是BACKGROUND模式如果在子線程post一個事件,那么會直接在該線程調用訂閱方法,只有在主線程post事件才會開啟一個新線程。而ASYNC模式,不管是在哪post事件,都會開啟一個新線程來調用訂閱方法。
最后再看下幾個poster基本上就看完了,幾個poster都實現了同一個接口Poster:
interface Poster {
/**
* Enqueue an event to be posted for a particular subscription.
*
* @param subscription Subscription which will receive the event.
* @param event Event that will be posted to subscribers.
*/
void enqueue(Subscription subscription, Object event);
}
可以看到里面只有一個需要實現的方法enqueue,是用來插入事件的,這個接口被三個類實現,分別是HandlerPoster,BackgroundPoster和AsyncPoster,上面的mainThreadPoster對應的就是HandlerPoster,這三個類中都有個類型為PendingPostQueue的成員變量,這是個事件隊列,具體實現就不看了,這個隊列提供了入隊和出隊的方法。
先看下HandlerPoster的enqueue方法:
public class HandlerPoster extends Handler implements Poster {
public void enqueue(Subscription subscription, Object event) {
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
synchronized (this) {
queue.enqueue(pendingPost);
if (!handlerActive) {
handlerActive = true;
if (!sendMessage(obtainMessage())) {
throw new EventBusException("Could not send handler message");
}
}
}
}
}
HandlerPoster繼承了Handler,調用enqueue方法后會向事件隊列中插入一個事件,然后將標記位handlerActive設置為true表示正在處理事件,然后調用sendMessage發送消息通知處理事件。PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);這行是用來獲取一個消息隊列的Node用來插入到隊列中去,EventBus維護著一個pool用來保存閑置的Node當有需要時從中取出一個給事件使用,pool不夠用時才會new新的Node出來,具體可以看下PendingPost,這樣做的好處是可以避免頻繁創建對象帶來的開銷。
再看下HandlerPoster的handleMessage方法:
public void handleMessage(Message msg) {
boolean rescheduled = false;
try {
long started = SystemClock.uptimeMillis();
while (true) {
PendingPost pendingPost = queue.poll();
if (pendingPost == null) {
synchronized (this) {
// Check again, this time in synchronized
pendingPost = queue.poll();
if (pendingPost == null) {
handlerActive = false;
return;
}
}
}
eventBus.invokeSubscriber(pendingPost);
long timeInMethod = SystemClock.uptimeMillis() - started;
if (timeInMethod >= maxMillisInsideHandleMessage) {
if (!sendMessage(obtainMessage())) {
throw new EventBusException("Could not send handler message");
}
rescheduled = true;
return;
}
}
} finally {
handlerActive = rescheduled;
}
}
首先會記錄下開始處理事件的時間,然后從事件隊列中取出事件,如果為空就將handlerActive設置為false直接return了,如果不為空,就調用eventBus.invokeSubscriber(pendingPost);來調用訂閱方法,執行完后,再看下時間,如果超出了規定的時間那么重新發送一條消息,本次消息處理結束,等下次輪到自己的時候再處理事件,畢竟不能一直處理隊列里的事件而阻塞了主線程,如果沒有超出規定事件,那么說明還可以有事件可以處理下一個事件,就會再次進入循環。
BackgroundPoster和AsyncPoster其實和HandlerPoster差不多,只是沒有用Handler而是用了線程池去處理事件,具體就不看了。
對了,還有個發送粘性事件:
public void postSticky(Object event) {
synchronized (stickyEvents) {
stickyEvents.put(event.getClass(), event);
}
// Should be posted after it is putted, in case the subscriberwants to remove immediately
post(event);
}
就是在stickyEvents這個map里存一下。
好了,完了。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/7397.html
摘要:進入源碼分析我們從的注冊開始入手。關閉此功能將改善事件的發布。創建線程池我們將此段代碼逐步分析這步主要是進行初始化話一下必要的參數,如代碼注解所示。必須在線程同步中進行。必須保持線程安全的,所以這里使用了。 簡介 前面我學習了如何使用EventBus,還有了解了EventBus的特性,那么接下來我們一起來學習EventBus的源碼,查看EventBus的源碼,看看EventBus給我們...
摘要:音樂團隊分享數據綁定運行機制分析一個項目搞定所有主流架構單元測試一個項目搞定所有主流架構系列的第二個項目。代碼開源,展示了的用法,以及如何使用進行測試,還有用框架對的進行單元測試。 Android 常用三方框架的學習 Android 常用三方框架的學習 likfe/eventbus3-intellij-plugin AS 最新可用 eventbus3 插件,歡迎品嘗 簡單的 MVP 模...
閱讀 3528·2023-04-25 20:09
閱讀 3733·2022-06-28 19:00
閱讀 3053·2022-06-28 19:00
閱讀 3071·2022-06-28 19:00
閱讀 3160·2022-06-28 19:00
閱讀 2870·2022-06-28 19:00
閱讀 3031·2022-06-28 19:00
閱讀 2628·2022-06-28 19:00