1.EventBus 怎麼做到的。
* EventBus is a central publish/subscribe event system for Java and Android.
* Events are posted ({@link #post(Object)}) to the bus, which delivers it to subscribers that have a matching handler
* method for the event type.
我認爲,eventBus 其實不復雜,他勝出就勝出在使用簡單。當你register 的時候,它會根據你傳遞的對象,拿到對應的class. 然後根據class拿到所有public 的方法,然後根據註解信息判斷是不是eventBus 的註解方法,之後把註解方法,以及方法的參數(也就是消息的類型),註冊的對象等放到一個bean 裏面。然後把註冊的消息類型當做key, bean 的列表 當做value,保存到hasmap 裏面。當有人發消息的時候,從map 裏面找一個那些註冊了這個類型的消息,列表循環一下,調用他們的方法就行。
以下是EventBus 的一些成員變量的作用:
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
//key 是當前註冊的class value 是該class 和 其父類的class
private static final Map<Class<?>, List<Class<?>>> eventTypesCache = new HashMap<>();
//key 是當前發送的消息對象 value 是所有註冊該消息的Subscription 列表
private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
//key 是訂閱的對象 value 是該對象訂閱的所有的消息類型
private final Map<Object, List<Class<?>>> typesBySubscriber;
//粘性的消息map
private final Map<Class<?>, Object> stickyEvents;
EventBus.getDefault().register(this);
我們看下 EventBus.getDefault().register(this); 幹了什麼事情。
可以跟代碼,可以看到,調用了getClass 拿到class.
subscriber.getClass();
然後找到使用註解註冊eventBus的所有方法,代碼如下:
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
try {
methods = findState.clazz.getMethods();
} catch (LinkageError error) { // super class of NoClassDefFoundError to be a bit more broad...
String msg = "Could not inspect methods of " + findState.clazz.getName();
if (ignoreGeneratedIndex) {
msg += ". Please consider using EventBus annotation processor to avoid reflection.";
} else {
msg += ". Please make this class visible to EventBus annotation processor to avoid reflection.";
}
throw new EventBusException(msg, error);
}
findState.skipSuperClasses = true;
}
for (Method method : methods) {
int modifiers = method.getModifiers();
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length == 1) {
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
if (subscribeAnnotation != null) {
Class<?> eventType = parameterTypes[0];
if (findState.checkAdd(method, eventType)) {
ThreadMode threadMode = subscribeAnnotation.threadMode();
findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
}
}
} 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");
}
}
}
這個方法會用 findState.clazz 對象,找到所有的非集成的方法,然後拿到修飾符,只有public 的 並且不是abstract 、不是static 等方法,我就去獲取註解Subscribe 類型的註解。
如果你的註解不是空,那麼就拿到該方法的參數,這個參數就是訂閱的通知消息類型。
這時候我們拿到了註冊者,以及註冊的一些信息,比如是否在主線程裏面,比如註冊的消息類型是什麼,以及當前的方法本身,這些都會放到SubscriberMethod 裏面,因爲後面調用方法以及分發消息的時候,都會用到。
然後把這個消息類型當做key,把你調用註冊方法註冊的對象,放到key 對應的列表裏。
也就是:
//key 是當前發送的消息對象 value 是所有註冊該消息的Subscription 列表
private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
當我們unregiest 的時候,就把這個對象置isRegiest 爲false. 發送消息的時候,就不會發給你了。
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--;
size--;
}
}
以上代碼值得參考,因爲有些人邊遍歷邊刪除數據,會導致多線程操作異常,上面的代碼就不會。
2.註解怎麼使用的?怎麼在代碼裏面讀取?
首先定義註解接口 並且@Retention(RetentionPolicy.RUNTIME)
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
ThreadMode threadMode() default ThreadMode.POSTING;
/**
* If true, delivers the most recent sticky event (posted with
* {@link EventBus#postSticky(Object)}) to this subscriber (if event available).
*/
boolean sticky() default false;
/** Subscriber priority to influence the order of event delivery.
* Within the same delivery thread ({@link ThreadMode}), higher priority subscribers will receive events before
* others with a lower priority. The default priority is 0. Note: the priority does *NOT* affect the order of
* delivery among subscribers with different {@link ThreadMode}s! */
int priority() default 0;
}
然後通過class.getMethod.getAnnotation(Subscribe.class) 就可以拿到了。
4.爲什麼那樣寫就可以收到消息?
post(Message) 方法,調用的時候,就會拿到這個Message 的 類型,去map 裏面找所有的註冊者,找到之後,調用註冊者的方法。
這樣你就會收到消息了。
5.eventbus 怎麼拿到context? 怎麼切換線程的?
eventBus 沒有拿到context.
判斷是不是主線程,根據looper 判斷的:
Looper.getMainLooper() == Looper.myLooper();
當前的looper 是MainLooper 就說明是主線程。
6.爲什麼PostingThreadState 要放在threadLoacl裏面,不放在裏面有什麼問題嗎?
private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
@Override
protected PostingThreadState initialValue() {
return new PostingThreadState();
}
};
沒有仔細研究,有知道的留言一下。
7.爲什麼eventBus 的接受消息的方法,只能是public?
因爲getMethods() 只能拿到public 的方法。而且取方法的時候,還對方法的類型做了限制。
反思:
其實平時真的是業務開發, 是不可能像寫eventBus 這樣去寫的。但是一些功能,用這個獲取很取巧。很不錯。
缺點:
感覺不規範。