EvenBus源码分析
概述
一般使用EventBus的組件類,類似下面這種方式:
public class SampleComponent extends Fragment {@Overridepublic void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);EventBus.getDefault().register(this);}public void onEventMainThread(param){}public void onEventPostThread(param){}public void onEventBackgroundThread(param){}public void onEventAsync(param){}@Overridepublic void onDestroy(){super.onDestroy();EventBus.getDefault().unregister(this);} }?
大多情況下,都會(huì)在onCreate中進(jìn)行register,在onDestory中進(jìn)行unregister ;
代碼中以onEvent開頭的方法的作用?
register(this)就是去當(dāng)前類,遍歷所有的方法,找到onEvent開頭的然后進(jìn)行存儲(chǔ)。
onEvent后面可以寫四種,也就是上面出現(xiàn)的四個(gè)方法,決定了當(dāng)前的方法最終在什么線程運(yùn)行,怎么運(yùn)行
register后,調(diào)用:
EventBus.getDefault().post(param);
調(diào)用也可以叫發(fā)布,只要把這個(gè)param發(fā)布出去,EventBus會(huì)在它內(nèi)部存儲(chǔ)的方法中,進(jìn)行掃描,找到參數(shù)匹配的,就使用反射進(jìn)行調(diào)用。
其實(shí)EventBus就是在內(nèi)部存儲(chǔ)了一堆onEvent開頭的方法,然后post的時(shí)候,根據(jù)post傳入的參數(shù),去找到匹配的方法,反射調(diào)用之。
它內(nèi)部使用了Map進(jìn)行存儲(chǔ),鍵就是參數(shù)的Class類型。知道是這個(gè)類型,根據(jù)post傳入的參數(shù)就可以進(jìn)行查找.
register
EventBus.getDefault().register(this);
首先:
EventBus.getDefault()是個(gè)單例模式,和傳統(tǒng)的getInstance一樣:
public static EventBus getDefault() {if(defaultInstance == null) {Class var0 = EventBus.class;synchronized(EventBus.class) {if(defaultInstance == null) {defaultInstance = new EventBus();}}}return defaultInstance; }?使用了雙重判斷的方式,防止并發(fā)的問(wèn)題,還能極大的提高效率。
register是一個(gè)普通的方法:
register使用的一般有4個(gè):
public void register(Object subscriber) {register(subscriber, DEFAULT_METHOD_NAME, false, 0); } public void register(Object subscriber, int priority) {register(subscriber, DEFAULT_METHOD_NAME, false, priority); } public void registerSticky(Object subscriber) {register(subscriber, DEFAULT_METHOD_NAME, true, 0); } public void registerSticky(Object subscriber, int priority) {register(subscriber, DEFAULT_METHOD_NAME, true, priority); }?本質(zhì)上就調(diào)用了同一個(gè):
private synchronized void register(Object subscriber, String methodName, boolean sticky, int priority) {List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass(),methodName);for (SubscriberMethod subscriberMethod : subscriberMethods) {subscribe(subscriber, subscriberMethod, sticky, priority);} }?四個(gè)參數(shù)
subscriber 是我們掃描類的對(duì)象,也就是我們代碼中常見的this;
methodName 這個(gè)是寫死的:“onEvent”,用于確定掃描什么開頭的方法,可見我們的類中都是以這個(gè)開頭。
sticky 這個(gè)參數(shù),解釋源碼的時(shí)候解釋,暫時(shí)不用管
priority 優(yōu)先級(jí),優(yōu)先級(jí)越高,在調(diào)用的時(shí)候會(huì)越先調(diào)用。
下面開始看代碼:
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass(), methodName);?調(diào)用內(nèi)部類SubscriberMethodFinder的findSubscriberMethods方法,傳入了subscriber 的class,以及methodName,
返回一個(gè)List<SubscriberMethod>。
然后去遍歷該類內(nèi)部所有方法,然后根據(jù)methodName去匹配,匹配成功的封裝成SubscriberMethod,最后返回一個(gè)List。下面看代碼
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass, String eventMethodName) {String key = subscriberClass.getName() + '.' + eventMethodName;List<SubscriberMethod> subscriberMethods;synchronized (methodCache) {subscriberMethods = methodCache.get(key);}if (subscriberMethods != null) {return subscriberMethods;}subscriberMethods = new ArrayList<SubscriberMethod>();Class<?> clazz = subscriberClass;HashSet<String> eventTypesFound = new HashSet<String>();StringBuilder methodKeyBuilder = new StringBuilder();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.getMethods();for (Method method : methods) {String methodName = method.getName();if (methodName.startsWith(eventMethodName)) {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(eventMethodName.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 "+ eventMethodName);} else {synchronized (methodCache) {methodCache.put(key, subscriberMethods);}return subscriberMethods;} }?核心代碼說(shuō)明:
代碼中clazz.getMethods();去得到所有的方法:
就開始遍歷每一個(gè)方法了,去匹配封裝了。
分別判斷了是否以onEvent開頭,是否是public且非static和abstract方法,是否是一個(gè)參數(shù)。如果都復(fù)合,才進(jìn)入封裝的部分。
根據(jù)方法的后綴,來(lái)確定threadMode,threadMode是個(gè)枚舉類型:就四種情況。
最后將method, threadMode, eventType傳入構(gòu)造了:new SubscriberMethod(method, threadMode, eventType)。添加到List,最終放回。
代碼clazz = clazz.getSuperclass();可以看到,會(huì)掃描所有的父類,不僅僅是當(dāng)前類。
繼續(xù)回到register:
for (SubscriberMethod subscriberMethod : subscriberMethods) {subscribe(subscriber, subscriberMethod, sticky, priority); }?for循環(huán)掃描到的方法,然后去調(diào)用suscribe方法。
// Must be called in synchronized block private void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority) {subscribed = true;Class<?> eventType = subscriberMethod.eventType;CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);Subscription newSubscription = new Subscription(subscriber, subscriberMethod, priority);if (subscriptions == null) {subscriptions = new CopyOnWriteArrayList<Subscription>();subscriptionsByEventType.put(eventType, subscriptions);} else {for (Subscription subscription : subscriptions) {if (subscription.equals(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;}}List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);if (subscribedEvents == null) {subscribedEvents = new ArrayList<Class<?>>();typesBySubscriber.put(subscriber, subscribedEvents);}subscribedEvents.add(eventType);if (sticky) {Object stickyEvent;synchronized (stickyEvents) {stickyEvent = stickyEvents.get(eventType);}if (stickyEvent != null) {// If the subscriber is trying to abort the event, it will fail (event is not tracked in posting state) // --> Strange corner case, which we don't take care of here. postToSubscription(newSubscription, stickyEvent, Looper.getMainLooper() == Looper.myLooper());}} }?
subscriberMethod中保存了method, threadMode, eventType
根據(jù)subscriberMethod.eventType,去subscriptionsByEventType去查找一個(gè)CopyOnWriteArrayList<Subscription> ,如果沒(méi)有則創(chuàng)建。
然后傳入的參數(shù)封裝成了一個(gè):Subscription(subscriber, subscriberMethod, priority);
其實(shí)subscriptionsByEventType是個(gè)Map,key:eventType ; value:CopyOnWriteArrayList<Subscription> ;
這個(gè)Map其實(shí)就是EventBus存儲(chǔ)方法的地方,一定要記住!
代碼中就是添加newSubscription;并且是按照優(yōu)先級(jí)添加的。可以看到,優(yōu)先級(jí)越高,會(huì)插到在當(dāng)前List的前面。
代碼中根據(jù)subscriber存儲(chǔ)它所有的eventType ; 依然是map;key:subscriber ,value:List<eventType> ;知道就行,非核心代碼,主要用于isRegister的判斷。
代碼中判斷sticky;如果為true,從stickyEvents中根據(jù)eventType去查找有沒(méi)有stickyEvent,如果有則立即發(fā)布去執(zhí)行。stickyEvent其實(shí)就是我們post時(shí)的參數(shù)。
postToSubscription這個(gè)方法,我們?cè)趐ost的時(shí)候會(huì)介紹。
到此,我們r(jià)egister就介紹完了。
你只要記得一件事:掃描了所有的方法,把匹配的方法最終保存在subscriptionsByEventType(Map,key:eventType ; value:CopyOnWriteArrayList<Subscription> )中;
eventType是我們方法參數(shù)的Class,Subscription中則保存著subscriber, subscriberMethod(method, threadMode, eventType), priority;包含了執(zhí)行改方法所需的一切。
post
post它又是如何調(diào)用我們的方法的。
register時(shí),把方法存在subscriptionsByEventType;那么post肯定會(huì)去subscriptionsByEventType去取方法,然后調(diào)用。
看源碼
** Posts the given event to the event bus. */ public void post(Object event) {PostingThreadState postingState = currentPostingThreadState.get();List<Object> eventQueue = postingState.eventQueue;eventQueue.add(event);if (postingState.isPosting) {return;} else {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;}} }?currentPostingThreadState是一個(gè)ThreadLocal類型的,里面存儲(chǔ)了PostingThreadState;PostingThreadState包含了一個(gè)eventQueue和一些標(biāo)志位。
private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {@Overrideprotected PostingThreadState initialValue() {return new PostingThreadState();} }把我們傳入的event,保存到了當(dāng)前線程中的一個(gè)變量PostingThreadState的eventQueue中。
判斷當(dāng)前是否是UI線程。
遍歷隊(duì)列中的所有的event,調(diào)用postSingleEvent(eventQueue.remove(0), postingState)方法。
每次post都會(huì)去調(diào)用整個(gè)隊(duì)列么,那么不會(huì)造成方法多次調(diào)用么?
可以看到代碼中,有個(gè)判斷,就是防止該問(wèn)題的,isPosting=true了,就不會(huì)往下走了。
下面看postSingleEvent
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {Class<? extends Object> eventClass = event.getClass();List<Class<?>> eventTypes = findEventTypes(eventClass);boolean subscriptionFound = false;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 && !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;}}subscriptionFound = true;}}if (!subscriptionFound) {Log.d(TAG, "No subscribers registered for event " + eventClass);if (eventClass != NoSubscriberEvent.class && eventClass != SubscriberExceptionEvent.class) {post(new NoSubscriberEvent(this, event));}} }?將我們的event,即post傳入的實(shí)參;以及postingState傳入到postSingleEvent中。
根據(jù)event的Class,去得到一個(gè)List<Class<?>>;其實(shí)就是得到event當(dāng)前對(duì)象的Class,以及父類和接口的Class類型;主要用于匹配,比如你傳入Dog extends Dog,他會(huì)把Animal也裝到該List中。
遍歷所有的Class,到subscriptionsByEventType去查找subscriptions;register里面把方法存的那個(gè)Map;
遍歷每個(gè)subscription,依次去調(diào)用postToSubscription(subscription, event, postingState.isMainThread);
這個(gè)方法就是去反射執(zhí)行方法了,大家還記得在register,if(sticky)時(shí),也會(huì)去執(zhí)行這個(gè)方法。
下面看它如何反射執(zhí)行:
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包含了所有執(zhí)行需要的東西,大致有:subscriber, subscriberMethod(method, threadMode, eventType), priority;
那么這個(gè)方法:第一步根據(jù)threadMode去判斷應(yīng)該在哪個(gè)線程去執(zhí)行該方法;
case PostThread:
直接反射調(diào)用;也就是說(shuō)在當(dāng)前的線程直接調(diào)用該方法;
case MainThread:
首先去判斷當(dāng)前如果是UI線程,則直接調(diào)用;否則: mainThreadPoster.enqueue(subscription, event);把當(dāng)前的方法加入到隊(duì)列,然后直接通過(guò)handler去發(fā)送一個(gè)消息,在handler的handleMessage中,去執(zhí)行我們的方法。就是通過(guò)Handler去發(fā)送消息,然后執(zhí)行的。
case BackgroundThread:
如果當(dāng)前非UI線程,則直接調(diào)用;如果是UI線程,則將任務(wù)加入到后臺(tái)的一個(gè)隊(duì)列,最終由Eventbus中的一個(gè)線程池去調(diào)用
executorService = Executors.newCachedThreadPool();。
case Async:將任務(wù)加入到后臺(tái)的一個(gè)隊(duì)列,最終由Eventbus中的一個(gè)線程池去調(diào)用;線程池與BackgroundThread用的是同一個(gè)。
BackgroundThread和Async有什么區(qū)別呢?
BackgroundThread中的任務(wù),一個(gè)接著一個(gè)去調(diào)用,中間使用了一個(gè)布爾型變量handlerActive進(jìn)行的控制。
Async則會(huì)動(dòng)態(tài)控制并發(fā)。
總結(jié):register會(huì)把當(dāng)前類中匹配的方法,存入一個(gè)map,而post會(huì)根據(jù)實(shí)參去map查找進(jìn)行反射調(diào)用。
可以說(shuō),就是在一個(gè)單例內(nèi)部維持著一個(gè)map對(duì)象存儲(chǔ)了一堆的方法;post無(wú)非就是根據(jù)參數(shù)去查找方法,進(jìn)行反射調(diào)用。
其余方法
介紹了register和post;大家獲取還能想到一個(gè)詞sticky,在register中,如何sticky為true,會(huì)去stickyEvents去查找事件,然后立即去post;
那么這個(gè)stickyEvents何時(shí)進(jìn)行保存事件呢?
其實(shí)evevntbus中,除了post發(fā)布事件,還有一個(gè)方法也可以:
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); }?和post功能類似,但是會(huì)把方法存儲(chǔ)到stickyEvents中去;
大家再去看看EventBus中所有的public方法,無(wú)非都是一些狀態(tài)判斷,獲取事件,移除事件的方法;
?
轉(zhuǎn)載于:https://www.cnblogs.com/loaderman/p/6472229.html
總結(jié)
以上是生活随笔為你收集整理的EvenBus源码分析的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: poj3421 X-factor Cha
- 下一篇: [vim]高亮查找匹配