EventBus 是一個在 Android 開發(fā)中使用的發(fā)布/訂閱事件總線框架
EventBus...
-
簡化組件之間的通信
- 解耦事件發(fā)送方和接收方
- 能夠很好地處理Activities、Fragment和后臺線程
- 避免復雜和容易出錯的依賴關系和生命周期問題
使您的代碼更簡單
快
體積小(~60k jar)
在實踐中已經被超過10億次安裝的應用程序證明
......(官方的王婆賣瓜自賣自夸)
注冊:
EventBus.getDefault().register(this)
public void register(Object subscriber) {
//1.獲取訂閱者的Class對象
Class<?> subscriberClass = subscriber.getClass();
// 判斷當前訂閱者是否為匿名類(因為在匿名類中,訂閱注解是不可見的,查找方式必須是反射方式,這個后面會詳細說明)
boolean forceReflection = subscriberClass.isAnonymousClass();
//2.通過反射獲取訂閱者中開發(fā)人員定義的處理事件的方法集合
List<SubscriberMethod> subscriberMethods =
subscriberMethodFinder.findSubscriberMethods(subscriberClass, forceReflection);
//3.將獲取的訂閱者和訂閱者中處理事件的方法按照一定的規(guī)則相結合并存儲到EventBus內部
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
register這個方法主要可以分為三部分。
第一步,獲取訂閱者的Class對象,這個訂閱者通常就是我們寫的Activity或者Fragment等。
第二步,findSubscriberMethods方法是關鍵,通過我們第一步拿到的訂閱者的Class對象,反射獲取所有符合規(guī)則的處理事件的方法。后面單獨看源碼
第三步,將獲取的訂閱者和訂閱者中處理事件的方法按照一定的規(guī)則相結合并存儲到EventBus內部
第一步很簡單,分析第二步的findSubscriberMethods方法:
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass, boolean forceReflection) {
String key = subscriberClass.getName();
List<SubscriberMethod> subscriberMethods;
//首先看緩存中是否已經存在該訂閱者的處理方法的集合
synchronized (METHOD_CACHE) {
subscriberMethods = METHOD_CACHE.get(key);
}
if (subscriberMethods != null) {
return subscriberMethods;
}
//查找事件處理方法有索引方式和反射方式兩種
if (INDEX != null && !forceReflection) {
//索引方式查找,其實一般默認進入當前 if 邏輯
subscriberMethods = findSubscriberMethodsWithIndex(subscriberClass);
if (subscriberMethods.isEmpty()) {
//索引方式查找不到,依舊通過反射查找
subscriberMethods = findSubscriberMethodsWithReflection(subscriberClass);
}
} else {
//反射方式查找
subscriberMethods = findSubscriberMethodsWithReflection(subscriberClass);
}
//如果當前訂閱者中的處理事件方法集合為空,則拋出異常;反之則加入緩存中并返回
if (subscriberMethods.isEmpty()) {
throw new EventBusException("Subscriber " + subscriberClass
+ " and its super classes have no public methods with the @Subscribe annotation");
} else {
synchronized (METHOD_CACHE) {
//加入緩存
METHOD_CACHE.put(key, subscriberMethods);
}
return subscriberMethods;
}
}
索引是什么,我們平常使用EventBus的時候從來都沒用過這個東西,其實在EventBus3.0之前是沒有索引這個東西的,第二步獲取方法集合走的是 findSubscriberMethodsWithReflection(subscriberClass); 這個方法。那為什么在3.0之后加上了索引呢?其實答案很簡單,就是因為反射獲取方法集合是比較低效的,使用反射所耗費的時間開銷比較大。如果使用索引,EventBus的性能會提升一倍。具體如何在你的代碼中使用EventBus的索引,可以參考 [greenrobot官網中index的介紹] ,實際應用中,我們使用EventBus根本沒添加過索引相關的配置,所以最終我們還是走反射邏輯(http://greenrobot.org/eventbus/documentation/subscriber-index/) 。
既然通過反射查找,接著看findSubscriberMethodsWithReflection方法
private List<SubscriberMethod> findSubscriberMethodsWithReflection(Class<?> subscriberClass) {
List<SubscriberMethod> subscriberMethods = new ArrayList<SubscriberMethod>();
Class<?> clazz = subscriberClass;
HashSet<String> eventTypesFound = new HashSet<String>();
StringBuilder methodKeyBuilder = new StringBuilder();
while (clazz != null) {
// Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again)
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
int modifiers = method.getModifiers();
//方法為public,且沒有final,static等修飾符
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
Class<?>[] parameterTypes = method.getParameterTypes();
//方法只能有一個參數(shù)
if (parameterTypes.length == 1) {
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
if (subscribeAnnotation != null) {
String methodName = method.getName();
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
ThreadMode threadMode = subscribeAnnotation.threadMode();
subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
}
}
}
}
}
clazz = clazz.getSuperclass();
}
return subscriberMethods;
}
該方法很明確,就是通過反射獲取訂閱者所有方法集合后,遍歷所有方法,查找符合規(guī)則的方法 ,而規(guī)則無非就是這些方法必須加 @subscribe 注解,參數(shù)必須有且只有一個等等
進入第三步,遍歷第二步獲取的方法集合,調用 subscribe(subscriber, subscriberMethod)
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
subscribe(subscriber, subscriberMethod)是最后一步的核心代碼
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
//獲取方法中的事件類型
Class<?> eventType = subscriberMethod.eventType;
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
//將訂閱者和方法綁在一起形成一個Subscription對象
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<Subscription>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
if (subscriptions.contains(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
}
}
//按照priority優(yōu)先級,將上面由訂閱者和方法綁在一起形成的Subscription對象加入subscriptions集合中
synchronized (subscriptions) {
int size = subscriptions.size();
for (int i = 0; i <= size; i++) {
if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.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);
//如果方法可以接收sticky事件
if (subscriberMethod.sticky) {
if (eventInheritance) {
Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
for (Map.Entry<Class<?>, Object> entry : entries) {
Class<?> candidateEventType = entry.getKey();
if (eventType.isAssignableFrom(candidateEventType)) {
Object stickyEvent = entry.getValue();
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
這段代碼雖然比較多,但看下來,功能就是初始化EventBus這個單例的的全局變量,主要是subscriptionsByEventType,typesBySubscriber, 通過名字我們也可以知道,都是map。
其中,subscriptionsByEventType 的key存的是eventType,value存的是Subscription的集合,記錄了每一個相同的事件類型所關聯(lián)的一切注冊者和他們的處理事件的方法。
typesBySubscriber的key是subscriber訂閱者,value是eventType的集合,它記錄了每一個訂閱者所注冊(care)的一切事件類型。
subscribe方法最后對sticky粘性事件做了處理,再次貼下后面的代碼
if (subscriberMethod.sticky) {
if (eventInheritance) {
//遍歷stickEvents這個map
Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
for (Map.Entry<Class<?>, Object> entry : entries) {
Class<?> candidateEventType = entry.getKey();
//如果找到了跟這個方法的eventType一致或是其子類的stickEvent
if (eventType.isAssignableFrom(candidateEventType)) {
Object stickyEvent = entry.getValue();
//發(fā)送事件,本質就是通過反射直接調用這個方法
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
什么是粘性事件?
簡單講,就是在發(fā)送事件之后再訂閱該事件也能收到該事件。Android中就有這樣的實例,也就是Sticky Broadcast,即粘性廣播。正常情況下如果發(fā)送者發(fā)送了某個廣播,而接收者在這個廣播發(fā)送后才注冊自己的Receiver,這時接收者便無法接收到 剛才的廣播,為此Android引入了StickyBroadcast,在廣播發(fā)送結束后會保存剛剛發(fā)送的廣播(Intent),這樣當接收者注冊完 Receiver后就可以接收到剛才已經發(fā)布的廣播。這就使得我們可以預先處理一些事件,讓有消費者時再把這些事件投遞給消費者.
EventBus也提供了這樣的功能,有所不同是EventBus會存儲所有的Sticky事件,如果某個事件在不需要再存儲則需要手動進行移除。用戶通過Sticky的形式發(fā)布事件,而消費者也需要通過Sticky的形式進行注冊,當然這種注冊除了可以接收 Sticky事件之外和常規(guī)的注冊功能是一樣的,其他類型的事件也會被正常處理。
繼續(xù)回到這段代碼,首先判斷了處理事件的方法是否有stick這個注解,如果存在則進入下一步。eventInheritance這個變量默認為true,代表事件是否具有繼承性。可以這么理解,發(fā)送一個事件或者發(fā)送這個事件類型的子類,EventBus的處理事件方法中的參數(shù)類型是父類型,那么這個方法既可以收到父類型的事件,也可以收到子類型的事件。那么如果處理事件方法中的參數(shù)類型是子類型呢?那這個方法就只能接收子類型的事件,而無法接收父類型的事件,這點是在if (eventType.isAssignableFrom(candidateEventType)) 這一行代碼做的判斷。isAssignableFrom這個方法是JDK中Class類中的一個方法,作用就是判斷前者是否是與后者類型一致或前者是后者的父類。下面這個代碼就很簡單了checkPostStickyEventToSubscription(newSubscription, stickyEvent);發(fā)送事件,我們跟進去看一下:
private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
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());
}
}
這個方法的功能很簡單,對事件做了非空校驗,然后調用了postToSubscription(newSubscription, stickyEvent, Looper.getMainLooper() == Looper.myLooper()) 方法,這個方法內部就是通過反射調用method.invoke(subscription.subscriber, event)來執(zhí)行我們定義的對事件的處理方法。這個方法很重要,后續(xù)在post方法的時候著重分析,現(xiàn)在需要知道它的功能即可??吹竭@里,也就能明白,為什么在粘性事件發(fā)出后再注冊的事件處理方法也可以接收到它了,因為之前發(fā)送的stickyEvent都會存入stickyEvents這個map類型的EventBus中的成員變量中,當我們注冊調用register方法時,在register方法內部會直接通過checkPostStickyEventToSubscription(newSubscription, stickyEvent);來執(zhí)行我們定義的粘性事件處理方法。
至此,終于說完了register這個方法,這個方法的內容看似只有短短幾行,但它所做的事情還是很繁瑣復雜的。