本文主要介紹EventBus3.0的源碼 EventBus是一個Android事件發佈/訂閱框架,通過解耦發佈者和訂閱者簡化 Android 事件傳遞。 EventBus使用簡單,並將事件發佈和訂閱充分解耦,從而使代碼更簡潔。 本文主要從以下幾個模塊來介紹 1、EventBus使用 2、EventB ...
本文主要介紹EventBus3.0的源碼
EventBus是一個Android事件發佈/訂閱框架,通過解耦發佈者和訂閱者簡化 Android 事件傳遞。 EventBus使用簡單,並將事件發佈和訂閱充分解耦,從而使代碼更簡潔。 本文主要從以下幾個模塊來介紹 1、EventBus使用 2、EventBus註冊源碼解析 3、EventBus事件分發解析 4、EventBus取消註冊解析 一、EventBus使用 1、首先是註冊1 EventBus.getDefault().register(this);
2、響應事件方法
1 @Subscribe(threadMode = ThreadMode.BACKGROUND, sticky = true, priority = 100) 2 public void jiaoTest(String str) { 3 System.out.println("響應方法:" + str); 4 }參數解析: threadMode :方法執行的線程 sticky:是否接受粘性事件 priority:優先順序 String str:方法接受對象類型 3、事件分發
1 EventBus.getDefault().post("Test");
4、解除註冊
1 EventBus.getDefault().unregister(this);
以上就是EventBus的使用過程,用起來非常簡單方便,非常實用。
二、註冊源碼解析
對應以上的註冊方式,我們就從EventBus.getDefault().register(this);入手,首先查看EventBus.getDefault()
看看EventBus是如何初始化的;
1 /** Convenience singleton for apps using a process-wide EventBus instance. */ 2 public static EventBus getDefault() { 3 if (defaultInstance == null) { 4 synchronized (EventBus.class) { 5 if (defaultInstance == null) { 6 defaultInstance = new EventBus(); 7 } 8 } 9 } 10 return defaultInstance; 11 }
可以看出來,EventBus是單例模式存在的,一個項目中只能有一個EventBus這樣有利於管理訂閱者和訂閱方法,這會在下麵的介紹中體現出來。
接下來看register(this)
1 public void register(Object subscriber) { 2 //訂閱者 3 Class<?> subscriberClass = subscriber.getClass(); 4 List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass); 5 synchronized (this) { 6 for (SubscriberMethod subscriberMethod : subscriberMethods) { 7 subscribe(subscriber, subscriberMethod); 8 } 9 } 10 } 11
可以看出首先獲取訂閱者的類對象Class<?> subscriberClass = subscriber.getClass();
在看這段代碼之前,我們首先要瞭解SubscriberMethod和subscriberMethodFinder.findSubscriberMethods方法到底做了什麼
首先來看SubscriberMethod
1 public class SubscriberMethod { 2 final Method method;//方法 3 final ThreadMode threadMode;//執行線程 4 final Class<?> eventType;//接收的事件類型 5 final int priority;//優先順序 6 final boolean sticky; 7 /** Used for efficient comparison */ 8 String methodString; 9 .... 10 }
可以看出SubscriberMethod其實就是一個訂閱方法的實體類,裡面保存了訂閱方法信息
接著看subscriberMethodFinder.findSubscriberMethods
該方法的作用其實就是從訂閱類中獲取所有的訂閱方法信息;
1 List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) { 2 3 //首先從緩存中讀取 4 List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass); 5 if (subscriberMethods != null) { 6 return subscriberMethods; 7 } 8 9 10 //是否忽略註解器生成的MyEventBusIndex類 11 12 if (ignoreGeneratedIndex) { 13 //利用反射來獲取訂閱類中的訂閱方法信息 14 subscriberMethods = findUsingReflection(subscriberClass); 15 } else { 16 //從註解器生成的MyEventBusIndex類中獲得訂閱類的訂閱方法信息 17 subscriberMethods = findUsingInfo(subscriberClass); 18 } 19 if (subscriberMethods.isEmpty()) { 20 throw new EventBusException("Subscriber " + subscriberClass 21 + " and its super classes have no public methods with the @Subscribe annotation"); 22 } else { 23 //保存進緩存 24 METHOD_CACHE.put(subscriberClass, subscriberMethods); 25 return subscriberMethods; 26 } 27 }
我們看到,該方法首先從緩存中獲取訂閱類的訂閱方法信息,如果沒有則通過兩種方式來獲取
1、通過EventBusAnnotationProcessor(註解處理器)生成的MyEventBusIndex中獲取
2、利用反射來讀取訂閱類中訂閱方法信息
EventBusAnnotationProcessor是什麼東東?(此處參考:文/達達達達sky(簡書作者)原文鏈接:http://www.jianshu.com/p/f057c460c77e)
在3.0版本中,EventBus提供了一個EventBusAnnotationProcessor註解處理器來在編譯期通過讀取@Subscribe()註解並解析,
處理其中所包含的信息,然後生成java類來保存所有訂閱者關於訂閱的信息,這樣就比在運行時使用反射來獲得這些訂閱者的
信息速度要快.我們可以參考EventBus項目里的EventBusPerformance這個例子,編譯後我們可以在build文件夾里找到這個類
,MyEventBusIndex 類,當然類名是可以自定義的.我們大致看一下生成的MyEventBusIndex類是什麼樣的:
1 /** 2 * This class is generated by EventBus, do not edit. 3 */ 4 public class MyEventBusIndex implements SubscriberInfoIndex { 5 private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX; 6 7 static { 8 SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>(); 9 10 putIndex(new SimpleSubscriberInfo(org.greenrobot.eventbusperf.testsubject.PerfTestEventBus.SubscriberClassEventBusAsync.class, 11 true, new SubscriberMethodInfo[]{ 12 new SubscriberMethodInfo("onEventAsync", TestEvent.class, ThreadMode.ASYNC), 13 })); 14 15 putIndex(new SimpleSubscriberInfo(TestRunnerActivity.class, true, new SubscriberMethodInfo[]{ 16 new SubscriberMethodInfo("onEventMainThread", TestFinishedEvent.class, ThreadMode.MAIN), 17 })); 18 } 19 20 private static void putIndex(SubscriberInfo info) { 21 SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info); 22 } 23 24 @Override 25 public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) { 26 SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass); 27 if (info != null) { 28 return info; 29 } else { 30 return null; 31 } 32 } 33 }
可以看出是使用一個靜態HashMap即:SUBSCRIBER_INDEX來保存訂閱類的信息,其中包括了訂閱類的class對象,
是否需要檢查父類,以及訂閱方法的信息SubscriberMethodInfo的數組,SubscriberMethodInfo中又保存了,訂閱方法的方法名,
訂閱的事件類型,觸發線程,是否接收sticky事件以及優先順序priority.這其中就保存了register()的所有需要的信息;
我們重點研究一下通過反射來獲取訂閱方法信息即:findUsingReflection(subscriberClass);
1 private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) { 2 FindState findState = prepareFindState(); 3 findState.initForSubscriber(subscriberClass); 4 while (findState.clazz != null) { 5 findUsingReflectionInSingleClass(findState); 6 findState.moveToSuperclass(); 7 } 8 return getMethodsAndRelease(findState); 9 }
FindState其實就是一個裡面保存了訂閱者和訂閱方法信息的一個實體類,包括訂閱類中所有訂閱的事件類型和所有的訂閱方法等。
我們看到會首先創建一個FindState對象並執行findUsingReflectionInSingleClass(findState);來獲取訂閱類的方法信息
1 private void findUsingReflectionInSingleClass(FindState findState) { 2 Method[] methods; 3 try { 4 // This is faster than getMethods, especially when subscribers are fat classes like Activities 5 methods = findState.clazz.getDeclaredMethods(); 6 } catch (Throwable th) { 7 // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149 8 //通過反射獲取到訂閱類中的所有方法 9 methods = findState.clazz.getMethods(); 10 findState.skipSuperClasses = true; 11 } 12 //遍歷所有方法,忽略private類型的,最後如果是公有,並且不是 13 //java編譯器 生成的方法名,那麼就是我們要的了。 14 for (Method method : methods) { 15 int modifiers = method.getModifiers(); 16 if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) { 17 Class<?>[] parameterTypes = method.getParameterTypes(); 18 //保證只有一個事件參數 19 if (parameterTypes.length == 1) { 20 //得到註解 21 Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class); 22 if (subscribeAnnotation != null) { 23 Class<?> eventType = parameterTypes[0]; 24 //校驗是否添加該方法 25 if (findState.checkAdd(method, eventType)) { 26 ThreadMode threadMode = subscribeAnnotation.threadMode(); 27 //添加方法 28 findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode, 29 subscribeAnnotation.priority(), subscribeAnnotation.sticky())); 30 } 31 } 32 } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) { 33 String methodName = method.getDeclaringClass().getName() + "." + method.getName(); 34 throw new EventBusException("@Subscribe method " + methodName + 35 "must have exactly 1 parameter but has " + parameterTypes.length); 36 } 37 } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) { 38 String methodName = method.getDeclaringClass().getName() + "." + method.getName(); 39 throw new EventBusException(methodName + 40 " is a illegal @Subscribe method: must be public, non-static, and non-abstract"); 41 } 42 } 43 }
可以看到,首先會得到訂閱類的class對象並通過反射獲取訂閱類中的所有方法信息,然後通過篩選獲取到訂閱方法集合。
程式執行到此我們就獲取到了訂閱類中的所有的訂閱方法信息,接下來我們就要對訂閱方法進行註冊;
subscribe(subscriber, subscriberMethod);//參數:1訂閱者2訂閱方法集
1 // Must be called in synchronized block 2 private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) { 3 //獲取訂閱方法的參數類型 4 Class<?> eventType = subscriberMethod.eventType; 5 Subscription newSubscription = new Subscription(subscriber, subscriberMethod); 6 //根據訂閱的事件類型獲取所有的訂閱者 7 CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType); 8 //將訂閱者添加到subscriptionsByEventType集合中 9 if (subscriptions == null) { 10 subscriptions = new CopyOnWriteArrayList<>(); 11 subscriptionsByEventType.put(eventType, subscriptions); 12 } else { 13 if (subscriptions.contains(newSubscription)) { 14 throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event " 15 + eventType); 16 } 17 } 18 19 //根據優先順序,將訂閱者插入到指定的位置 20 int size = subscriptions.size(); 21 for (int i = 0; i <= size; i++) { 22 if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) { 23 subscriptions.add(i, newSubscription); 24 break; 25 } 26 } 27 28 //獲取訂閱者所有訂閱的事件類型 29 30 List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber); 31 if (subscribedEvents == null) { 32 subscribedEvents = new ArrayList<>(); 33 typesBySubscriber.put(subscriber, subscribedEvents); 34 } 35 //將該事件類型添加到typesBySubscriber中 36 subscribedEvents.add(eventType); 37 38 39 //如果接收sticky事件,立即分發sticky事件 40 if (subscriberMethod.sticky) { 41 if (eventInheritance) { 42 // Existing sticky events of all subclasses of eventType have to be considered. 43 // Note: Iterating over all events may be inefficient with lots of sticky events, 44 // thus data structure should be changed to allow a more efficient lookup 45 // (e.g. an additional map storing sub classes of super classes: Class -> List<Class>). 46 Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet(); 47 for (Map.Entry<Class<?>, Object> entry : entries) { 48 Class<?> candidateEventType = entry.getKey(); 49 if (eventType.isAssignableFrom(candidateEventType)) { 50 Object stickyEvent = entry.getValue(); 51 checkPostStickyEventToSubscription(newSubscription, stickyEvent); 52 } 53 } 54 } else { 55 Object stickyEvent = stickyEvents.get(eventType); 56 checkPostStickyEventToSubscription(newSubscription, stickyEvent); 57 } 58 } 59 }
上面這段代碼涉及到幾個對象我來介紹一下:
Subscription
//訂閱者信息
final class Subscription {
final Object subscriber;//訂閱者
final SubscriberMethod subscriberMethod;//訂閱方法
}
subscriptionsByEventType
key訂閱方法類型 values 所有訂閱了該類型的訂閱者集合
Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
typesBySubscriber
key訂閱者 values訂閱事件集合
Map<Object, List<Class<?>>> typesBySubscriber;
瞭解了這幾個對象,上面的代碼就很容易看懂了,
1、首先獲取訂閱方法的參數類型即訂閱事件類型
2、根據訂閱事件類型獲取該事件類型的所有訂閱者
3、將該訂閱者添加到該事件類型的訂閱者集合中即:subscriptionsByEventType
4、獲取訂閱者所有的訂閱事件類型
5、將該事件類型添加到該訂閱者的訂閱事件類型集中即:typesBySubscriber
至此,就完成了訂閱類中訂閱方法的註冊,我們來看一下整個流程
三、事件分發解析
接下來我們來分析EventBus的事件分發機制即:EventBus.getDefault().post("Test");
我們從post方法入手
1 /** Posts the given event to the event bus. */ 2 public void post(Object event) { 3 //獲取當前線程的postingState 4 PostingThreadState postingState = currentPostingThreadState.get(); 5 //取得當前線程的事件隊列 6 List<Object> eventQueue = postingState.eventQueue; 7 //將該事件添加到當前的事件隊列中等待分發 8 eventQueue.add(event); 9 10 if (!postingState.isPosting) { 11 //判斷是否是在主線程post 12 postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper(); 13 postingState.isPosting = true; 14 if (postingState.canceled) { 15 throw new EventBusException("Internal error. Abort state was not reset"); 16 } 17 try { 18 while (!eventQueue.isEmpty()) { 19 //分發事件 20 postSingleEvent(eventQueue.remove(0), postingState); 21 } 22 } finally { 23 postingState.isPosting = false; 24 postingState.isMainThread = false; 25 } 26 } 27 }
什麼是PostingThreadState?
1 final static class PostingThreadState { 2 final List<Object> eventQueue = new ArrayList<Object>();//當前線程的事件隊列 3 boolean isPosting;//是否有事件正在分發 4 boolean isMainThread;//post的線程是否是主線程 5 Subscription subscription;//訂閱者 6 Object event;//訂閱事件 7 boolean canceled;//是否取消 8 }
PostingThreadState中包含了當前線程的事件隊列,就是當前線程所有分發的事件都保存在eventQueue事件隊列中
以及訂閱者訂閱事件等信息,有了這些信息我們就可以從事件隊列中取出事件分發給對應的訂閱者。
PostingThreadState怎麼獲得?
1 ThreadLocal 是一個線程內部的數據存儲類,通過它可以在指定的線程中存儲數據,而這段數據是不會與其他線程共用的。 2 private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() { 3 @Override 4 protected PostingThreadState initialValue() { 5 return new PostingThreadState(); 6 } 7 };
可以看出currentPostingThreadState
的實現是一個包含了PostingThreadState
的ThreadLocal
對象,這樣可以保證取到的都是
自己線程對應的數據。
我們有了PostingThreadState獲取到了當前線程的事件隊列,接下來就是事件分發,我們來看
postSingleEvent(eventQueue.remove(0), postingState);
1 事件分發 2 private void postSingleEvent(Object event, PostingThreadState postingState) throws Error { 3 //得到事件類型 4 Class<?> eventClass = event.getClass(); 5 boolean subscriptionFound = false; 6 7 //是否觸發訂閱了該事件(eventClass)的父類,以及介面的類的響應方法. 8 if (eventInheritance) { 9 List<Class<?>> eventTypes = lookupAllEventTypes(eventClass); 10 int countTypes = eventTypes.size(); 11 for (int h = 0; h < countTypes; h++) { 12 Class<?> clazz = eventTypes.get(h); 13 subscriptionFound |= postSingleEventForEventType(event, postingState, clazz); 14 } 15 } else { 16 subscriptionFound = postSingleEventForEventType(event, postingState, eventClass); 17 } 18 if (!subscriptionFound) { 19 if (logNoSubscriberMessages) { 20 Log.d(TAG, "No subscribers registered for event " + eventClass); 21 } 22 if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class && 23 eventClass != SubscriberExceptionEvent.class) { 24 post(new NoSubscriberEvent(this, event)); 25 } 26 } 27 }
通過以上代碼我們可以發現,真正的事件分發是通過postSingleEventForEventType(event, postingState, eventClass);發出去的我們來看:
1 private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) { 2 CopyOnWriteArrayList<Subscription> subscriptions; 3 synchronized (this) { 4 //根據事件類型獲取所有的訂閱者 5 subscriptions = subscriptionsByEventType.get(eventClass); 6 } 7 //向每個訂閱者分發事件 8 if (subscriptions != null && !subscriptions.isEmpty()) { 9 for (Subscription subscription : subscriptions) { 10 postingState.event = event; 11 postingState.subscription = subscription; 12 boolean aborted = false; 13 try { 14 postToSubscription(subscription, event, postingState.isMainThread); 15 aborted = postingState.canceled; 16 } finally { 17 postingState.event = null; 18 postingState.subscription = null; 19 postingState.canceled = false; 20 } 21 if (aborted) { 22 break; 23 } 24 } 25 return true; 26 } 27 return false; 28 }
可以看到首先根據事件類型獲取到所有的訂閱者,然後迴圈向每個訂閱者發送事件,通過
postToSubscription(subscription, event, postingState.isMainThread);發送出去
1 private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) { 2 switch (subscription.subscriberMethod.threadMode) { 3 case POSTING://預設的 ThreadMode,表示在執行 Post 操作的線程直接調用訂閱者的事件響應方法, 4 //不論該線程是否為主線程(UI 線程)。 5 invokeSubscriber(subscription, event); 6 break; 7 case MAIN://在主線程中執行響應方法。 8 if (isMainThread) { 9 invokeSubscriber(subscription, event); 10 } else { 11 mainThreadPoster.enqueue(subscription, event); 12 } 13 break; 14 case BACKGROUND://在後臺線程中執行響應方法。 15 if (isMainThread) { 16 backgroundPoster.enqueue(subscription, event); 17 } else { 18 invokeSubscriber(subscription, event); 19 } 20 break; 21 case ASYNC://不論發佈線程是否為主線程,都使用一個空閑線程來處理。 22 asyncPoster.enqueue(subscription, event); 23 break; 24 default: 25 throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode); 26 } 27 }
以上的四種threadMode可以看代碼註釋簡單瞭解一下,通過一下代碼我們來看一下訂閱方法最後是通過invokeSubscriber(subscription, event);來執行的
1 //最終通過反射調用訂閱者的訂閱函數 並把event作為參數傳入 2 void invokeSubscriber(Subscription subscription, Object event) { 3 try { 4 subscription.subscriberMethod.method.invoke(subscription.subscriber, event); 5 } catch (InvocationTargetException e) { 6 handleSubscriberException(subscription, event, e.getCause()); 7 } catch (IllegalAccessException e) { 8 throw new IllegalStateException("Unexpected exception", e); 9 } 10 }
真相大白;最後是通過反射的方式,調用了訂閱類中的訂閱方法。我們來總結一下整個事件分發的過程
1、首先獲取當前線程的PostingThreadState對象從而獲取到當前線程的事件隊列
2、通過事件類型獲取到所有訂閱者集合
3、通過反射執行訂閱者中的訂閱方法
是不是很簡單。
我們來看一下整個事件分發的流程圖
四、取消註冊解析
我們簡單看一下取消註冊的源碼EventBus.getDefault().unregister(this);
1 /** Unregisters the given subscriber from all event classes. */ 2 public synchronized void unregister(Object subscriber) { 3 //獲取訂閱者的所有訂閱的事件類型 4 List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber); 5 if (subscribedTypes != null) { 6 for (Class<?> eventType : subscribedTypes) { 7 //從事件類型的訂閱者集合中移除訂閱者 8 unsubscribeByEventType(subscriber, eventType); 9 } 10 typesBySubscriber.remove(subscriber); 11 } else { 12 Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass()); 13 } 14 }
再來看一下:unsubscribeByEventType(subscriber, eventType);
1 /** Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber. */ 2 private void unsubscribeByEventType(Object subscriber, Class<?> eventType) { 3 //獲取事件類型的所有訂閱者 4 List<Subscription> subscriptions = subscriptionsByEventType.get(eventType); 5 //遍歷訂閱者集合,將解除的訂閱者移除 6 if (subscriptions != null) { 7 int size = subscriptions.size(); 8 for (int i = 0; i < size; i++) { 9 Subscription subscription = subscriptions.get(i); 10 if (subscription.subscriber == subscriber) { 11 subscription.active = false; 12 subscriptions.remove(i); 13 i--; 14 size--; 15 } 16