EventBus1.0.1源码解析
很久沒有更新過源碼解析類文章,以下內(nèi)容作為源碼分析類的筆記。分析方法適用于其它源碼分析。
分析工具說明
許久以來,閱讀源代碼最得力的工具就非Source Insight莫屬了。然,后來微軟出了一款輕量級但功能強(qiáng)大的IDE就沒Source Insight什么事了。微軟的這款I(lǐng)DE就是大名鼎鼎的VSCODE,全名叫:Visual Studio Code。它的強(qiáng)大之處這里就不過多描述了。我們轉(zhuǎn)回到正題。
獲取源碼并導(dǎo)入IDE
我們在Github.com上搜索到EventBus的項(xiàng)目地址,然后進(jìn)行克隆即可。
git clone https://github.com/greenrobot/EventBus.git克隆完成之后,導(dǎo)入IDE。打開VS CODE,在菜單欄中選擇File -> Open,選擇克隆后文件所在的目錄,然后點(diǎn)擊確認(rèn)。這就完成了代碼導(dǎo)入工作。
由于我們只是分析EventBus的骨架脈絡(luò),所以我們要選擇EventBus的早期版本。這里我們將要選擇V1.0.1版。這同樣適用于Android源代碼分析,適宜選擇1.5版或1.6版。
切換版本的步驟為:先進(jìn)入EventBus剛才的克隆目錄,然后執(zhí)行以下步驟:
//列出EventBus項(xiàng)目的所有tag。sahadevs-MacBook-Pro:EventBus sahadev$ git tagV1.0.1V2.2.0V2.3.0V2.4.0V3.0.0//上面可以看出EventBus共發(fā)布了5個(gè)版本。我們切換到V1.0.1版。sahadevs-MacBook-Pro:EventBus sahadev$ git checkout V1.0.1經(jīng)過以上步驟,我們的代碼就切換到了V1.0.1版本。VS CODE中的內(nèi)容也會(huì)同步更新。接下來開始我們的代碼分析。
事件監(jiān)聽注冊
我們熟知EventBus的用法,其中最有代表性的入口為:
EventBus.getDefault().register(this);為了方便分析,上面這行代碼放在Activity中執(zhí)行。所以上面的this就是Activity的實(shí)例。EventBus采用了單例模式,在這個(gè)版本中,采用了最簡便的實(shí)現(xiàn)方式:
public class EventBus {private static final EventBus defaultInstance = new EventBus();public static EventBus getDefault() {return defaultInstance;} }好,以上并不是我們的重點(diǎn)。我們要從register方法說起。
public void register(Object subscriber) {register(subscriber, defaultMethodName, ThreadMode.PostThread);}public void register(Object subscriber, String methodName, ThreadMode threadMode) {List<Method> subscriberMethods = findSubscriberMethods(subscriber.getClass(), methodName);for (Method method : subscriberMethods) {Class<?> eventType = method.getParameterTypes()[0];subscribe(subscriber, method, eventType, threadMode);}}register(this)方法執(zhí)行時(shí)調(diào)用了register(Object subscriber, String methodName, ThreadMode threadMode),subscriber是Activity,methodName采用了defaultMethodName的值,這個(gè)值在EventBus中定義為:’onEvent’,threadMode為ThreadMode.PostThread。其中ThreadMode為一個(gè)枚舉,它有兩個(gè)值:
public enum ThreadMode {/** Subscriber will be called in the same thread, which is posting the event. */PostThread,/** Subscriber will be called in Android's main thread (sometimes referred to as UI thread). */MainThread,/* BackgroundThread */}PostThread用于標(biāo)識(shí)發(fā)送線程,MainThread用于標(biāo)識(shí)主線程。它們在后期執(zhí)行的區(qū)別為:如果在注冊時(shí)為PostThread,則事件發(fā)送后,會(huì)直接調(diào)起監(jiān)聽方法。而如果注冊時(shí)為MainThread,則會(huì)經(jīng)過主線程的消息隊(duì)列調(diào)起。
好,回到剛才的地方,在register(Object subscriber, String methodName, ThreadMode threadMode)方法內(nèi)先執(zhí)行了findSubscriberMethods,我們移步看看這個(gè)方法內(nèi)執(zhí)行了什么工作:
private List<Method> findSubscriberMethods(Class<?> subscriberClass, String methodName) {...List<Method> subscriberMethods;...subscriberMethods = new ArrayList<Method>();Class<?> clazz = subscriberClass;HashSet<Class<?>> eventTypesFound = new HashSet<Class<?>>();while (clazz != null) {String name = clazz.getName();...Method[] methods = clazz.getDeclaredMethods();for (Method method : methods) {if (method.getName().equals(methodName)) {Class<?>[] parameterTypes = method.getParameterTypes();if (parameterTypes.length == 1) {if (eventTypesFound.add(parameterTypes[0])) {// Only add if not already found in a sub classsubscriberMethods.add(method);}}}}clazz = clazz.getSuperclass();}...}這里面的代碼比較長,將不必要的略去。這段代碼主要做了以下工作:
根據(jù)給定的類,遍歷該類及其父類所有的方法,如果遍歷時(shí)發(fā)現(xiàn)與給定的方法名相等,并且該方法的參數(shù)只有一個(gè),那么就將該方法加入到subscriberMethods中,最后返回。這里被找到的方法是同名的,但是參數(shù)類型不同,這里的參數(shù)類型決定了將來要監(jiān)聽的事件類型。
好,我們回到findSubscriberMethods之前被調(diào)用的地方,繼續(xù)往下走。接下來的動(dòng)作是將findSubscriberMethods方法返回的Method列表進(jìn)行遍歷。而這次遍歷時(shí),將Method的第一個(gè)參數(shù)類型作為參數(shù)傳給了subscribe方法:
private void subscribe(Object subscriber, Method subscriberMethod, Class<?> eventType, ThreadMode threadMode) {CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);if (subscriptions == null) {subscriptions = new CopyOnWriteArrayList<Subscription>();subscriptionsByEventType.put(eventType, subscriptions);} ...subscriberMethod.setAccessible(true);Subscription subscription = new Subscription(subscriber, subscriberMethod, threadMode);subscriptions.add(subscription);...}該方法首先做的就是根據(jù)eventType從subscriptionsByEventType對象中取出對應(yīng)的subscriptions,然這里第一次肯定是null。所以先進(jìn)行實(shí)例化,然后存放于subscriptionsByEventType中。而subscriptionsByEventType對象存放的就是根據(jù)eventType所對應(yīng)的subscriptions。而subscriptions中存放的就是所有的觀察者,待會(huì)有時(shí)間發(fā)生時(shí),根據(jù)事件的類型,從subscriptionsByEventType中取出所有的觀察者subscriptions逐一通知。
在創(chuàng)建了觀察者列表subscriptions之后開始創(chuàng)建具體的觀察者。Subscription是一個(gè)基本的類,它的示例只是持有了subscriber(Activity),subscriberMethod(onEvent),threadMode(ThreadMode.PostThread)三者的引用而已。
到這里,觀察者Activity監(jiān)聽某一事件的注冊工作就算完成了。
事件發(fā)送
接下來,我們將從事件發(fā)送入口post方法開始分析:
public void post(Object event) {//這里的兩個(gè)屬性很重要,下面這個(gè)用于緩存事件,待下次事件執(zhí)行時(shí),一并執(zhí)行。List<Object> eventQueue = currentThreadEventQueue.get();eventQueue.add(event);//這個(gè)用于標(biāo)識(shí)是否正在發(fā)送事件,保證事件的順序發(fā)送。防止多線程情況下的交叉執(zhí)行。BooleanWrapper isPosting = currentThreadIsPosting.get();if (isPosting.value) {return;} else {isPosting.value = true;try {while (!eventQueue.isEmpty()) {postSingleEvent(eventQueue.remove(0));}} finally {isPosting.value = false;}}}這里最開始有兩個(gè)EventBus的成員屬性,這里很重要:currentThreadEventQueue與currentThreadIsPosting。這兩個(gè)屬性本身是和線程有關(guān)系的。關(guān)于這兩個(gè)屬性內(nèi)部的作用機(jī)制這里就不再贅述了。上面post方法的第一行currentThreadEventQueue.get()返回的是一個(gè)空的ArrayList()對象。而currentThreadIsPosting.get()方法返回的是一個(gè)BooleanWrapper對象,它的默認(rèn)屬性value默認(rèn)是false。如果對這塊的返回值有疑問,可以在EventBus.java文件內(nèi)找到這兩個(gè)對象的定義,它們在new時(shí),通過initialValue方法返回了get獲取到的初始值。
通過判斷,最終進(jìn)入postSingleEvent方法內(nèi),而該方法則是事件分發(fā)的核心:
private void postSingleEvent(Object event) throws Error {List<Class<?>> eventTypes = findEventTypes(event.getClass());...int countTypes = eventTypes.size();for (int h = 0; h < countTypes; h++) {Class<?> clazz = eventTypes.get(h);CopyOnWriteArrayList<Subscription> subscriptions;synchronized (this) {subscriptions = subscriptionsByEventType.get(clazz);}if (subscriptions != null) {for (Subscription subscription : subscriptions) {if (subscription.threadMode == ThreadMode.PostThread) {postToSubscribtion(subscription, event);} else if (subscription.threadMode == ThreadMode.MainThread) {mainThreadPoster.enqueue(event, subscription);} else {throw new IllegalStateException("Unknown thread mode: " + subscription.threadMode);}}...}}...}這個(gè)方法內(nèi)首先調(diào)用了findEventTypes方法,我們先移步看看它做了什么工作:
private List<Class<?>> findEventTypes(Class<?> eventClass) {...Class<?> clazz = eventClass;while (clazz != null) {eventTypes.add(clazz);addInterfaces(eventTypes, clazz.getInterfaces());clazz = clazz.getSuperclass();}return eventTypes;}這部分的主要工作為:將指定類以及父類所實(shí)現(xiàn)的接口及其本身的類型加入到eventTypes中,最后將eventTypes返回。如果我們在定義事件類型時(shí),沒有繼承任何類、實(shí)現(xiàn)任何接口,那么就會(huì)省去這個(gè)步驟,節(jié)省時(shí)間。
回到postSingleEvent方法繼續(xù)往下看:根據(jù)剛剛返回的事件類型集合開始遍歷,每次遍歷時(shí),根據(jù)給定的事件類型從subscriptionsByEventType中尋找對應(yīng)的監(jiān)聽者。我們在上面分析注冊部分時(shí)候已經(jīng)見過,subscriptionsByEventType中存放的就是事件類型與監(jiān)聽者的鍵值對。找到監(jiān)聽者列表subscriptions之后,開始對subscriptions進(jìn)行遍歷,進(jìn)行消息下發(fā)。
消息的發(fā)送主要兩種模式:
- PostThread
- MainThread
我們在開頭的時(shí)候就提到過,PostThread會(huì)通過反射直接調(diào)起對應(yīng)的方法,而MainThread則會(huì)通過主線程的消息隊(duì)列進(jìn)行調(diào)起。我們來看看究竟是不是這樣:
//PostThread的執(zhí)行方式static void postToSubscribtion(Subscription subscription, Object event) throws Error {...subscription.method.invoke(subscription.subscriber, event);...}//MainThread的執(zhí)行方式void enqueue(Object event, Subscription subscription) {PendingPost pendingPost = PendingPost.obtainPendingPost(event, subscription);Message message = obtainMessage();message.obj = pendingPost;if (!sendMessage(message)) {throw new RuntimeException("Could not send handler message");}}可以看到:對于PostThread類型,直接通過反射將事件傳給了觀察者。而對于MainThread類型,則是通過Handler將信息傳給了主線程。最后消息到達(dá)主線程后,通過與PostThread方式一樣經(jīng)過postToSubscribtion方法執(zhí)行。
以上就是關(guān)于EventBus事件監(jiān)聽注冊、事件廣播的解析過程。
總結(jié)
以上是生活随笔為你收集整理的EventBus1.0.1源码解析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 新研究表明 深层神经网络的功能存在局限性
- 下一篇: “猜心思”的Hard模式:问答系统在智能