while (clazz != null) {
String name = clazz.getName();
if (name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("android.")) {
// Skip system classes, this just degrades performance
break;
}
// Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again)
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
String methodName = method.getName();
if (methodName.startsWith(ON_EVENT_METHOD_NAME)) {
int modifiers = method.getModifiers();
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
Class>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length == 1) {
String modifierString = methodName.substring(ON_EVENT_METHOD_NAME.length());
ThreadMode threadMode;
if (modifierString.length() == 0) {
threadMode = ThreadMode.PostThread;
} else if (modifierString.equals("MainThread")) {
threadMode = ThreadMode.MainThread;
} else if (modifierString.equals("BackgroundThread")) {
threadMode = ThreadMode.BackgroundThread;
} else if (modifierString.equals("Async")) {
threadMode = ThreadMode.Async;
} else {
if (skipMethodVerificationForClasses.containsKey(clazz)) {
continue;
} else {
throw new EventBusException("Illegal onEvent method, check for typos: " + method);
}
}
Class> eventType = parameterTypes[0];
methodKeyBuilder.setLength(0);
methodKeyBuilder.append(methodName);
methodKeyBuilder.append(>).append(eventType.getName());
String methodKey = methodKeyBuilder.toString();
if (eventTypesFound.add(methodKey)) {
// Only add if not already found in a sub class
subscriberMethods.add(new SubscriberMethod(method, threadMode, eventType));
}
}
} else if (!skipMethodVerificationForClasses.containsKey(clazz)) {
Log.d(EventBus.TAG, "Skipping method (not public, static or abstract): " + clazz + "."
- methodName);
}
}
}
clazz = clazz.getSuperclass();
}
if (subscriberMethods.isEmpty()) {
throw new EventBusException("Subscriber " + subscriberClass + " has no public methods called "
- ON_EVENT_METHOD_NAME);
} else {
synchronized (methodCache) {
methodCache.put(key, subscriberMethods);
}
return subscriberMethods;
}
}
上面就是獲取傳入對象class的方法的方法。其中前半部分是先去緩存查找是否有這個類的記錄,如果有直接返回,沒有繼續執行。當沒有時繼續走到Method[] methods = clazz.getDeclaredMethods();
語句得到該類的所有方法;接著那個大for循環就是遍歷這個類匹配符合封裝要求的method;其中,if (methodName.startsWith(ON_EVENT_METHOD_NAME))
用來判斷方法名是不是以“onEvent”開頭;接著if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0)
用于繼續判斷是否是public且非static和abstract方法;if (parameterTypes.length == 1)
用于繼續判斷是否是一個參數。如果都復合,才進入封裝的部分;接著也比較簡單,根據方法的后綴,來確定threadMode,threadMode是個四種情況的枚舉類型(前面基礎使用一篇解釋過四種類型);接著通過subscriberMethods.add(new SubscriberMethod(method, threadMode, eventType));
把method添加到subscriberMethods列表;接著通過clazz = clazz.getSuperclass();
掃描父類的方法;接著while結束,掃描完后通過methodCache.put(key, subscriberMethods);
將方法放入緩存,然后返回List<SubscriberMethod>
的方法列表(訂閱者方法至此查找完成)。
接著繼續回到上一級方法:
private synchronized void register(Object subscriber, boolean sticky, int priority) {
List
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod, sticky, priority);
}
}
通過for循環遍歷List<SubscriberMethod>
里的方法,同時傳入suscribe方法。具體如下:
// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority) {
//從訂閱方法中拿到訂閱事件的類型
Class> eventType = subscriberMethod.eventType;
通過訂閱事件類型,找到所有的訂閱(Subscription)
CopyOnWriteArrayList
//創建一個新的訂閱
Subscription newSubscription = new Subscription(subscriber, subscriberMethod, priority);
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);
}
}
// Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again)
// subscriberMethod.method.setAccessible(true);
int size = subscriptions.size();
for (int i = 0; i <= size; i++) {
//根據優先級插入訂閱
if (i == size || newSubscription.priority > subscriptions.get(i).priority) {
subscriptions.add(i, newSubscription);
break;
}
}
//根據subscriber存儲它所有的eventType
List
if (subscribedEvents == null) {
subscribedEvents = new ArrayList
typesBySubscriber.put(subscriber, subscribedEvents);
}
//將這個訂閱事件加入到訂閱者的訂閱事件列表中
subscribedEvents.add(eventType);
//判斷sticky;如果為true,從stickyEvents中根據eventType去查找有沒有stickyEvent,如果有則立即發布去執行。stickyEvent其實就是我們post時的參數
if (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
Set
for (Map.Entry
Class> candidateEventType = entry.getKey();
if (eventType.isAssignableFrom(cand
《Android學習筆記總結+最新移動架構視頻+大廠安卓面試真題+項目實戰源碼講義》
【docs.qq.com/doc/DSkNLaERkbnFoS0ZF】 完整內容開源分享
idateEventType)) {
Object stickyEvent = entry.getValue();
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
到這里register方法分析完了,大致流程總結一下:
找到被注冊者類中的所有的訂閱方法。
遍歷訂閱方法,找到EventBus中eventType對應的訂閱列表,然后根據當前訂閱者和訂閱方法創建一個新的訂閱加入到訂閱列表。
- 找到EvnetBus中subscriber訂閱的事件列表,將eventType加入到這個事件列表。
所以對于任何一個訂閱者,我們可以找到它的訂閱事件類型列表,通過這個訂閱事件類型,可以找到在訂閱者中的訂閱函數。
既然register函數分析完了,那么接下來就該分析unregister了,成對出現嘛!如下:
/* Unregisters the given subscriber from all event classes. /
public synchronized void unregister(Object subscriber) {
List
if (subscribedTypes != null) {
for (Class> eventType : subscribedTypes) {
unubscribeByEventType(subscriber, eventType);
}
typesBySubscriber.remove(subscriber);
} else {
Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
可以看到,首先獲取了subscribe函數中根據subscriber存儲它的所有eventType保存到List<Class<?>> subscribedTypes
;接著如果存在注冊過的type則通過unubscribeByEventType(subscriber, eventType);
循環遍歷,完事remove掉所有,這樣就完成了所有的unregister功能;至于unubscribeByEventType函數如何實現,具體如下:
/* Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber. /
private void unubscribeByEventType(Object subscriber, Class> eventType) {
List
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--;
}
}
}
}
從上面代碼可以看出,原來在register時真正存儲EventBus事件的Map是subscriptionsByEventType成員。這里就是循環遍歷找出需要unregister的remove掉。至此,整個EventBus的register與unregister函數都分析完畢。
依照前一篇使用來看,進行完register與unregister后剩下的就是post了,那么接下來分析分析post過程,如下:
/* Posts the given event to the event bus. /
public void post(Object event) {
PostingThreadState postingState = currentPostingThreadState.get();
List
eventQueue.add(event);
if (!postingState.isPosting) {
postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper();
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;
}
}
}
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);
}
如上postSticky(Object event)
的實質是post了一個stickyEvents,而真正的post(Object event)
方法里,currentPostingThreadState是一個ThreadLocal類型的,里面存儲了PostingThreadState;PostingThreadState包含了一個eventQueue和一些標志位;eventQueue.add(event);
就是把事件放入eventQueue隊列,然后while循環遍歷eventQueue通過postSingleEvent(eventQueue.remove(0), postingState);語句分發事件;那繼續看下這條語句的實現:
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class> eventClass = event.getClass();
boolean subscriptionFound = false;
if (eventInheritance) {
List
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) {
Log.d(TAG, "No subscribers registered for event " + eventClass);
}
if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
eventClass != SubscriberExceptionEvent.class) {
post(new NoSubscriberEvent(this, event));
}
}
}
如上代碼通過List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
語句傳入eventClass得到eventClass對應的事件,包含父類對應的事件和接口對應的事件;接著通過循環遍歷eventTypes執行subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
語句;最后如果發現沒有對應事件就通過post(new NoSubscriberEvent(this, event));
post一個NoSubscriberEvent事件;接下來看下postSingleEventForEventType函數的實現:
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class> eventClass) {
CopyOnWriteArrayList
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;
}
如上可以發現,我們在register時掃面class把匹配的方法都存儲在了subscriptionsByEventType
,這里通過subscriptions = subscriptionsByEventType.get(eventClass);
語句首先拿到register時掃描的匹配方法;然后判斷是否有匹配的方法,如果有就繼續遍歷每個subscription,依次去調用postToSubscription(subscription, event, postingState.isMainThread);
;
其實這個方法在register的subscribe的checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent)
的方法中也調運過。所以我們繼續來分析下這個方法,如下:
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
case PostThread:
invokeSubscriber(subscription, event);
break;
case MainThread:
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
mainThreadPoster.enqueue(subscription, event);
}
break;
case BackgroundThread:
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);
}
}
這個方法傳入的三個參數含義分別是:第一個參數就是傳入的訂閱,第二個參數就是對于的分發事件,第三個參數表明是否在主線程;然后通過subscription.subscriberMethod.threadMode
判斷該在哪個線程去執行;這里通過switch分四種情況,如下:
case PostThread:直接在當前線程反射調用。
case MainThread:如果是(isMainThread)主UI線程則直接調用,否則把當前的方法加入到隊列,然后直接通過handler去發送一個消息,通過Handler在主線程執行。
case BackgroundThread:如果當前不是主UI線程(!isMainThread)則直接調用,如果是UI線程則創建一個runnable加入到后臺的一個隊列,最終由Eventbus中的一個線程池去調用。
case Async:不論什么線程,直接丟入線程池,也就是將任務加入到后臺的一個隊列,最終由Eventbus中的一個線程池去調用;線程池與BackgroundThread用的是同一個。
- default:拋出線程state狀態非法異常。
繼續分析可以發現mainThreadPoster是繼承Handler實現的,其中Looper是MainLooper;invokeSubscriber與asyncPoster都是繼承Runnable實現的,其中invokeSubscriber與asyncPoster的enqueue方法實質都差不多,如下:
public void enqueue(Subscription subscription, Object event) {
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
queue.enqueue(pendingPost);
eventBus.getExecutorService().execute(this);
}
可以驗證上面說的,invokeSubscriber與asyncPoster的enqueue方法都是扔到了一個線程池中執行。好了,繼續看下mainThreadPoster的enqueue方法,如下:
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");
}
}
文末
今天關于面試的分享就到這里,還是那句話,有些東西你不僅要懂,而且要能夠很好地表達出來,能夠讓面試官認可你的理解,例如Handler機制,這個是面試必問之題。有些晦澀的點,或許它只活在面試當中,實際工作當中你壓根不會用到它,但是你要知道它是什么東西。
最后在這里小編分享一份自己收錄整理上述技術體系圖相關的幾十套騰訊、頭條、阿里、美團等公司2021年的面試題,把技術點整理成了視頻和PDF(實際上比預期多花了不少精力),包含知識脈絡 + 諸多細節,由于篇幅有限,這里以圖片的形式給大家展示一部分。
還有?高級架構技術進階腦圖、Android開發面試專題資料,高級進階架構資料 幫助大家學習提升進階,也節省大家在網上搜索資料的時間來學習,也可以分享給身邊好友一起學習。
【Android核心高級技術PDF文檔,BAT大廠面試真題解析】
【算法合集】
【延伸Android必備知識點】
【Android部分高級架構視頻學習資源】
Android精講視頻領取學習后更加是如虎添翼!進軍BATJ大廠等(備戰)!現在都說互聯網寒冬,其實無非就是你上錯了車,且穿的少(技能),要是你上對車,自身技術能力夠強,公司換掉的代價大,怎么可能會被裁掉,都是淘汰末端的業務Curd而已!現如今市場上初級程序員泛濫,這套教程針對Android開發工程師1-6年的人員、正處于瓶頸期,想要年后突破自己漲薪的,進階Android中高級、架構師對你更是如魚得水,趕快領取吧!
本文已被[CODING開源項目:《Android學習筆記總結+移動架構視頻+大廠面試真題+項目實戰源碼》]( )收錄