EventBus源码分析
簡介
前面我學(xué)習(xí)了如何使用EventBus,還有了解了EventBus的特性,那么接下來我們一起來學(xué)習(xí)EventBus的源碼,查看EventBus的源碼,看看EventBus給我們帶來什么驚喜以及編程思想。
這個圖我們從一開始就一直放置在上面了。我們在來回顧一下,EventBus的官網(wǎng)是怎么定義它的呢?
EventBus是Android和Java的發(fā)布/訂閱(觀察者模式)事件總線。
我們大概了解了EventBus的構(gòu)建思想。接下來我們進入源碼學(xué)習(xí)吧。
進入源碼分析
我們從EventBus的注冊開始入手。
EventBus.getDefault().register(this); public static EventBus getDefault() {if (defaultInstance == null) {synchronized (EventBus.class) {if (defaultInstance == null) {//創(chuàng)建了EventBus實例,進入下面的方法defaultInstance = new EventBus(); }}}return defaultInstance;}上面的方法是一個雙重校驗的單例。
public EventBus() {this(DEFAULT_BUILDER); //private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();}DEFAULT_BUILDER是EventBusBuilder.class實例來創(chuàng)建的,這個類非常重要:使用自定義參數(shù)創(chuàng)建EventBus實例,還允許自定義默認EventBus實例,如前面的例子使用索引、配置EventBus的配置&事件的優(yōu)先級&使用索引(四)等就是通過這個類來實現(xiàn)的,大家可以回顧一下。進入初始化一下必要的參數(shù)的構(gòu)造方法EventBus(EventBusBuilder builder),如下
EventBus(EventBusBuilder builder) {logger = builder.getLogger(); /初始化LoggersubscriptionsByEventType = new HashMap<>(); //存儲訂閱事件的typesBySubscriber = new HashMap<>(); //存儲相關(guān)訂閱者class列表,key是注冊者的對象,value是訂閱者的class列表stickyEvents = new ConcurrentHashMap<>(); //粘性事件//下面這4個實例就是我們設(shè)置方法``threadMode = ThreadMode.xxx``//*1mainThreadSupport = builder.getMainThreadSupport();mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;backgroundPoster = new BackgroundPoster(this);asyncPoster = new AsyncPoster(this);//獲取注冊者信息索引(添加由EventBus的注釋預(yù)處理器生成的索引)indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;//初始訂閱方法查找器。這個類主要具有查找訂閱的方法,繼續(xù)往下看subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,builder.strictMethodVerification, builder.ignoreGeneratedIndex);logSubscriberExceptions = builder.logSubscriberExceptions; //是否有日記訂閱,默認truelogNoSubscriberMessages = builder.logNoSubscriberMessages;//是否有日記信息,默認 truesendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;//是否發(fā)送訂閱異常事件,默認truesendNoSubscriberEvent = builder.sendNoSubscriberEvent; //是否發(fā)送沒有訂閱事件,默認truethrowSubscriberException = builder.throwSubscriberException; //是否拋出異常處理//默認情況下,EventBus會考慮事件類層次結(jié)構(gòu)(將通知超類的注冊者)。 關(guān)閉此功能將改善事件的發(fā)布。對于直接擴展Object的簡單事件類,我們測量事件發(fā)布的速度提高了20%。eventInheritance = builder.eventInheritance; executorService = builder.executorService; //創(chuàng)建線程池}我們將此段代碼逐步分析.
這步主要是進行初始化話一下必要的參數(shù),如代碼注解所示。
下面這段代碼就是我們常用的@Subscribe(threadMode = ThreadMode.xxx);初始化。一般常用的就是以下4種。
我們來看看builder.getMainThreadSupport()方法返回的是MainThreadSupport接口,表示為支持Android主線程。
上面的4個線程中都持有 PendingPostQueue 等待發(fā)送的隊列實例。
由mainThreadSupport.createPoster(this)創(chuàng)建一個HandlerPoster而該類繼承了Handle,并且初始化了一個等待發(fā)布隊列。代碼如下。
上面代碼在這里主要初始化訂閱的方法查找器。下面會講解到它是如何進行訂閱方法的。
我們這這里知道了EventBus初始化,然后相關(guān)的實例的創(chuàng)建,接下來我我們進入到register(this)方法的調(diào)用如下方法。
上面的代碼表示的是給注冊者接收事件。 傳遞當(dāng)前所注冊的對象,如Activity、Fragment。
- 注意: 注冊者如果不再接收信息,必須調(diào)用unregister(Object)方法,表示解除注冊則該訂閱者將不再接收到數(shù)據(jù)了,如果不進行解除將可能出現(xiàn)內(nèi)存泄漏。一般在onDestroy方法解除注冊后面會講解到。 在注冊者中擁有必須由@Subscribe注解的方法。@Subscribe還允許配置ThreadMode和優(yōu)先級、是否是粘性行為。
接著,我們進入List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);方法。
//private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>(); //線程安全List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {//從Map集合中獲取訂閱方法列表List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);//判斷當(dāng)前獲取方法是否為空if (subscriberMethods != null) {return subscriberMethods;}//通過EventBusBuilder.ignoreGeneratedIndex//是否忽略配置索引,默認是忽略索引if (ignoreGeneratedIndex) {//1.通過反射獲取方法列表subscriberMethods = findUsingReflection(subscriberClass);} else {//2.通過索引方式獲取訂閱方法列表subscriberMethods = findUsingInfo(subscriberClass);}//是否訂閱方法為空,如果為空則表示該訂閱者里面的方法都不符合EventBus訂閱方法的規(guī)則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;} }我們逐步分析該段代碼。
該段代碼通過注冊者的類來獲取當(dāng)前dingy的方法列表,如果不為空則直接返回訂閱方法。
通過反射來查找訂閱方法,如果該方法為空則拋出異常:該訂閱者的方法和其超類沒有@Subscriber注解的共有方法(即表示不符合EventBus訂閱方法的規(guī)則)。如果不為空則存儲到ConcurrentHashMap集合中。
這里的是由ConcurrentHashMap集合存儲是以當(dāng)前訂閱者的class為key,而將其由@Subscriber綁定的方法添加到List中,,就是集合的value。必須保持線程安全的,所以這里使用了ConcurrentHashMap。
- 1.是否忽略索引設(shè)置,該方法表示忽略設(shè)置索引,進入該方法findUsingReflection(Class<?> subscriberClass),通過反射進行獲取List<SubscriberMethod>
- 注:設(shè)置索引在EventBus的配置&事件的優(yōu)先級&使用索引(四)這里說的,大家可以去看看。
上面的代碼主要是通過反射來獲取相關(guān)的訂閱方法,里面由靜態(tài)內(nèi)部類FindState進行管理相關(guān)信息,由于上面的方法和下面索引的方法都將調(diào)用同一個方法,所以放在下面來將講解,請看下面信息。
- 2.是否忽略索引設(shè)置,該方法表示的設(shè)置了索引,進入findUsingInfo(Class<?> subscriberClass)方法,如下。
分析本段代碼。
首先進入查找的準備狀態(tài),通過默認狀態(tài)池返回狀態(tài)信息,主要存儲的是FindState實例。主要存儲的有訂閱者的class、訂閱者信息SubscriberInfo。訂閱者@Subscribe方法列表List<SubscriberMethod>等.
如果當(dāng)前訂閱者class不為空,則將獲取到訂閱者信息。這里分兩種情況。
如果使用添加索引的話,getSubscriberInfo(findState)該方法將獲取到訂閱信息,如果沒有使用索引的話則將調(diào)用findUsingReflectionInSingleClass(findState);該方法來獲取信息
- 使用添加索引,并返回訂閱方法,該方法如下
由于AbstractSubscriberInfo類實現(xiàn)了SubscriberInfo接口,而SimpleSubscriberInfo繼承了AbstractSubscriberInfo并實現(xiàn)了getSubscriberMethods方法,代碼如下:
本段代碼在SimpleSubscriberInfo.class中。返回訂閱方法數(shù)組SubscriberMethod[]。
- 3.如果沒有添加索引則進入該方法findUsingReflectionInSingleClass(findState);
通過反射來獲取訂閱者方法。這個方法有點長,慢慢看。
private void findUsingReflectionInSingleClass(FindState findState) {Method[] methods;try {// This is faster than getMethods, especially when subscribers are fat classes like Activities//該方法代替getMethods()方法,獲取該訂閱者class全部方法。methods = findState.clazz.getDeclaredMethods();} catch (Throwable th) {// Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149methods = findState.clazz.getMethods(); //獲取該訂閱者class全部方法findState.skipSuperClasses = true; //設(shè)置跳過超類}for (Method method : methods) {int modifiers = method.getModifiers();//獲取修飾符if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) { //通過與運算判斷當(dāng)前的修飾符,是否符合EventBus方法定義Class<?>[] parameterTypes = method.getParameterTypes(); //獲取參數(shù)類型if (parameterTypes.length == 1) { //因為EventBus方法中必須有參數(shù),所以當(dāng)參數(shù)為1時,符合要求//通過注解的形式@Subscribe獲取Subscribe,默認的Subscribe為(priority==0,sticky=false,threadMode=POSTING),就是說ThreadMode為POSTING,粘性為false,優(yōu)先級為0,如果方法中設(shè)置了相應(yīng)的值,則將是你設(shè)置的值。如threadMode=MAIN。Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);if (subscribeAnnotation != null) {Class<?> eventType = parameterTypes[0]; //獲取當(dāng)前參數(shù)的class//進行等級檢查,并存儲到Map集合中,該方法checkAdd是FindState.class中的方法 ,key是@Subscriber中參數(shù)的class,value是該subscriberMethod.method,也就是@Subscribe中的方法如:public void onMessageEvent(MessageEvent event)if (findState.checkAdd(method, eventType)) { // 獲取ThreadMode ,如MAINThreadMode threadMode = subscribeAnnotation.threadMode();//將訂閱方法添加到列表中findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,subscribeAnnotation.priority(), subscribeAnnotation.sticky()));}}} else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) { //不符合EventBus訂閱方法的規(guī)則要求。拋出異常,提示該方法必須是@Subscribe注解,并且需要一個參數(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)) {//是一種非法的@Subscribe方法:必須是公共的,非靜態(tài)的,非抽象的String methodName = method.getDeclaringClass().getName() + "." + method.getName();throw new EventBusException(methodName +" is a illegal @Subscribe method: must be public, non-static, and non-abstract");}}}該段代碼主要是獲取該注冊者的全部方法,并進行篩選出來符合EventBus的訂閱方法的規(guī)則,通過注解的形式來獲取訂閱方法,最后添加到查找狀態(tài)的列表中。
首先獲取的是修飾符,參數(shù)類型,再通過注解的形式來獲取訂閱方法。如下注解Subscribe ,如果不符合EventBus相關(guān)訂閱方法的規(guī)則將拋出異常提示,是否在方法上書寫了錯誤的方法。
接下來我們進入看看@Subscribe 訂閱方法是通過注解的形式來設(shè)置的。
接下來進入getMethodsAndRelease(FindState findState)
private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {//獲取到訂閱方法列表List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods); findState.recycle(); //清空回收相關(guān)信息synchronized (FIND_STATE_POOL) {for (int i = 0; i < POOL_SIZE; i++) {if (FIND_STATE_POOL[i] == null) {FIND_STATE_POOL[i] = findState; //將查找狀態(tài)器findState,添加到狀態(tài)池FIND_STATE_POOL中,為下次直接從查找狀態(tài)池中獲取break;}}}return subscriberMethods; // 返回訂閱方法列表}上面該方法通過FindState靜態(tài)內(nèi)部類獲取了訂閱的列表,然后被存儲到了ConcurrentHashMap集合中。并且存儲到狀態(tài)池中,為方便下次讀取。并且回收資源。
獲取到List<SubscriberMethod>訂閱列表后,再次回到了注冊方法中register(Object subscriber)進入到方法subscribe(subscriber, subscriberMethod);該方法通過循環(huán)進行遍歷
- 注 此處必須是在線程同步中進行,所以添加synchronized。
上面的方法中主要是通過獲取"事件類型"即訂閱方法中的參數(shù)class。然后將其添加到Map集合中,這里用到了CopyOnWriteArrayList(原理:是寫時復(fù)制容器,即當(dāng)我們往CopyOnWriteArrayList容器添加元素時,先從原有的數(shù)組中拷貝一份出來,然后在新的容器做寫操作(添加元素),寫完之后,再將原來的數(shù)組引用指向到新數(shù)組的形式存儲數(shù)據(jù)的,它是線程安全的。)
然后進行判斷是否設(shè)置優(yōu)先級,遍歷并添加到subscriptions列表中。
通過當(dāng)前的注冊者,獲取注冊者class列表,并添加到typesBySubscriber集合中,key是注冊者的class,如:Activity,而value就是訂閱方法中的參數(shù)class的列表。這個地方說明了,一個注冊者,可以擁有多個訂閱事件(方法),將其綁定起來,存儲到Map集合中。最后將該參數(shù)class添加到subscribedEvents.add(eventType);列表中。
下面方法是檢查粘性事件訂閱發(fā)送事件方法,如下
- 注:這里主要是檢查是否發(fā)送粘性發(fā)送事件,方法 postToSubscription我們到發(fā)送事件POST方法調(diào)用時講解。
- 事件發(fā)送
接下來我進入post方法,事件發(fā)送。此時,我們回憶一下簡介中的圖:
post()==>EventBus==>將分發(fā)到各個訂閱事件中。也就是訂閱方法。我們來看看是如何進行操作的。代碼如下
通過以上代碼我大概的知道,post()方法里面的參數(shù)是一個實體的對象,而該實體就是我們在訂閱方法中的參數(shù)實體。你發(fā)現(xiàn)了什么了嗎?
前邊我們已經(jīng)分析了,該注冊者的訂閱方法主要的存儲過程,接下來我們一起進入探究吧。方法跟蹤進入post()方法。
currentPostingThreadState.get()方法中我們知道
private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {@Overrideprotected PostingThreadState initialValue() {return new PostingThreadState();}};通過ThreadLocal這里就叫"本地線程"吧,來管理當(dāng)前的發(fā)送線程狀態(tài),每個發(fā)送線程將得到對應(yīng)一個PostingThreadState,而該PostingThreadState管理eventQueue信息,該信息主要存儲的是發(fā)送事件。
通過eventQueue.add(event)存儲該發(fā)送事件以后完成以后,就判斷當(dāng)前的發(fā)送事件狀態(tài)是否是正在發(fā)送中,如果還沒發(fā)送則當(dāng)該eventQueue不為為空的時候,進入循環(huán)發(fā)送該事件,發(fā)送完一個就刪除一個,直到發(fā)送完成為止。
進入方法postSingleEvent(Object event, PostingThreadState postingState)中。
上面的方法首先獲取該事件類型的class然后傳遞到lookupAllEventTypes(eventClass)方法中,通過該方法獲取當(dāng)前發(fā)送事件的class,包括父類、實現(xiàn)的接口等等列表。所以一般情況下會返回當(dāng)前發(fā)送的事件,和Object.class(注:當(dāng)然了,如果該事件實現(xiàn)接口的話,也會包括實現(xiàn)的接口的。)列表即List<Class<?>>,然后進行遍歷進行或運算。
我們先進入lookupAllEventTypes(eventClass)方法,看看返回的事件類型,并且知道他存儲到Map集合中,即key是當(dāng)前事件class,value是該事件的所有類型,包括父類,接口等。
看看方法postSingleEventForEventType(event, postingState, clazz),發(fā)送事件
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {CopyOnWriteArrayList<Subscription> subscriptions;synchronized (this) {//通過當(dāng)前的事件class,獲取訂閱事件列表subscriptions = subscriptionsByEventType.get(eventClass);}if (subscriptions != null && !subscriptions.isEmpty()) {for (Subscription subscription : subscriptions) {//對PostingThreadState進行賦值postingState.event = event;postingState.subscription = subscription;boolean aborted = false;try {//發(fā)送事件postToSubscription(subscription, event, postingState.isMainThread);aborted = postingState.canceled;} finally {//設(shè)置參數(shù)為空postingState.event = null;postingState.subscription = null;postingState.canceled = false; //設(shè)置標志}if (aborted) {break;}}return true;}return false;}大家是否還記得在注冊的時候出現(xiàn)的CopyOnWriteArrayList<Subscription>在這里就用到了,通過發(fā)送事件的class獲取CopyOnWriteArrayList容器里面的訂閱事件信息,包括注冊者對象,訂閱方法等。然后遍歷并進行發(fā)送事件。在此方法發(fā)布postToSubscription()事件
/*** subscription:發(fā)布訂閱的事件處理器* event:當(dāng)前發(fā)布的事件* isMainThread:是否是在主線程中*/ private void postToSubscription(Subscription subscription, Object event, 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 not decoupled from subscriberinvokeSubscriber(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);}}好了,這就是之前我們在注冊中有一個地方遺留下的,就是判斷是否是粘性狀態(tài)是調(diào)用的方法,大家往回看,就看到了在方法checkPostStickyEventToSubscription中有調(diào)用到該方法。
這里會根據(jù),在方法中所選的threadMode進行調(diào)用,
- 1.MAIN,如果當(dāng)前是否是UI線程,則會進入到invokeSubscriber(subscription, event);方法中。否則將調(diào)用mainThreadPoster.enqueue(subscription, event);這個方法將交給HandlerPoster.class里面的enqueue(Subscription subscription, Object event)方法,HandlerPoster.class現(xiàn)實了該接口,移交給handle完成,大家可以進入HandlerPoster.class看看,這里就不說了。
- 2.POSTING直接調(diào)用 invokeSubscriber(subscription, event);
- 3.MAIN_ORDERED如果當(dāng)前mainThreadPoster不為空則調(diào)用 mainThreadPoster.enqueue(subscription, event);和MAIN進入HandlerPoster.class,反之調(diào)用invokeSubscriber(subscription, event);
- 4.BACKGROUND如果當(dāng)前是主線程,則調(diào)用backgroundPoster.enqueue(subscription, event);,反之調(diào)用invokeSubscriber(subscription, event);
- 5.ASYNC,異步方法調(diào)用,執(zhí)行 asyncPoster.enqueue(subscription, event);,方法進行跟蹤,該類有實現(xiàn)了Runnable接口,然后調(diào)用eventBus.invokeSubscriber(pendingPost);,最終將調(diào)用了invoke方法。
invoke()這是一個本地的方法,將調(diào)用C/C++的到方法進行發(fā)布。最后將進入到訂閱方法中。并傳遞該事件到到訂閱的方法中。
好了,這里我們將post發(fā)布事件的方法簡單的分析了一下。整個EventBus的分析差不多完成了,但是,我們還有一點不能忘記就是解除綁定。接下來我們來看看解除綁定的方法。
- 注:該方法一般生命周期的onDestroy()方法中進行解除綁定。
代碼如下:
EventBus.getDefault().unregister(this);直接進入unregister(this)
/** Unregisters the given subscriber from all event classes. */public synchronized void unregister(Object subscriber) {List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);if (subscribedTypes != null) {for (Class<?> eventType : subscribedTypes) {unsubscribeByEventType(subscriber, eventType);}typesBySubscriber.remove(subscriber);} else {logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());}} /** Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber. *//*** subscriber:指代當(dāng)前解除注冊的對象如Activity,* eventType:發(fā)送的事件類型,post函數(shù)的實體對象class*/ private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {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;subscriptions.remove(i); //刪除i--; //目的是減少遍歷次數(shù)size--;}}} }解除綁定,這里很簡單。這里就不一一進行解釋了,^_^。
總結(jié)
1.本篇文章主要對EventBus源碼進行簡單的分析。主要是進行了觀察者模式的一些高級運用。如果大家對觀察者模式理解不怎么清楚可以進入這里看看簡單的案例觀察者模式,內(nèi)容非常簡單。
2.相關(guān)的EvenBut的使用,請看之前的內(nèi)容。如EventBus認識(一)、EventBus的ThreadMode使用以及分析(二)等等。
3.學(xué)習(xí)本篇文章中可以認識到一些常用的類如CopyOnWriteArrayList、ThreadLocal等,到時可以深入研究一下,有助我們提高。
4.一些編程思想,設(shè)計模式值得我們?nèi)W(xué)習(xí)的,如單例模式EventBus雙重校驗、建造者模式,EventBus構(gòu)造器的時候用到,用了初始化各個參數(shù)等等。面向接口編程而不針對實現(xiàn)編程。
5.如果有什么問題希望大家進行指正,好好學(xué)習(xí),一起進步。
總結(jié)
以上是生活随笔為你收集整理的EventBus源码分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 10. Python面向对象
- 下一篇: 2019年1月3日