疑問:
EventBus 注冊(cè),發(fā)送事件裙盾,注銷時(shí)分別做了哪些操作?
1、EventBus.getDefault().register(Object)闷煤、
2童芹、EventBus.getDefault().post(Object)
3、EventBus.getDefault().unregister(Object)
1鲤拿、EventBus.getDefault().register(Object)
步驟一
首先通過 EventBus.getDefault() 獲取 EventBus 單例
//EventBus 單例對(duì)象
static volatile EventBus defaultInstance;
//默認(rèn) EventBusBuilder
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
//獲取單例 EventBus
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
public EventBus() {
this(DEFAULT_BUILDER);
}
//初始默認(rèn)值
EventBus(EventBusBuilder builder) {
subscriptionsByEventType = new HashMap<>();
typesBySubscriber = new HashMap<>();
stickyEvents = new ConcurrentHashMap<>();
mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
backgroundPoster = new BackgroundPoster(this);
asyncPoster = new AsyncPoster(this);
indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
builder.strictMethodVerification, builder.ignoreGeneratedIndex);
logSubscriberExceptions = builder.logSubscriberExceptions;
logNoSubscriberMessages = builder.logNoSubscriberMessages;
sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
throwSubscriberException = builder.throwSubscriberException;
eventInheritance = builder.eventInheritance;
executorService = builder.executorService;
}
下面再看 register() 方法
public void register(Object subscriber) {
//1假褪、獲取注冊(cè)對(duì)象的 class 對(duì)象 subscriberClass
Class<?> subscriberClass = subscriber.getClass();
//2、通過 class 對(duì)象 subscriberClass 找到該 class 的 SubscriberMethod 集合
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
//3近顷、訂閱注冊(cè)對(duì)象支持注解 @Subscribe 的方法
subscribe(subscriber, subscriberMethod);
}
}
}
其中 SubscriberMethod 用來保存訂閱方法的信息
public class SubscriberMethod {
//方法
final Method method;
//線程類型(ThreadMode.POST生音、ThreadMode.MAIN...)
final ThreadMode threadMode;
//事件類型,即發(fā)送的事件
final Class<?> eventType;
//優(yōu)化級(jí)
final int priority;
//是否粘性事件
final boolean sticky;
}
第二步找到注冊(cè)對(duì)象中 @Subscribe 注解的方法列表窒升,并將每個(gè)方法保存在 SubscriberMethod 中缀遍。第三步訂閱注解的方法列表。下面先看第二步
步驟二
class SubscriberMethodFinder {
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
//METHOD_CACHE 是一個(gè) Map饱须,用來緩存注冊(cè)對(duì)象 Class 內(nèi)所有的被@Subscribe注解的方法集合
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
//優(yōu)先從緩存中取
if (subscriberMethods != null) {
return subscriberMethods;
}
//ignoreGeneratedIndex 默認(rèn)值為 false域醇,
if (ignoreGeneratedIndex) {
subscriberMethods = findUsingReflection(subscriberClass);
} else {
//2.1、查找注冊(cè)對(duì)象訂閱方法列表
subscriberMethods = findUsingInfo(subscriberClass);
}
if (subscriberMethods.isEmpty()) {
throw new EventBusException("Subscriber " + subscriberClass
+ " and its super classes have no public methods with the @Subscribe annotation");
} else {
//查找到的訂閱方法加入到緩存中蓉媳,key 為注冊(cè)對(duì)象的 class
METHOD_CACHE.put(subscriberClass, subscriberMethods);
return subscriberMethods;
}
}
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
//初使化 FindState 對(duì)象
FindState findState = prepareFindState();
//給 findState.subscriberClass 賦值譬挚,關(guān)聯(lián)注冊(cè)對(duì)象
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
//subscriberInfo 默認(rèn)為空,高級(jí)用法暫不分析
findState.subscriberInfo = getSubscriberInfo(findState);
if (findState.subscriberInfo != null) {
...
}
} else {
//2.2 使用反射查找注冊(cè)對(duì)象的訂閱方法集合酪呻,存放在 findState 中
findUsingReflectionInSingleClass(findState);
}
//移到父類减宣,繼續(xù) while 循環(huán)
findState.moveToSuperclass();
}
//從 findState 中獲取訂閱方法集合,并釋放 findState 對(duì)象
return getMethodsAndRelease(findState);
}
//享元模式玩荠,降低創(chuàng)建 FindState 對(duì)象的數(shù)量漆腌,減小內(nèi)存開銷
private static final FindState[] FIND_STATE_POOL = new FindState[POOL_SIZE];
private FindState prepareFindState() {
synchronized (FIND_STATE_POOL) {
for (int i = 0; i < POOL_SIZE; i++) {
FindState state = FIND_STATE_POOL[i];
if (state != null) {
FIND_STATE_POOL[i] = null;
return state;
}
}
}
return new FindState();
}
//重置 FindState 狀態(tài),并返回注冊(cè)對(duì)象訂閱的方法集合
private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {
List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods);
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;
}
}
先具體看下 moveToSuperclass()阶冈,將 clazz 賦值給父類闷尿,所以也會(huì)去父類中查找訂單事件,上面的 while 循環(huán) clazz == null
void moveToSuperclass() {
if (skipSuperClasses) {
clazz = null;
} else {
clazz = clazz.getSuperclass();
String clazzName = clazz.getName();
/** Skip system classes, this just degrades performance. */
if (clazzName.startsWith("java.") || clazzName.startsWith("javax.") || clazzName.startsWith("android.")) {
clazz = null;
}
}
}
再去看 findUsingReflectionInSingleClass()女坑,使用反射查找注冊(cè)對(duì)象的訂閱方法集合
private void findUsingReflectionInSingleClass(FindState findState) {
Method[] methods;
try {
// 查找注冊(cè)對(duì)象的所有方法
// 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;
}
for (Method method : methods) {
//獲取方法的修飾符(public填具、private)
int modifiers = method.getModifiers();
//方法的修飾符必須是 public
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
//獲取該方法入?yún)⒓? Class<?>[] parameterTypes = method.getParameterTypes();
//入?yún)⒅挥幸粋€(gè)
if (parameterTypes.length == 1) {
//獲取被 @Subscribe 注解的方法
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
if (subscribeAnnotation != null) {
//獲取參數(shù)類型
Class<?> eventType = parameterTypes[0];
if (findState.checkAdd(method, eventType)) {
//獲取線程模型
ThreadMode threadMode = subscribeAnnotation.threadMode();
//方法名、參數(shù)堂飞、線程類型灌旧、粘性 信息存放在 SubscriberMethod 中绑咱,放加入到 subscriberMethods 集合中
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");
}
}
}
以上完成步驟 2 中對(duì)象注冊(cè)對(duì)象的訂閱方法集合绰筛,下面梳理一下:
1、獲取單例 EventBus 對(duì)象描融,并獲取注冊(cè)對(duì)象的 class 對(duì)象 subscriberClass
2铝噩、獲取注冊(cè)對(duì)象的訂閱方法集合 subscriberMethods,優(yōu)先從緩存 METHOD_CACHE 中以 class 為 key 獲取其 value 窿克,獲取不到再通過反射獲取骏庸。
3毛甲、獲取不到時(shí),初例化 FindState 對(duì)象具被,將 subscriberClass 賦值給 FindState.clazz
4玻募、反射獲取 subscriberClass 的所有方法,遍歷所有方法一姿,將 public 修飾七咧、只有一個(gè)參數(shù)且添加 Subscribe 注解的方法名,包裝成 SubscriberMethod 對(duì)象并添加到 subscriberMethods 中叮叹。
5艾栋、將 subscriberClass 的父類賦值給 FindState.clazz,獲取父類的訂閱方法蛉顽,重復(fù)第 4 步蝗砾,直到?jīng)]有父類。
6携冤、重置 FindState 對(duì)象狀態(tài)悼粮,返回注冊(cè)對(duì)象的訂閱方法集合 subscriberMethods
步驟三
正式注冊(cè)訂閱
// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
//獲取 event 類型
Class<?> eventType = subscriberMethod.eventType;
//將注冊(cè)對(duì)象 subscriber 和 訂閱方法 subscriberMethod 包裝在 Subscription 中
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
//獲取 event 的訂閱集合 subscriptions,可以理解成用來存放所有注冊(cè)對(duì)象訂閱過該 event 的方法
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions == null) {
//新建并加入緩存
subscriptions = new CopyOnWriteArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
if (subscriptions.contains(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
}
}
//根據(jù)優(yōu)先級(jí)大小排序噪叙,將大的插入到集合前面矮锈,所以在事件分發(fā)時(shí)就會(huì)優(yōu)先獲取
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;
}
}
//將 subscribedEvents 以 subscriber 為 key 添加到 typesBySubscriber 中即注冊(cè)對(duì)象,unreigster 時(shí)會(huì)進(jìn)行 remove
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
//處理粘性事件
if (subscriberMethod.sticky) {
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();
//如果 candidateEventType 是 eventType 的子類或者接口睁蕾,發(fā)送粘性事件
if (eventType.isAssignableFrom(candidateEventType)) {
Object stickyEvent = entry.getValue();
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
以上則是注冊(cè)對(duì)象的流程苞笨,下面看發(fā)送事件 post()
2、EventBus.getDefault().post(Object)
public void post(Object event) {
//獲取 PostingThreadState子眶,currentPostingThreadState 是 ThreadLocal瀑凝,可以當(dāng)前線程的 PostingThreadState 對(duì)象
PostingThreadState postingState = currentPostingThreadState.get();
List<Object> eventQueue = postingState.eventQueue;
//把 event 添加到事件集合中
eventQueue.add(event);
if (!postingState.isPosting) {
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()) {
//1、當(dāng)事件不為空時(shí)臭杰,發(fā)送消息
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
//提供 PostingThreadState
private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
@Override
protected PostingThreadState initialValue() {
return new PostingThreadState();
}
};
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
//eventInheritance 默認(rèn)為 true
if (eventInheritance) {
//查找 event 的所有 Class 對(duì)象粤咪,包括超類和接口,優(yōu)先從緩存讀取
List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
int countTypes = eventTypes.size();
for (int h = 0; h < countTypes; h++) {
Class<?> clazz = eventTypes.get(h);
//2渴杆、發(fā)送事件
subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
}
} else
...
}
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
//獲取訂閱過 event 的所有注冊(cè)對(duì)象的所有訂閱方法
subscriptions = subscriptionsByEventType.get(eventClass);
}
if (subscriptions != null && !subscriptions.isEmpty()) {
for (Subscription subscription : subscriptions) {
...
//3寥枝、真正發(fā)送 event 事件
postToSubscription(subscription, event, postingState.isMainThread);
...
}
return true;
}
return false;
}
//發(fā)送事件
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
//訂閱的線程模式為當(dāng)前線程
case POSTING:
//4、執(zhí)行訂閱方法
invokeSubscriber(subscription, event);
break;
//訂閱的線程模式為主線程
case MAIN:
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
mainThreadPoster.enqueue(subscription, event);
}
break;
//訂閱的線程模式為子線程
case BACKGROUND:
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);
}
}
void invokeSubscriber(Subscription subscription, Object event) {
try {
//反射執(zhí)行該訂閱方法磁奖!
subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
} catch (InvocationTargetException e) {
handleSubscriberException(subscription, event, e.getCause());
} catch (IllegalAccessException e) {
throw new IllegalStateException("Unexpected exception", e);
}
}
第四步在主線程囊拜、后臺(tái)線程執(zhí)行用的是 Handler 、Thread 等實(shí)現(xiàn)原理較簡單比搭,最終調(diào)用訂閱方式一樣都是 invokeSubscriber冠跷,故不做分析。
下面把 post() 流程梳理一下:
1、獲取當(dāng)前線程對(duì)象蜜托,把 event 添加到 eventQueue 中抄囚,遍歷 eventQueue 不為空時(shí),發(fā)送消息橄务。
2幔托、查找 event 的所有 Class 對(duì)象,包括超類和接口蜂挪,優(yōu)先從緩存讀取柑司,lookupAllEventTypes()。
3锅劝、取訂閱過 event 的所有注冊(cè)對(duì)象的所有訂閱方法攒驰,遍歷一個(gè)個(gè)去發(fā)送事件。
4故爵、處理訂閱線程模式玻粪,使用反射執(zhí)行訂閱方法。
3诬垂、EventBus.getDefault().unregister(Object)
unregister 比較簡單劲室,只移除注冊(cè)即可
public synchronized void unregister(Object subscriber) {
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
if (subscribedTypes != null) {
for (Class<?> eventType : subscribedTypes) {
unsubscribeByEventType(subscriber, eventType);
}
//移除注冊(cè)
typesBySubscriber.remove(subscriber);
} else {
Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
知識(shí)點(diǎn)小結(jié):
1、EventBus 使用了享元模式结窘,降低了內(nèi)存中對(duì)象的數(shù)據(jù)很洋。
2、使用了線程安全的 CopyOnWriteArrayList 確保數(shù)據(jù)安全隧枫。
3喉磁、使用 ThreadLocal 確保對(duì)象線程間共享。
4官脓、使用反射執(zhí)行訂閱方法