個(gè)人博客:haichenyi.com。感謝關(guān)注
??之前我們講過獲取EventBus對象的源碼,這一篇,我們來講講注冊的源碼。推薦EventBus 3.0進(jìn)階:源碼及其設(shè)計(jì)模式 完全解析
簡介
/**
* Registers the given subscriber to receive events. Subscribers must call {@link #unregister(Object)} once they
* are no longer interested in receiving events.
* <p/>
* Subscribers have event handling methods that must be annotated by {@link Subscribe}.
* The {@link Subscribe} annotation also allows configuration like {@link
* ThreadMode} and priority.
*/
?? 翻譯: 注冊給訂閱方去接收事件,訂閱者一旦對接收事件不感興趣了,就要unregister,訂閱者必須要有用Subscribe注解的方法,注解也可以設(shè)置線程和優(yōu)先級(jí)
?? 白話文: 訂閱者要是想接收消息,必須要先注冊。當(dāng)頁面退出,或者不想接收消息的時(shí)候必須要反注冊,不然他會(huì)一直處于接收消息的狀態(tài),頁面退出會(huì)內(nèi)存泄漏。訂閱者的接收方法必須要用Subscribe注解,這個(gè)注解的后面可以設(shè)置接收這個(gè)消息的線程和優(yōu)先級(jí)。如下:
@Subscribe(threadMode = ThreadMode.MAIN,priority = 100,sticky = true)
public void handleMsg(DataBean dataBean){
}
??就像上面這樣寫,我一個(gè)一個(gè)來講。我們先來說說這個(gè)ThreadMode類,點(diǎn)進(jìn)去,我們可以看到如下內(nèi)容:
/**
* 每個(gè)訂閱的方法都有一個(gè)線程,決定那個(gè)線程的方法被叫做EventBus
* EventBus的線程可以跟Post事件的那個(gè)線程不相同
*/
public enum ThreadMode {
/**
*訂閱者將在跟Post事件的那個(gè)線程的同一個(gè)線程中被調(diào)用,這是默認(rèn)值,
* 因?yàn)?,他沒有線程切換,所以開銷最少,所以也是推薦模式。需要注意的是
* post事件的線程可能是UI線程,也可能是其他線程,所以,這里的操作要做判斷,
* 如果是UI操作,你必須要在UI線程中完成,如果是耗時(shí)操作,你必須要新開線程
*/
POSTING,
/**
* 在Android上面,訂閱者將會(huì)在UI線程中調(diào)用,如果post事件的線程是UI線程,
* 辣么,這個(gè)訂閱方法將直接被調(diào)用,如果不是UI線程,辣么,它將要排隊(duì)交付,
* 所以,這里可能阻塞線程,訂閱者使用這個(gè)模式必須要快速返回,避免阻塞UI線程,
* 就是不要在這里做耗時(shí)操作。謝謝。
*/
MAIN,
/**
*這一個(gè),跟上面的剛好對應(yīng),就是不管怎么樣,都要排隊(duì)交付,
* 不論post事件是不是處于UI線程發(fā)送的
*/
MAIN_ORDERED,
/**
* 在android上面,訂閱方法將在子線程中調(diào)用。如果post事件處于子線程,
* 辣么,訂閱方法將直接被調(diào)用。如果post事件處于UI線程,辣么,eventBus
* 就會(huì)新開線程,按照順序處理事件,當(dāng)然,也要注意,避免阻塞子線程
*/
BACKGROUND,
/**
* 訂閱方法將會(huì)在獨(dú)立的線程中調(diào)用,這個(gè)線程總是獨(dú)立語post事件
* 所處的線程和主線程。如果post事件是耗時(shí)操作:例如網(wǎng)絡(luò)請求,
* 訂閱方法調(diào)用的時(shí)候,不會(huì)等待。我們不用考慮線程數(shù)量的問題,
* EventBus已經(jīng)限制了并發(fā)線程,并使用線程池高效的重用線程
*/
ASYNC
}
他就是一個(gè)枚舉類,幾個(gè)值的意義,我說的很清楚了。
??我們再來講講另外兩個(gè): sticky,默認(rèn)值是false,如果設(shè)置成true,辣么,這個(gè)事件將會(huì)是粘性事件。發(fā)送事件的方式從post變成了postSticky,其他都沒變。
??再來講講 priority ,默認(rèn)值是0,在同一個(gè)線程中值越大,優(yōu)先級(jí)越高。優(yōu)先級(jí)高的比優(yōu)先級(jí)低的先收到消息。
好,終于準(zhǔn)備工作做完了,我們來看看 register() 方法
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
??注冊方法。首先,他通過反射的方式獲得當(dāng)前類名,然后通過當(dāng)前類名,找到訂閱方法,存到list里面。我們來看看 findSubscriberMethods()方法
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
//首先從緩存中讀取當(dāng)前類的訂閱方法,如果不等于null,就直接返回從緩存中讀取到的list
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
//ignoreGeneratedIndex的值,從Builder可知,一般為false。
if (ignoreGeneratedIndex) {
subscriberMethods = findUsingReflection(subscriberClass);
} else {
subscriberMethods = findUsingInfo(subscriberClass);
}
if (subscriberMethods.isEmpty()) {
throw new EventBusException("Subscriber " + subscriberClass
+ " and its super classes have no public methods with the @Subscribe annotation");
} else {
//將獲取的subscriberMeyhods放入緩存中
METHOD_CACHE.put(subscriberClass, subscriberMethods);
return subscriberMethods;
}
}
??上面的注釋寫的很清楚,ignoreGeneratedIndex為false,辣么就會(huì)走findUsingInfo() 方法
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
//首先新建了一個(gè)FindState,F(xiàn)indState是一個(gè)靜態(tài)內(nèi)部類,保存訂閱者的信息
FindState findState = prepareFindState();
//初始化FindState
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
findState.subscriberInfo = getSubscriberInfo(findState);
//初始化的findState獲得的訂閱者信息,一般都是null
if (findState.subscriberInfo != null) {
SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
for (SubscriberMethod subscriberMethod : array) {
if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
findState.subscriberMethods.add(subscriberMethod);
}
}
} else {
//就會(huì)跳到這里
findUsingReflectionInSingleClass(findState);
}
//移動(dòng)到父類繼續(xù)查找
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
上面,我們提到了FindState類,我們來看看這個(gè)類的代碼
static class FindState {
//訂閱方法的列表
final List<SubscriberMethod> subscriberMethods = new ArrayList<>();
//以class的名稱為key,以方法為value
final Map<Class, Object> anyMethodByEventType = new HashMap<>();
//以方法名稱為key,訂閱者類為value
final Map<String, Class> subscriberClassByMethodKey = new HashMap<>();
final StringBuilder methodKeyBuilder = new StringBuilder(128);
Class<?> subscriberClass;
Class<?> clazz;
boolean skipSuperClasses;
SubscriberInfo subscriberInfo;
//初始化
void initForSubscriber(Class<?> subscriberClass) {
this.subscriberClass = clazz = subscriberClass;
skipSuperClasses = false;
subscriberInfo = null;
}
}
??不難看出,這里的幾個(gè)map包括了,類名找方法,方法名找類,我們后面都用的到,然后就是初始化方法,前面我們注釋里面寫了,初始化之后一般信息都是null,這里我們也可以看到。所以,它會(huì)走 findUsingReflectionInSingleClass
private void findUsingReflectionInSingleClass(FindState findState) {
Method[] methods;
try {
// This is faster than getMethods, especially when subscribers are fat classes like Activities
methods = findState.clazz.getDeclaredMethods();
} catch (Throwable th) {
// Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
methods = findState.clazz.getMethods();
findState.skipSuperClasses = true;
}
/*------------------------------上面就是獲取方法,重要的是在下面------------------------------------*/
//這里我強(qiáng)調(diào)的是我們前面的用法里面有說過注意點(diǎn)
//1.必須是public修飾
//2.必須是void類型
//3.必須是一個(gè)參數(shù)
//4.必須用Subscribe注解
for (Method method : methods) {
//獲取方法的修飾符
int modifiers = method.getModifiers();
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
//獲取方法參數(shù)類型
Class<?>[] parameterTypes = method.getParameterTypes();
//如果參數(shù)個(gè)數(shù)等于1
if (parameterTypes.length == 1) {
//獲取方法注解名稱
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
if (subscribeAnnotation != null) {
//參數(shù)類型 即為事件類型
Class<?> eventType = parameterTypes[0];
//調(diào)用checkAdd方法判斷是否添加過
if (findState.checkAdd(method, eventType)) {
//從注解里面獲取線程模式
ThreadMode threadMode = subscribeAnnotation.threadMode();
//新建一個(gè)SubscriberMethod對象,并添加到findState的subscriberMethods這個(gè)集合內(nèi)
findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
}
}
//如果開啟了嚴(yán)格驗(yàn)證,同時(shí)當(dāng)前方法又有@Subscribe注解,對不符合要求的方法會(huì)拋出異常
} else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
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)) {
String methodName = method.getDeclaringClass().getName() + "." + method.getName();
throw new EventBusException(methodName +
" is a illegal @Subscribe method: must be public, non-static, and non-abstract");
}
}
}
??這個(gè)方法非常重要?。。≡谶@個(gè)方法內(nèi)部,利用反射的方式,對訂閱者類進(jìn)行掃描判斷,是否滿足條件從而找出訂閱方法,并用上面的容器進(jìn)行保存。辣么,上面提到的 checkAdd() 方法是怎么檢查的呢?
boolean checkAdd(Method method, Class<?> eventType) {
// 2 level check: 1st level with event type only (fast), 2nd level with complete signature when required.
// Usually a subscriber doesn't have methods listening to the same event type.
Object existing = anyMethodByEventType.put(eventType, method);
if (existing == null) {
return true;
} else {
if (existing instanceof Method) {
if (!checkAddWithMethodSignature((Method) existing, eventType)) {
// Paranoia check
throw new IllegalStateException();
}
// Put any non-Method object to "consume" the existing Method
anyMethodByEventType.put(eventType, this);
}
return checkAddWithMethodSignature(method, eventType);
}
}
??這個(gè)注釋寫的很清楚,兩層檢驗(yàn),第一層是檢測事件類型,第二次檢驗(yàn)則是檢驗(yàn)判斷方法的完整,首先以eventType為鍵,方法為值,存到map中(這個(gè)map是在FindState類初始化的),put方法會(huì)有一個(gè)返回值,返回value,這個(gè)value是這個(gè)key對應(yīng)的上一個(gè)值,所以說,如果是第一次存放,那么就會(huì)返回null。否則,之前存放過,辣么就會(huì)進(jìn)入下一個(gè)判斷 checkAddWithMethodSignature
private boolean checkAddWithMethodSignature(Method method, Class<?> eventType) {
methodKeyBuilder.setLength(0);
methodKeyBuilder.append(method.getName());
methodKeyBuilder.append('>').append(eventType.getName());
String methodKey = methodKeyBuilder.toString();
Class<?> methodClass = method.getDeclaringClass();
Class<?> methodClassOld = subscriberClassByMethodKey.put(methodKey, methodClass);
if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) {
// Only add if not already found in a sub class
return true;
} else {
// Revert the put, old class is further down the class hierarchy
subscriberClassByMethodKey.put(methodKey, methodClassOld);
return false;
}
}
??這個(gè)方法就是用來判斷方法簽名是否相同的,方法簽名是什么呢?就是修飾符+返回類型+方法名+參數(shù)list是否相同。如果方法簽名相同,辣么,就把舊值賦值給methodClassOld,判斷這個(gè)值不是為null,第一次調(diào)用,沒有舊值,就肯定為null,所以,if前面的一個(gè)條件是滿足的,后面一個(gè)條件methodClassOld.isAssignableFrom(methodClass) 的意思是判斷舊值是否是methodClass或者同一個(gè)類,如果兩個(gè)條件都不滿足,辣么當(dāng)前方法就不會(huì)添加為訂閱方法。
??那么,說了一大堆關(guān)于checkAdd和checkAddWithMethodSignature方法的源碼,那么這兩個(gè)方法到底有什么作用呢?從這兩個(gè)方法的邏輯來看,第一層判斷根據(jù)eventType來判斷是否有多個(gè)方法訂閱該事件,而第二層判斷根據(jù)完整的方法簽名(包括方法名字以及參數(shù)名字)來判斷。下面是筆者的理解:
??第一種情況:比如一個(gè)類有多個(gè)訂閱方法,方法名不同,但它們的參數(shù)類型都是相同的(雖然一般不這樣寫,但不排除這樣的可能),那么遍歷這些方法的時(shí)候,會(huì)多次調(diào)用到checkAdd方法,由于existing不為null,那么會(huì)進(jìn)而調(diào)用checkAddWithMethodSignature方法,但是由于每個(gè)方法的名字都不同,因此methodClassOld會(huì)一直為null,因此都會(huì)返回true。也就是說,允許一個(gè)類有多個(gè)參數(shù)相同的訂閱方法。
??第二種情況:類B繼承自類A,而每個(gè)類都是有相同訂閱方法,換句話說,類B的訂閱方法繼承并重寫自類A,它們都有著一樣的方法簽名。方法的遍歷會(huì)從子類開始,即B類,在checkAddWithMethodSignature方法中,methodClassOld為null,那么B類的訂閱方法會(huì)被添加到列表中。接著,向上找到類A的訂閱方法,由于methodClassOld不為null而且顯然類B不是類A的父類,methodClassOld.isAssignableFrom(methodClass)也會(huì)返回false,那么會(huì)返回false。也就是說,子類繼承并重寫了父類的訂閱方法,那么只會(huì)把子類的訂閱方法添加到訂閱者列表,父類的方法會(huì)忽略。
??讓我們回到findUsingReflectionInSingleClass方法,當(dāng)遍歷完當(dāng)前類的所有方法后,會(huì)回到findUsingInfo方法,接著會(huì)執(zhí)行最后一行代碼,即return getMethodsAndRelease(findState);那么我們繼續(xù) getMethodsAndRelease
private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {
//從findState獲取subscriberMethods,放進(jìn)新的ArrayList
List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods);
//把findState回收
findState.recycle();
synchronized (FIND_STATE_POOL) {
for (int i = 0; i < POOL_SIZE; i++) {
if (FIND_STATE_POOL[i] == null) {
FIND_STATE_POOL[i] = findState;
break;
}
}
}
return subscriberMethods;
}
通過該方法,把subscriberMethods不斷逐層返回,直到返回EventBus#register()方法,最后開始遍歷每一個(gè)訂閱方法,并調(diào)用subscribe(subscriber, subscriberMethod)方法,那么,我們繼續(xù)來看subscribe方法。
// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class<?> eventType = subscriberMethod.eventType;
//將subscriber和subscriberMethod封裝成 Subscription
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
//根據(jù)事件類型獲取特定的 Subscription
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
//如果為null,說明該subscriber尚未注冊該事件
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
//如果不為null,并且包含了這個(gè)subscription 那么說明該subscriber已經(jīng)注冊了該事件,拋出異常
if (subscriptions.contains(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
}
}
//根據(jù)優(yōu)先級(jí)來設(shè)置放進(jìn)subscriptions的位置,優(yōu)先級(jí)高的會(huì)先被通知
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;
}
}
//根據(jù)subscriber(訂閱者)來獲取它的所有訂閱事件
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
//下面是對粘性事件的處理
if (subscriberMethod.sticky) {
//從EventBusBuilder可知,eventInheritance默認(rèn)為true
if (eventInheritance) {
// Existing sticky events of all subclasses of eventType have to be considered.
// Note: Iterating over all events may be inefficient with lots of sticky events,
// thus data structure should be changed to allow a more efficient lookup
// (e.g. an additional map storing sub classes of super classes: Class -> List<Class>).
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 {
//根據(jù)eventType,從stickyEvents列表中獲取特定的事件
Object stickyEvent = stickyEvents.get(eventType);
//分發(fā)事件
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
到目前為止,注冊流程基本分析完畢,丟一張流程圖
