主目錄見:Android高級(jí)進(jìn)階知識(shí)(這是總目錄索引)
?因?yàn)閲?guó)慶放假的緣故,好幾天沒有寫文章媳板,今天抽空來(lái)寫一篇杏糙,那我們就從我們平常用的比較熟悉的框架開始講解,今天講解一款比較熟悉的框架EventBus3.0衬鱼,在實(shí)際項(xiàng)目用的也比較多,廢話不多說(shuō)憔杨,直接開始。
一.目標(biāo)
今天寫這篇主要是從源碼角度來(lái)講解一個(gè)流行框架的原理蒜胖,使用方法我就不講了消别,因?yàn)楸容^簡(jiǎn)單抛蚤,那么我們今天的目標(biāo)很明確:
1.從源碼角度了解EventBus的用法;
2.通過源碼來(lái)學(xué)習(xí)一個(gè)開源項(xiàng)目可能用到的技術(shù)點(diǎn)寻狂。
二.源碼分析
我們知道我們EventBus3.0的用法分為創(chuàng)建岁经,注冊(cè),發(fā)送蛇券,粘性事件和后面加入的索引缀壤。今天我們將從一個(gè)一個(gè)開始講。為了后面說(shuō)的時(shí)候比較順利呢纠亚,我們先說(shuō)下索引的作用下塘慕。
1.Subscriber Index
如果要用到索引的話我們需要在gradle文件里面添加如下:
buildscript {
dependencies {
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}
}
apply plugin: 'com.neenbedankt.android-apt'
dependencies {
compile 'org.greenrobot:eventbus:3.0.0'
apt 'org.greenrobot:eventbus-annotation-processor:3.0.1'
}
apt {
arguments {
eventBusIndex "com.example.myapp.MyEventBusIndex"
}
}
這是典型的apt的配置,當(dāng)然現(xiàn)在一般框架用的annotationProcessor的方式蒂胞,其實(shí)也是一樣的图呢,都差不多。這里的配置主要是用來(lái)生成放置一個(gè)索引類的骗随,我們使用的時(shí)候會(huì)這樣使用:
EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus(); //開啟加速模式
當(dāng)然這是其中的一種使用方法蛤织。我們不去看其他的方式主要我們看到這個(gè)地方索引是用addIndex方法加入的,這里面的MyEventBusIndex是哪里生成的呢鸿染?其實(shí)這個(gè)地方就是用的apt的方式生成的(包名和類名是根據(jù)上面gradle里面配置的)指蚜。首先我們來(lái)看生成的文件長(zhǎng)什么樣:
/** This class is generated by EventBus, do not edit. */
public class MyEventBusIndex implements SubscriberInfoIndex {
private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;
static {
SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();
putIndex(new SimpleSubscriberInfo(com.example.zz.eventbusnewdemo.MainActivity.class, true,
new SubscriberMethodInfo[] {
new SubscriberMethodInfo("onEvent", com.example.zz.eventbusnewdemo.event.FirstEvent.class, ThreadMode.MAIN,
60, false),
}));
putIndex(new SimpleSubscriberInfo(com.example.zz.eventbusnewdemo.ThirdActivity.class, true,
new SubscriberMethodInfo[] {
new SubscriberMethodInfo("onStickyEvent", com.example.zz.eventbusnewdemo.event.StickyEvent.class,
ThreadMode.MAIN, 0, true),
}));
putIndex(new SimpleSubscriberInfo(com.example.zz.eventbusnewdemo.SecondActivity.class, true,
new SubscriberMethodInfo[] {
new SubscriberMethodInfo("onSecondEvent", com.example.zz.eventbusnewdemo.event.FirstEvent.class,
ThreadMode.POSTING, 1000, false),
}));
}
private static void putIndex(SubscriberInfo info) {
SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);
}
@Override
public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {
SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);
if (info != null) {
return info;
} else {
return null;
}
}
}
這個(gè)類其實(shí)很簡(jiǎn)單,就是調(diào)用putIndex方法將類與類對(duì)應(yīng)的訂閱信息放進(jìn)Map里面涨椒。因?yàn)檫@些方法是放進(jìn)static中的摊鸡,所以在new的時(shí)候就會(huì)調(diào)用了。具體這個(gè)類怎么生成我到時(shí)會(huì)在編譯期注解那里說(shuō)到丢烘,代碼主要在源碼中的下圖位置:
2.創(chuàng)建
我們知道我們一個(gè)類要想成為訂閱者必須在類中注冊(cè):
EventBus.getDefault().register(this);
所以我們很自然來(lái)看getDefault()方法是干了什么:
/** 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;
}
這里就是獲取EventBus的實(shí)例柱宦,這里使用了單例的模式。那么我們這里接著就是看EventBus的構(gòu)造函數(shù)了:
public EventBus() {
this(DEFAULT_BUILDER);
}
我們看到這里EventBus的構(gòu)造函數(shù)是public的播瞳,也就是說(shuō)我們可以自己去實(shí)例化一個(gè)EventBus掸刊,所以也就是說(shuō)允許多個(gè)EventBus,然后獨(dú)立處理自己的事件赢乓,但是如果通過getDefault()方法創(chuàng)建的話忧侧,因?yàn)槭鞘褂脝卫J竭M(jìn)行創(chuàng)建,所以只會(huì)有一個(gè)EventBus實(shí)例牌芋。我們繼續(xù)看這里this調(diào)用了另外一個(gè)構(gòu)造函數(shù):
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;
}
我們看到這個(gè)類里面實(shí)例化了好多類蚓炬,首先我們看下以下幾個(gè):
private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
private final Map<Object, List<Class<?>>> typesBySubscriber;
private final Map<Class<?>, Object> stickyEvents;
subscriptionsByEventType:這個(gè)類主要是key為event,value為Subscription的集合躺屁,Subscription是個(gè)封裝的類肯夏,里面主要有訂閱者和訂閱類兩個(gè)類,這個(gè)map主要是在發(fā)送的時(shí)候根據(jù)event來(lái)查找訂閱者的。
typesBySubscriber:這個(gè)類主要是key為訂閱者驯击,value為event烁兰,主要是注冊(cè)的時(shí)候會(huì)用到。
stickyEvents:這個(gè)方法主要保存粘性事件的徊都。
這些都會(huì)在后面講到沪斟,所以大家放心,如果這個(gè)地方還不知道他們是干嘛用的暇矫。然后我們來(lái)看下面幾個(gè)Poster:
private final HandlerPoster mainThreadPoster;
private final BackgroundPoster backgroundPoster;
private final AsyncPoster asyncPoster;
這幾個(gè)Poster在ThreadMode和粘性事件的時(shí)候都會(huì)用到主之。主要是線程有關(guān)。另外我們看構(gòu)造函數(shù)里面下面一些都是用builder給EventBus的屬性賦值,這里主要是通過建造者的設(shè)計(jì)模式給builder構(gòu)造不同的功能。我們可以通過builder構(gòu)造如下:
eventBus = EventBus.builder().eventInheritance(false).build();
具體的建造者有哪些這里不展開講了看峻。我們有用到可以看下很簡(jiǎn)單。
3.注冊(cè)
注冊(cè)的話主要是關(guān)聯(lián)訂閱者和訂閱方法史翘,3.0以后的話訂閱方法支持注解@Subscribe,注解的話支持配置如下:
支持配置線程模式冀续,是否是粘性事件琼讽,優(yōu)先級(jí)。然后我們會(huì)調(diào)用register()方法來(lái)進(jìn)行注冊(cè):
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
我們看到首先我們是查找訂閱者對(duì)應(yīng)的訂閱方法(一個(gè)訂閱者對(duì)應(yīng)多個(gè)訂閱方法)洪唐,然后就行訂閱钻蹬。首先我們看findSubscriberMethods方法干了些什么:
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
//首先從緩存中查找訂閱者對(duì)應(yīng)的訂閱方法,如果有則直接返回即可
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
//是否忽略索引功能凭需,這個(gè)地方3.0用來(lái)加速的问欠,我們不忽略,要想忽略可以通過builder進(jìn)行設(shè)置
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 {
//最后放進(jìn)緩存粒蜈,然后返回對(duì)應(yīng)的訂閱方法集合
METHOD_CACHE.put(subscriberClass, subscriberMethods);
return subscriberMethods;
}
}
從我們注釋可以看到這里的ignoreGeneratedIndex為false即不忽略索引功能顺献,所以我們會(huì)走到findUsingInfo方法里面,我們看下這個(gè)方法到底是做了什么:
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
//這里首先準(zhǔn)備一個(gè)FindState對(duì)象枯怖,這個(gè)類主要保存了訂閱者信息
FindState findState = prepareFindState();
//用訂閱者類初始化findState對(duì)象
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
//獲取訂閱者信息注整,如果開啟了索引功能就不會(huì)為空否則會(huì)為空
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 {
findUsingReflectionInSingleClass(findState);
}
//向上往父類進(jìn)行查找
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
上面我們看到一個(gè)FindState類,我們來(lái)看看FindState類主要有哪些信息呢度硝?
static class FindState {
//訂閱方法列表
final List<SubscriberMethod> subscriberMethods = new ArrayList<>();
//key為event肿轨,value為Method,主要后面checkAdd方法會(huì)用到
final Map<Class, Object> anyMethodByEventType = new HashMap<>();
//用方法和event生成的key蕊程,value為訂閱類椒袍,這個(gè)在checkAddWithMethodSignature方法里面會(huì)用到
final Map<String, Class> subscriberClassByMethodKey = new HashMap<>();
final StringBuilder methodKeyBuilder = new StringBuilder(128);
Class<?> subscriberClass;
Class<?> clazz;
boolean skipSuperClasses;
SubscriberInfo subscriberInfo;
.......
}
我們看到這里面FindState保存了訂閱者和訂閱方法以及在檢查的方法里面會(huì)用到,我們程序調(diào)用findState.initForSubscriber(subscriberClass)就是將FindState的屬性賦值如下:
void initForSubscriber(Class<?> subscriberClass) {
this.subscriberClass = clazz = subscriberClass;
skipSuperClasses = false;
subscriberInfo = null;
}
我們看到這個(gè)地方其實(shí)只有訂閱者被賦值了藻茂。所以循環(huán)會(huì)走進(jìn)去驹暑。然后我們看到getSubscriberInfo方法玫恳,這個(gè)方法具體是做了什么呢,看返回知道返回的是訂閱的信息优俘,那是怎么得到:
private SubscriberInfo getSubscriberInfo(FindState findState) {
if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) {
SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();
if (findState.clazz == superclassInfo.getSubscriberClass()) {
return superclassInfo;
}
}
if (subscriberInfoIndexes != null) {
for (SubscriberInfoIndex index : subscriberInfoIndexes) {
SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
if (info != null) {
return info;
}
}
}
return null;
}
我們知道我們第一個(gè)subscriberInfo是空的所以判斷不成功纽窟,第二個(gè)判斷的subscriberInfoIndexes 是什么呢?這個(gè)其實(shí)就是 List<SubscriberInfoIndex> subscriberInfoIndexes兼吓,這個(gè)類是什么時(shí)候賦值的呢?這個(gè)其實(shí)我們前面說(shuō)過了我們?cè)赼ddIndex方法的時(shí)候已經(jīng)賦值過了森枪。
/** Adds an index generated by EventBus' annotation preprocessor. */
public EventBusBuilder addIndex(SubscriberInfoIndex index) {
if(subscriberInfoIndexes == null) {
subscriberInfoIndexes = new ArrayList<>();
}
subscriberInfoIndexes.add(index);
return this;
}
也就是說(shuō)了添加了索引這個(gè)subscriberInfoIndexes才不會(huì)為空视搏,不然將返回null,顯然我們這里是能得到的县袱,因?yàn)榫幾g期我們已經(jīng)putIndex了浑娜,文章的開篇就說(shuō)了,不知道可以返回回去看下式散。
所以我們findUsingInfo方法就會(huì)繼續(xù)走到獲取訂閱信息里面的所有方法筋遭,然后調(diào)用findState的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);
}
}
首先我們看下程序是檢驗(yàn)event對(duì)應(yīng)的訂閱方法存不存在(put方法將value放置進(jìn)去的同時(shí)會(huì)返回一個(gè)舊值,也就是說(shuō)已經(jīng)有event對(duì)應(yīng)的方法就會(huì)返回)如果是沒有訂閱過的直接返回true暴拄。不然接著判斷程序走到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è)方法也不難看懂漓滔,這個(gè)方法首先是拼接了方法名和事件名稱作為key,然后將訂閱者作為value放置進(jìn)去乖篷,如果是第一次的話那肯定返回是null响驴,程序就返回true,但是這個(gè)訂閱方法已經(jīng)有對(duì)應(yīng)的話撕蔼,那么會(huì)接著判斷
methodClassOld.isAssignableFrom(methodClass)豁鲤,意思就是說(shuō)舊的注冊(cè)過的訂閱者是否是新的訂閱者的父類或者同個(gè)類,如果是的話還是返回ture即這是個(gè)新的訂閱者鲸沮,需要添加琳骡。那么到底這兩個(gè)方法(checkAdd,checkAddWithMethodSignature)適用的場(chǎng)景是什么呢讼溺?
場(chǎng)景一:假如一個(gè)類里面有多個(gè)訂閱方法楣号,而這些方法的方法名是一樣的,但是參數(shù)不一樣即接收的事件不一樣肾胯,那么在checkAdd中的existing不為null竖席,會(huì)到checkAddWithMethodSignature中,這個(gè)方法里面又會(huì)根據(jù)方法名和事件名進(jìn)行作為key判斷敬肚,顯然這個(gè)地方拼接的key在這種情況下是不同的毕荐。方法返回true,所以也就是說(shuō)艳馒,允許一個(gè)類有多個(gè)參數(shù)不同的相同方法憎亚。
場(chǎng)景二:這里假設(shè)兩個(gè)類有繼承關(guān)系员寇,B類繼承了A類,B類中重寫了A類中的訂閱方法第美,那么在checkAdd方法中的exiting不為null蝶锋,會(huì)到checkAddWithMethodSignature中,顯然這個(gè)地方因?yàn)榉椒褪录且粯拥氖餐詍ethodClassOld 不為空扳缕,然后會(huì)判斷isAssignableFrom,由于B不是A類的父類别威,所以這里會(huì)返回false躯舔,也就是說(shuō)A類里面的訂閱方法不會(huì)被通知到了。所以子類重寫父類的訂閱方法省古,那么父類的訂閱方法就失效了粥庄,顯然這也是符合設(shè)計(jì)思想的。
接著我們回到findUsingInfo方法豺妓,程序會(huì)接著走到最后一句getMethodsAndRelease方法:
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;
}
首先我們會(huì)得到findstate對(duì)象里面的訂閱方法惜互,然后將findstate放進(jìn)緩存池里面。最后程序返回訂閱類對(duì)應(yīng)的訂閱方法集合琳拭,這樣我們程序回到EventBus的register()方法里面训堆,我們程序調(diào)用了subscribe方法:
// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class<?> eventType = subscriberMethod.eventType;
//首先將訂閱者和訂閱的方法封裝到SubSciption中
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
//根據(jù)event獲取對(duì)應(yīng)的Subsciption CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions == null) {
//如果為null表示沒有訂閱
subscriptions = new CopyOnWriteArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
//如果已經(jīng)訂閱過了就拋出錯(cuò)誤
if (subscriptions.contains(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
}
}
//遍歷訂閱集合,然后將新添加的訂閱按照優(yōu)先級(jí)進(jìn)行添加
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ù)訂閱者來(lái)獲取訂閱的事件集合臀栈,如果還沒有就創(chuàng)建然后放進(jìn)去
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
//判斷訂閱方法是粘性的蔫慧,如果是粘性的會(huì)直接就發(fā)送,后面也會(huì)詳細(xì)做說(shuō)明
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();
if (eventType.isAssignableFrom(candidateEventType)) {
Object stickyEvent = entry.getValue();
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
到這里我們注冊(cè)的流程已經(jīng)講解完畢权薯,我們這里做個(gè)總結(jié):
在注冊(cè)的時(shí)候姑躲,會(huì)先查找這個(gè)訂閱類對(duì)應(yīng)于哪幾個(gè)訂閱方法,這里面會(huì)判斷是要從索引里面查找還是反射進(jìn)行遍歷然后找到符合條件的方法盟蚣,查找的時(shí)候會(huì)通過checkAdd和checkAddWithMethodSignature方法進(jìn)行篩選黍析。找到訂閱類對(duì)應(yīng)的訂閱方法然后就遍歷訂閱方法調(diào)用subscibe方法放進(jìn)subscriptions集合中。
4.發(fā)送
發(fā)送事件的時(shí)候我們一般會(huì)調(diào)用如下代碼:
EventBus.getDefault().post(new FirstEvent("Post Content"));
所以我們直接來(lái)看EventBus的post方法:
public void post(Object event) {
//從ThreadLocal中獲取一個(gè)PostingThreadState
PostingThreadState postingState = currentPostingThreadState.get();
List<Object> eventQueue = postingState.eventQueue;
//將事件添加進(jìn)事件隊(duì)列中
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 {
//隊(duì)列不為空就發(fā)送
while (!eventQueue.isEmpty()) {
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
程序第一句會(huì)獲取PostingThreadState對(duì)象屎开,那currentPostingThreadState到底是啥呢阐枣,我們可以看看:
private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
@Override
protected PostingThreadState initialValue() {
return new PostingThreadState();
}
};
我們看到這里new出這個(gè)對(duì)象,然后get的時(shí)候會(huì)從ThreadLocal中獲取奄抽,也就是說(shuō)這個(gè)對(duì)象是線程安全的蔼两,如果不同的線程這個(gè)對(duì)象是不同的。然后我們程序會(huì)調(diào)用postSingleEvent方法:
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
//eventInheritance這個(gè)可以在builder中設(shè)置逞度,這個(gè)標(biāo)志的意思是是否考慮繼承關(guān)系额划,
//如果考慮的話那么如果事件繼承自父類,那么父類也會(huì)作為事件被發(fā)送
if (eventInheritance) {
//查找該事件對(duì)應(yīng)的所有父類和接口
List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
int countTypes = eventTypes.size();
for (int h = 0; h < countTypes; h++) {
Class<?> clazz = eventTypes.get(h);
//遍歷所有的事件然后進(jìn)行發(fā)送
subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
}
} else {
subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
}
//如果沒有找到訂閱信息
if (!subscriptionFound) {
if (logNoSubscriberMessages) {
Log.d(TAG, "No subscribers registered for event " + eventClass);
}
if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
eventClass != SubscriberExceptionEvent.class) {
post(new NoSubscriberEvent(this, event));
}
}
}
從上面程序可以看到如果找到事件對(duì)應(yīng)的所有父類和接口則進(jìn)行遍歷發(fā)送档泽。程序會(huì)調(diào)用postSingleEventForEventType方法:
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
//根據(jù)事件來(lái)獲取subscriptions集合
subscriptions = subscriptionsByEventType.get(eventClass);
}
if (subscriptions != null && !subscriptions.isEmpty()) {
for (Subscription subscription : subscriptions) {
postingState.event = event;
postingState.subscription = subscription;
boolean aborted = false;
try {
//然后發(fā)送俊戳,參數(shù)為subscription和事件揖赴,是否在主線程
postToSubscription(subscription, event, postingState.isMainThread);
aborted = postingState.canceled;
} finally {
postingState.event = null;
postingState.subscription = null;
postingState.canceled = false;
}
if (aborted) {
break;
}
}
return true;
}
return false;
}
程序接下來(lái)調(diào)用了postToSubscription,傳的參數(shù)我們已經(jīng)說(shuō)明了抑胎,我們可以具體來(lái)看這個(gè)方法干了啥:
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
case POSTING:
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);
}
}
這個(gè)方法很簡(jiǎn)單有沒有燥滑,根據(jù)threadMode線程模式來(lái)判斷要怎么調(diào)用。如果是POSTING則會(huì)直接調(diào)用invokeSubscriber阿逃,如果是MAIN但是在主線程也會(huì)調(diào)用invokeSubscriber铭拧,如果是BACKGROUND,但是不在主線程也會(huì)調(diào)用invokeSubscriber恃锉,不然就會(huì)調(diào)用幾個(gè)poster進(jìn)行進(jìn)隊(duì)列羽历。首先我們看下invokeSubscriber:
void invokeSubscriber(Subscription subscription, Object event) {
try {
subscription.subscriberMethod.method.invoke(subscription.subscriber, event);
} catch (InvocationTargetException e) {
handleSubscriberException(subscription, event, e.getCause());
} catch (IllegalAccessException e) {
throw new IllegalStateException("Unexpected exception", e);
}
}
這個(gè)方法很簡(jiǎn)單呀,就是利用反射進(jìn)行對(duì)方法進(jìn)行調(diào)用淡喜。然后我們看看其他的用poster進(jìn)行調(diào)用的。首先我們看下mainThreadPoster(是個(gè)HandlerPoster):
final class HandlerPoster extends Handler {
//待發(fā)送的post隊(duì)列
private final PendingPostQueue queue;
//最大的運(yùn)行時(shí)間诵闭,因?yàn)樵谥骶€程不能執(zhí)行時(shí)間過長(zhǎng)
private final int maxMillisInsideHandleMessage;
private final EventBus eventBus;
private boolean handlerActive;
HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) {
super(looper);
this.eventBus = eventBus;
this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage;
queue = new PendingPostQueue();
}
........
}
這個(gè)類里面有PendingPostQueue 是個(gè)鏈表的結(jié)構(gòu)炼团,鏈表里面的節(jié)點(diǎn)PendingPost包括了event和subscription,這個(gè)poster主要是threadMode為MAIN然后不在主線程時(shí)候調(diào)用疏尿,我們看enqueue做了啥:
void enqueue(Subscription subscription, Object event) {
//根據(jù)subscription和event來(lái)構(gòu)建PendingPost對(duì)象
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
synchronized (this) {
//加入隊(duì)列中
queue.enqueue(pendingPost);
if (!handlerActive) {
handlerActive = true;
//用handler來(lái)發(fā)送消息
if (!sendMessage(obtainMessage())) {
throw new EventBusException("Could not send handler message");
}
}
}
}
這個(gè)方法主要是獲取到PendingPost對(duì)象然后加入隊(duì)列瘟芝,接著發(fā)送消息。由于這個(gè)Handler初始化的時(shí)候new HandlerPoster(this, Looper.getMainLooper(), 10)是傳入主線程的Looper褥琐,所以這個(gè)是運(yùn)行在ui線程的:
@Override
public void handleMessage(Message msg) {
boolean rescheduled = false;
try {
long started = SystemClock.uptimeMillis();
//死循環(huán)锌俱,不斷從隊(duì)列中取出PendingPost
while (true) {
PendingPost pendingPost = queue.poll();
if (pendingPost == null) {
synchronized (this) {
// Check again, this time in synchronized
pendingPost = queue.poll();
if (pendingPost == null) {
handlerActive = false;
return;
}
}
}
//接著調(diào)用這個(gè)方法,這樣的話這個(gè)方法就是在主線程的
eventBus.invokeSubscriber(pendingPost);
long timeInMethod = SystemClock.uptimeMillis() - started;
if (timeInMethod >= maxMillisInsideHandleMessage) {
if (!sendMessage(obtainMessage())) {
throw new EventBusException("Could not send handler message");
}
rescheduled = true;
return;
}
}
} finally {
handlerActive = rescheduled;
}
}
我們看到敌呈,這個(gè)方法就是取出隊(duì)列里面的PendingPost然后進(jìn)行正常的invokeSubscriber贸宏。這樣的話就是在主線程運(yùn)行了。接著我們就看看backgroundPoster:
final class BackgroundPoster implements Runnable {
private final PendingPostQueue queue;
private final EventBus eventBus;
private volatile boolean executorRunning;
BackgroundPoster(EventBus eventBus) {
this.eventBus = eventBus;
queue = new PendingPostQueue();
}
}
我們看到這個(gè)方法里面也是有一個(gè)PendingPostQueue磕洪,跟HandlerPost是一樣的吭练,這個(gè)BackgroundPoster實(shí)現(xiàn)了Runnable接口,其他沒有什么析显,我們來(lái)看enqueue方法:
public void enqueue(Subscription subscription, Object event) {
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
synchronized (this) {
queue.enqueue(pendingPost);
if (!executorRunning) {
executorRunning = true;
eventBus.getExecutorService().execute(this);
}
}
}
我們看到這個(gè)方法前面都一樣鲫咽,就最后調(diào)用了 eventBus.getExecutorService().execute(this)執(zhí)行了這個(gè)線程,所以接下來(lái)會(huì)調(diào)用這個(gè)線程的run方法:
@Override
public void run() {
try {
try {
while (true) {
PendingPost pendingPost = queue.poll(1000);
if (pendingPost == null) {
synchronized (this) {
// Check again, this time in synchronized
pendingPost = queue.poll();
if (pendingPost == null) {
executorRunning = false;
return;
}
}
}
eventBus.invokeSubscriber(pendingPost);
}
} catch (InterruptedException e) {
Log.w("Event", Thread.currentThread().getName() + " was interruppted", e);
}
} finally {
executorRunning = false;
}
}
我們看到這個(gè)run方法里面也是取出隊(duì)列中的PendingPost然后調(diào)用invokeSubscriber方法谷异,也就是說(shuō)這個(gè)方法是運(yùn)行在子線程中的分尸。最后我們看AsyncPoster,這個(gè)我就不認(rèn)真說(shuō)了歹嘹,其實(shí)和BackgroudPoster是類似的箩绍,只是沒有判斷是不是有一條線程正在運(yùn)行,就是說(shuō)每次運(yùn)行都是在不同的線程荞下。
5.取消
我們注冊(cè)事件之后伶选,沒有用的時(shí)候要取消史飞,即我們會(huì)調(diào)用方法如下:
EventBus.getDefault().unregister(this);
所以我們直接就來(lái)看看unregister是做了什么呢?
/** Unregisters the given subscriber from all event classes. */
public synchronized void unregister(Object subscriber) {
//根據(jù)訂閱者來(lái)獲取事件
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());
}
}
我們找到訂閱者對(duì)應(yīng)的事件集合仰税,然后遍歷之后調(diào)用unsubscribeByEventType构资,那我們就直接看看這個(gè)方法做了啥:
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
//根據(jù)事件獲取訂閱者信息
List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions != null) {
int size = subscriptions.size();
for (int i = 0; i < size; i++) {
//取出訂閱者然后判斷是否跟要?jiǎng)h除的訂閱者相同,相同則刪除
Subscription subscription = subscriptions.get(i);
if (subscription.subscriber == subscriber) {
subscription.active = false;
subscriptions.remove(i);
i--;
size--;
}
}
}
}
我們看到注銷的流程是很簡(jiǎn)單的陨簇,從typesBySubscriber和subscriptionsByEventType移除跟訂閱者有關(guān)的信息吐绵。
6.粘性事件
粘性事件跟其他事件不同的是先發(fā)送出去,然后再注冊(cè)接收到事件河绽。粘性事件有時(shí)候還是很有用的己单。粘性的事件發(fā)送是通過postSticky方法來(lái)發(fā)送的,我們這里來(lái)看看:
public void postSticky(Object event) {
synchronized (stickyEvents) {
stickyEvents.put(event.getClass(), event);
}
// Should be posted after it is putted, in case the subscriber wants to remove immediately
post(event);
}
我們看這個(gè)方法將事件為value和事件對(duì)應(yīng)的class對(duì)象為key放進(jìn)stickyEvents(Map)中耙饰,然后調(diào)用post事件纹笼,但是這時(shí)候因?yàn)檫€沒有注冊(cè)的話是找不到訂閱者的,但是為什么在注冊(cè)的時(shí)候又可以收到粘性事件呢苟跪?其實(shí)我們之前有看過廷痘,我們來(lái)看看:
// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class<?> eventType = subscriberMethod.eventType;
//首先將訂閱者和訂閱的方法封裝到SubSciption中
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
//根據(jù)event獲取對(duì)應(yīng)的Subsciption CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions == null) {
//如果為null表示沒有訂閱
subscriptions = new CopyOnWriteArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
//如果已經(jīng)訂閱過了就拋出錯(cuò)誤
if (subscriptions.contains(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
}
}
//遍歷訂閱集合,然后將新添加的訂閱按照優(yōu)先級(jí)進(jìn)行添加
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ù)訂閱者來(lái)獲取訂閱的事件集合件已,如果還沒有就創(chuàng)建然后放進(jìn)去
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
//判斷訂閱方法是粘性的笋额,如果是粘性的會(huì)直接就發(fā)送,后面也會(huì)詳細(xì)做說(shuō)明
if (subscriberMethod.sticky) {
if (eventInheritance) {
//這個(gè)標(biāo)志我們之前也說(shuō)過篷扩,默認(rèn)是true的
// 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();
//發(fā)送粘性事件
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
} else {
//根據(jù)event獲取特定的事件然后發(fā)送
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
EventBus并不知道當(dāng)前的訂閱者對(duì)應(yīng)于哪個(gè)事件兄猩,所以要遍歷一遍找到匹配的粘性事件,然后調(diào)用checkPostStickyEventToSubscription方法鉴未,這個(gè)方法就是調(diào)用的postToSubscription枢冤,就是根據(jù)threadMode調(diào)用相應(yīng)的訂閱方法,我們之前已經(jīng)講過了铜秆。好啦掏导,到這里我們已經(jīng)講完了所有的源代碼,收獲還是很大的羽峰,有些知識(shí)技術(shù)點(diǎn)我們會(huì)一一講解的趟咆。
總結(jié):講完EventBus3.0的源碼,我們已經(jīng)對(duì)于應(yīng)用這個(gè)開源框架更有把握了梅屉,遇到問題我們也能輕易排除值纱,所以收獲還是很大的,最后祝大家國(guó)慶快樂哈坯汤。