相信做Android開發(fā)的很少有沒聽說過EventBus的,如果真沒聽過那只能說明你Out了。EventBus是針對(duì)Android平臺(tái)優(yōu)化了的發(fā)布/訂閱事件總線,它的目的是簡化Android組件以及線程間的通信,從而使代碼更加簡潔。
官方給的流程圖
簡單使用
- 定義事件類型
public class MessageEvent{}
- 訂閱者訂閱事件
eventBus.register(this)
- 發(fā)送事件
eventBus.post(messageEvent)
通過以上三步,就完成一次完整的EventBus使用流程。
其實(shí)類似這種發(fā)布/訂閱的機(jī)制在生活中用的也挺多的。比如我晚上定個(gè)8點(diǎn)的鬧鐘,就相當(dāng)于訂閱了一個(gè)事件,這個(gè)事件就是'8點(diǎn)鐘的鬧鐘'。當(dāng)?shù)诙?點(diǎn)鬧鐘響的時(shí)候,就相當(dāng)于發(fā)送了事件。我就會(huì)收到這個(gè)事件,然后執(zhí)行相應(yīng)的動(dòng)作,比如起床。
那么,既然是訂閱/發(fā)布。肯定就會(huì)有訂閱,有發(fā)布了。這篇文章先根據(jù)源碼來研究下訂閱事件的流程和原理。
一般我們都會(huì)像這樣EventBus.getDefault().register(this)
使用EventBus。這里首先會(huì)創(chuàng)建一個(gè)默認(rèn)的EventBus對(duì)象,然后調(diào)用EventBus的register(Object subscriber)
方法將當(dāng)前類作為訂閱者進(jìn)行事件的訂閱。
/**
Convenience singleton for apps using a process-wide EventBus instance.
*/
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
可以看到,源碼是通過單例模式來維護(hù)一個(gè)EventBus的實(shí)例的。從注釋中看以看到,這個(gè)實(shí)例的作用于為整個(gè)進(jìn)程。
這里的關(guān)鍵點(diǎn)就是register(Object subscriber)
方法了,上源碼:
public void register(Object subscriber) {
register(subscriber, false, 0);
}
private synchronized void register(Object subscriber, boolean sticky, int priority) {
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass());
for (SubscriberMethod subscriberMethod : subscriberMethods) {
//遍歷所有訂閱方法進(jìn)行訂閱
subscribe(subscriber, subscriberMethod, sticky, priority);
}
}
可以看到register(Object subscriber)
方法內(nèi)部調(diào)用了重載的方法register(Object subscriber, boolean sticky, int priority)
,所有的邏輯都在這個(gè)重載的方法內(nèi)部實(shí)現(xiàn)。
首先執(zhí)行subscriberMethodFinder.findSubscriberMethods(subscriber.getClass())
,我們跟蹤到findSubscriberMethods(Class<?> subscriberClass)
方法內(nèi)部去看看。
//尋找subscriberClass中所有的訂閱方法
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
String key = subscriberClass.getName();
List<SubscriberMethod> subscriberMethods;
synchronized (methodCache) {//先從緩存中根據(jù)類名找對(duì)應(yīng)的方法,如果沒找到說明這個(gè)類之前沒有注冊(cè)過
subscriberMethods = methodCache.get(key);
}
if (subscriberMethods != null) {//如果找到了則直接返回,不用再查找了
return subscriberMethods;
}
subscriberMethods = new ArrayList<SubscriberMethod>();
Class<?> clazz = subscriberClass;
HashMap<String, Class> eventTypesFound = new HashMap<String, Class>();
StringBuilder methodKeyBuilder = new StringBuilder();
while (clazz != null) {
String name = clazz.getName();
if (name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("android.")) {//排除系統(tǒng)的一些類
// Skip system classes, this just degrades performance
break;
}
// Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again)
try {
// This is faster than getMethods, especially when subscribers a fat classes like Activities
Method[] methods = clazz.getDeclaredMethods();//拿到訂閱者中聲明的所有方法
filterSubscriberMethods(subscriberMethods, eventTypesFound, methodKeyBuilder, methods);//對(duì)方法進(jìn)行過濾,找到符合訂閱規(guī)則的訂閱方法
} catch (Throwable th) {
th.printStackTrace();
// Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
Method[] methods = subscriberClass.getMethods();
subscriberMethods.clear();
eventTypesFound.clear();
filterSubscriberMethods(subscriberMethods, eventTypesFound, methodKeyBuilder, methods);
break;
}
clazz = clazz.getSuperclass();
}
if (subscriberMethods.isEmpty()) {//查找完后如果沒找到,報(bào)異常:該類訂閱了EventBus,但沒有對(duì)應(yīng)的訂閱方法
throw new EventBusException("Subscriber " + subscriberClass + " has no public methods called "
+ ON_EVENT_METHOD_NAME);
} else {
synchronized (methodCache) {//如果找到了,則存起來。防止重復(fù)查找。這里的key就是方法名
methodCache.put(key, subscriberMethods);
}
return subscriberMethods;
}
}
//對(duì)方法進(jìn)行過濾,篩選出符合事件接收的方法 形如 "public void onEvent**(Object event)"
private void filterSubscriberMethods(List<SubscriberMethod> subscriberMethods,
HashMap<String, Class> eventTypesFound, StringBuilder methodKeyBuilder,
Method[] methods) {
for (Method method : methods) {
String methodName = method.getName();
if (methodName.startsWith(ON_EVENT_METHOD_NAME)) {//方法以onEvent開頭
int modifiers = method.getModifiers(); //方法的限定符類型 public private等
Class<?> methodClass = method.getDeclaringClass();//聲明方法的類
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
//方法是public的 且非abstract static (bridge synthetic 這兩個(gè)啥意思?)
Class<?>[] parameterTypes = method.getParameterTypes(); //方法參數(shù)類型
if (parameterTypes.length == 1) {//方法只有一個(gè)參數(shù)
ThreadMode threadMode = getThreadMode(methodClass, method, methodName);
if (threadMode == null) {//不是訂閱方法,繼續(xù)迭代下一個(gè)方法
continue;
}
Class<?> eventType = parameterTypes[0];
methodKeyBuilder.setLength(0);
methodKeyBuilder.append(methodName);
methodKeyBuilder.append('>').append(eventType.getName());
String methodKey = methodKeyBuilder.toString();
Class methodClassOld = eventTypesFound.put(methodKey, methodClass);//以方法名和event類型名作為key
//存入訂閱方法列表中
if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) {
// Only add if not already found in a sub class
subscriberMethods.add(new SubscriberMethod(method, threadMode, eventType));
} else {
// Revert the put, old class is further down the class hierarchy
eventTypesFound.put(methodKey, methodClassOld);
}
}
} else if (!skipMethodVerificationForClasses.containsKey(methodClass)) {
Log.d(EventBus.TAG, "Skipping method (not public, static or abstract): " + methodClass + "."
+ methodName);
}
}
}
}
//判斷訂閱方法需要在哪個(gè)線程中調(diào)用
private ThreadMode getThreadMode(Class<?> clazz, Method method, String methodName) {
String modifierString = methodName.substring(ON_EVENT_METHOD_NAME.length());//截取方法名中onEvent后的字符
//如 onEventMainThread -> MainThread
ThreadMode threadMode;
if (modifierString.length() == 0) { //方法名為onEVent 默認(rèn)在PostThread中調(diào)用
threadMode = ThreadMode.PostThread;
} else if (modifierString.equals("MainThread")) {//方法名為onEVentMainThread 在MainThread中調(diào)用
threadMode = ThreadMode.MainThread;
} else if (modifierString.equals("BackgroundThread")) {//方法名為onEVentBackgroundThread 在BackgroundThread中調(diào)用
threadMode = ThreadMode.BackgroundThread;
} else if (modifierString.equals("Async")) {//方法名為onEVentAsync 異步調(diào)用
threadMode = ThreadMode.Async;
} else {//如果不是以上幾個(gè)選擇 認(rèn)為不是訂閱方法 threadMode為null
if (!skipMethodVerificationForClasses.containsKey(clazz)) {
throw new EventBusException("Illegal onEvent method, check for typos: " + method);
} else {
threadMode = null;
}
}
return threadMode;
}
上面代碼比較長,注釋的已經(jīng)很清楚了。findSubscriberMethods(Class<?> subscriberClass)
執(zhí)行完之后,就找到了訂閱者類中所有的能夠接受事件的方法并存了起來。
我們繼續(xù)回到register(Object subscriber, boolean sticky, int priority)
方法中,查詢完訂閱方法后,對(duì)所有的方法進(jìn)行迭代,然后調(diào)用subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority)
進(jìn)行一一注冊(cè)。
// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority) {
Class<?> eventType = subscriberMethod.eventType;
//根據(jù)事件類型取出該類事件的所有訂閱者
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
Subscription newSubscription = new Subscription(subscriber, subscriberMethod, priority);
if (subscriptions == null) {//如果沒有該類型的訂閱者 創(chuàng)建一個(gè) 加入列表
subscriptions = new CopyOnWriteArrayList<Subscription>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
if (subscriptions.contains(newSubscription)) {//如果所有訂閱者中已經(jīng)包含新加入的這個(gè)訂閱者,提示已經(jīng)訂閱了
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
}
}
// Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again)
// subscriberMethod.method.setAccessible(true);
int size = subscriptions.size();
//遍歷所有的訂閱者 按照優(yōu)先級(jí)(Subscription的priority屬性)進(jìn)行排序
for (int i = 0; i <= size; i++) {
if (i == size || newSubscription.priority > subscriptions.get(i).priority) {
subscriptions.add(i, newSubscription);
break;
}
}
//取出某個(gè)訂閱者中所有的訂閱事件
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<Class<?>>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
if (sticky) {//這塊是對(duì)黏性事件的處理。目前項(xiàng)目中還沒用過,不太理解這塊
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 {
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
當(dāng)subscribe(Object subscriber, SubscriberMethod subscriberMethod, boolean sticky, int priority)
方法執(zhí)行完之后,所有的訂閱者以及訂閱者中的事件接受方法就被存起來了。
//按事件類型存放的訂閱者集合
private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
//按訂閱者類型存放的事件集合
private final Map<Object, List<Class<?>>> typesBySubscriber;
//黏性事件(這個(gè)怎么理解?)
private final Map<Class<?>, Object> stickyEvents;
至此,事件的訂閱就完成了。最終的結(jié)果就是EventBus保存了訂閱者中所有符合規(guī)則(即public void onEvent***(Object event)
)的能接收事件的方法。當(dāng)post事件的時(shí)候,就會(huì)拿著這個(gè)事件去保存的訂閱者方法中去對(duì)比尋找,找到后通過反射去調(diào)用對(duì)應(yīng)的方法。