簡介
EventBus是針對(duì)Android優(yōu)化的發(fā)布-訂閱事件總線,簡化了Android組件間的通信。EventBus以其簡單易懂、優(yōu)雅撬统、開銷小等優(yōu)點(diǎn)而備受歡迎,基本在每一個(gè)APP都會(huì)用到敦迄,Eventbus源碼較為簡單宪摧,可作為開源框架源碼學(xué)習(xí)入門教材
如何使用
gradle中添加依賴:
implementation 'org.greenrobot:eventbus:3.2.0'
在需要發(fā)布事件的組件里調(diào)用如下代碼即可實(shí)現(xiàn)事件發(fā)送
EventBus.getDefault().post(TestEvent())
在需要訂閱事件的頁面進(jìn)行注冊(cè),并添加相應(yīng)@Subscribe注解即可實(shí)現(xiàn)事件訂閱
override fun onStart() {
super.onStart()
EventBus.getDefault().register(this)
}
override fun onStop() {
super.onStop()
EventBus.getDefault().unregister(this)
}
@Subscribe(threadMode = ThreadMode.MAIN)
public fun onRecEvent(event:TestEvent) {
Log.e(TAG, "收到事件:$event")
}
源碼分析(register和unregister)
EventBus.getDefault() 由字面感覺是個(gè)單例颅崩,點(diǎn)進(jìn)去看看:
static volatile EventBus defaultInstance;
/** Convenience singleton for apps using a process-wide EventBus instance. */
//可以看到是采用雙重鎖的一個(gè)單例模式
public static EventBus getDefault() {
EventBus instance = defaultInstance;
if (instance == null) {
synchronized (EventBus.class) {
instance = EventBus.defaultInstance;
if (instance == null) {
instance = EventBus.defaultInstance = new EventBus();
}
}
}
return instance;
}
register注冊(cè)
/**
* 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.
*/
public void register(Object subscriber) {
//獲取訂閱者對(duì)象類型
Class<?> subscriberClass = subscriber.getClass();
//獲取該訂閱者的所有訂閱方法(即被打上 @Subscribe()注解的方法)
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
//上鎖
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
//遍歷逐一訂閱每一個(gè)方法
subscribe(subscriber, subscriberMethod);
}
}
}
上述流程几于,第一步先是獲取了subscriber的對(duì)象類型,然后通過findSubscriberMethods()方法獲取了該subscriber的所有訂閱方法沿后,并將每個(gè)方法包裝為SubscriberMethod對(duì)象沿彭,最后遍歷逐一調(diào)用subscribe()去訂閱每一個(gè)方法。
這里看一下findSubscriberMethods()是如何查找訂閱者所訂閱的方法并將其包裝為SubscriberMethod返回的:
//緩存map尖滚,key是訂閱者對(duì)象類型喉刘,value是該訂閱者下的所有訂閱方法
private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>();
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
//1.先從緩存獲取該訂閱者下的所有訂閱方法瞧柔,獲取到的話直接返回
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
//是否忽略注解器生成的MyEventBusIndex
//默認(rèn)ignoreGeneratedIndex為false,即忽略睦裳,走findUsingInfo分支
if (ignoreGeneratedIndex) {
subscriberMethods = findUsingReflection(subscriberClass);
} else {
subscriberMethods = findUsingInfo(subscriberClass);
}
//找不到@Subscribe()注解的方法造锅,拋出異常,這就是平時(shí)調(diào)用EventBus.getDefault().register(this)然后沒有注冊(cè)接收事件閃退的原因
if (subscriberMethods.isEmpty()) {
throw new EventBusException("Subscriber " + subscriberClass
+ " and its super classes have no public methods with the @Subscribe annotation");
} else {
//查找成功后放入緩存廉邑,下次直接用
METHOD_CACHE.put(subscriberClass, subscriberMethods);
return subscriberMethods;
}
}
ignoreGeneratedIndex不設(shè)置的話默認(rèn)為false哥蔚,所以這里直接走到了findUsingInfo()方法,看一下findUsingInfo()具體是怎么查找訂閱方法的:
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
//這里通過FindState來儲(chǔ)存找到的方法信息
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
// 循環(huán)蛛蒙,從當(dāng)前類開始遍歷該類的所有父類
while (findState.clazz != null) {
//獲取訂閱者的信息糙箍,因?yàn)闆]有默認(rèn)沒有使用MyEventBusIndex,這里獲取到會(huì)是null牵祟,走findUsingReflectionInSingleClass分支
findState.subscriberInfo = getSubscriberInfo(findState);
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 {
//正常沒有使用MyEventBusIndex來到這里
findUsingReflectionInSingleClass(findState);
}
// 將findState.clazz設(shè)置為當(dāng)前的findState.clazz的父類
findState.moveToSuperclass();
}
//獲取methodsList并釋放資源
return getMethodsAndRelease(findState);
}
static class FindState {
//某個(gè)訂閱者下的所有訂閱方法(這個(gè)list就是我們最終要拿到返回回去到第一步的)
final List<SubscriberMethod> subscriberMethods = new ArrayList<>();
//map集合诺苹,key為EventType汁果,value為訂閱者的某一個(gè)方法
final Map<Class, Object> anyMethodByEventType = new HashMap<>();
//map集合谓传,key為訂閱方法名等參數(shù)拼接起來的string槽袄,value為方法的getDeclaringClass()
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;
}
//省略
.....
}
上一步findUsingInfo()主要是走到了findUsingReflectionInSingleClass()這個(gè)方法里凡蜻,那他又是如何查找訂閱方法的呢。單看命名筹淫,翻譯過來好像是“使用反射查找”?呢撞?损姜?,即使用反射從訂閱者中得到訂閱方法殊霞?摧阅?看一下代碼:
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;
}
//遍歷找到的方法數(shù)組
for (Method method : methods) {
//獲取方法聲明的修飾符,如private绷蹲,public棒卷,protected等
int modifiers = method.getModifiers();
//進(jìn)行位運(yùn)算,取出用public修飾的方法祝钢,這就是為什么我們聲明接收事件必須要使用public的原因
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
//獲取該方法下的參數(shù)類型
Class<?>[] parameterTypes = method.getParameterTypes();
//只處理一個(gè)參數(shù)的方法
if (parameterTypes.length == 1) {
//獲取該方法下的注解比规,用來從注解中獲取聲明的信息
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
if (subscribeAnnotation != null) {
// 獲取該方法的第一個(gè)參數(shù)對(duì)象
Class<?> eventType = parameterTypes[0];
// 檢查是否需要添加進(jìn)findState的訂閱方法列表,后面看怎么較驗(yàn)
if (findState.checkAdd(method, eventType)) {
//獲取注解的threadMode 信息拦英,即我們常使用的@Subscribe(threadMode = ThreadMode.MAIN)
ThreadMode threadMode = subscribeAnnotation.threadMode();
//添加進(jìn)訂閱方法列表
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");
}
}
}
由上面代碼可以知道蜒什,findUsingReflectionInSingleClass()確實(shí)是使用了很多反射方法,第一步先調(diào)用getDeclaredMethods獲取了Method[]數(shù)組疤估,然后遍歷該方法數(shù)組灾常,取出用public修飾符修飾且只有單個(gè)參數(shù)的方法霎冯,緊接著取出該方法下的注解(必須是Subscribe類型的注解),然后通過調(diào)用findState.checkAdd(method, eventType)方法钞瀑,判斷是否需要將該方法添加進(jìn)findState.subscriberMethods數(shù)組中沈撞,這個(gè)subscriberMethods數(shù)組也就是我們最終要返回回第一步的數(shù)據(jù)源。
看一下findState.checkAdd(method, eventType)方法是如何判斷是如何較驗(yàn)這個(gè)訂閱方法是否有效的
static class FindState {
//省略
.....
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.
//map集合雕什,key為EventType缠俺,value為訂閱者的某一個(gè)method方法
Object existing = anyMethodByEventType.put(eventType, method);
if (existing == null) {
//之前沒有其他方法訂閱了該事件類型,返回true,表示需要添加進(jìn)subscriberMethods數(shù)組
return true;
} else {
if (existing instanceof Method) {
//之前已經(jīng)有方法訂閱過該事件類型监徘,例如void1 void2都訂閱接受String類型晋修,則此時(shí)會(huì)走到這個(gè)分支,進(jìn)行二次較驗(yàn)
if (!checkAddWithMethodSignature((Method) existing, eventType)) {
// Paranoia check
throw new IllegalStateException();
}
// Put any non-Method object to "consume" the existing Method
//放進(jìn)map
anyMethodByEventType.put(eventType, this);
}
return checkAddWithMethodSignature(method, eventType);
}
}
//二次較驗(yàn)
private boolean checkAddWithMethodSignature(Method method, Class<?> eventType) {
//key為方法名+"<"+事件類型
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
// 首次注冊(cè)methodClassOld為null凰盔,符合條件墓卦,之后methodClassOld不為空
//使用methodClassOld.isAssignableFrom(methodClass)判斷methodClassOld是否為methodClass的父類,然后之前了解到我們是從子類開始遍歷到父類的户敬,
//顯然這個(gè)條件不滿足落剪,這也是使用EventBus的時(shí)候子類不能重寫父類的函數(shù)原因?尿庐?忠怖??抄瑟?凡泣?
return true;
} else {
// Revert the put, old class is further down the class hierarchy
subscriberClassByMethodKey.put(methodKey, methodClassOld);
return false;
}
}
//省略
.....
}
到這里獲取訂閱方法的流程就已經(jīng)結(jié)束了,獲取到的訂閱方法已經(jīng)被打包成 SubscriberMethod對(duì)象并添加到findState.subscriberMethods數(shù)組里面去了皮假,接下來只要把它取出來就好了鞋拟。
回到我們剛開始分析注冊(cè)進(jìn)來的地方,我們?cè)谝幌盗胁僮骱竽玫搅擞嗛喺咚嗛喌乃蟹椒↙ist<SubscriberMethod>惹资,接下來還需要給每個(gè)方法添加訂閱贺纲,subscribe(subscriber, subscriberMethod)方法又是怎樣的呢?
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
subscribe(subscriber, subscriberMethod)代碼如下:
// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
//獲取該訂閱方法的訂閱類型
Class<?> eventType = subscriberMethod.eventType;
//通過訂閱者和訂閱方法構(gòu)建Subscription 對(duì)象
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
//通過事件類型褪测,獲取該事件類型下的所有訂閱事件
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<>();
//放入map緩存
subscriptionsByEventType.put(eventType, subscriptions);
} else {
//如果重復(fù)訂閱則會(huì)報(bào)錯(cuò)猴誊,這也是為什么有時(shí)我們多次調(diào)用 EventBus.getDefault().register(this)閃退的原因
if (subscriptions.contains(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
}
}
//根據(jù)新加入的訂閱方法的優(yōu)先級(jí)決定插入到隊(duì)列中的位置
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;
}
}
//將該訂閱方法存放到typesBySubscriber中
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
...省略粘性事件相關(guān)代碼
}
簡單講,訂閱流程就是將獲取到的subscriberMethod數(shù)組逐一添加到subscriptionsByEventType和typesBySubscriber這兩個(gè)map中去侮措,為了給在事件發(fā)送的時(shí)候使用懈叹,以此形成通信。
這里比較復(fù)雜的就是獲取方法的流程分扎,簡單總結(jié)調(diào)用順序如下:
findSubscriberMethods()->findUsingInfo()->包裝成FindState對(duì)象存儲(chǔ)相關(guān)信息和映射關(guān)系->findUsingReflectionInSingleClass()
unregister解注冊(cè)
/** Unregisters the given subscriber from all event classes. */
public synchronized void unregister(Object subscriber) {
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
if (subscribedTypes != null) {
for (Class<?> eventType : subscribedTypes) {
unsubscribeByEventType(subscriber, eventType);
}
typesBySubscriber.remove(subscriber);
} else {
Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
// 找到與事件類型相匹配的訂閱方法
List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions != null) {
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--;
}
}
}
}
取消訂閱的方法中跟訂閱反著來项阴,subscriptionsByEventType和typesBySubscriber有沒有很熟悉!!环揽!首先通過獲取typesBySubscriber map獲取該訂閱者所訂閱的所有訂閱類型略荡,接著通過subscriptionsByEventType map獲取每一個(gè)訂閱類型下的所有屬于該訂閱者的訂閱方法,然后逐一移除掉歉胶。