概述
關于EventBus3.x的用法港庄,本文不再贅述乾胶,只分析其實現(xiàn)原理捌年,官方的流程圖:
上圖是EventBus基于觀察者模式的事件發(fā)布流程的高度概括,經(jīng)過下文的源碼分析后醇滥,再來體會該流程會比較清晰黎比。
訂閱流程
需要訂閱事件的對象首先需要進行注冊,比如Activity中:
EventBus.getDefault().register(activity);
看看EventBus
這個類中做了什么:
/** 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(EventBusBuilder builder) {
//eventType作為key,Subscription為value的map集合
subscriptionsByEventType = new HashMap<>();
//key為訂閱對象,value為該對象中所有的訂閱event類型的map集合
typesBySubscriber = new HashMap<>();
//黏性事件map集合
stickyEvents = new ConcurrentHashMap<>();
//主線程的事件處理器
mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
//后臺線程的事件處理器
backgroundPoster = new BackgroundPoster(this);
//異步的事件處理器
asyncPoster = new AsyncPoster(this);
//注解處理器生成的索引數(shù)量,如果沒用注解處理器那么為0
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;
//事件繼承 默認true
eventInheritance = builder.eventInheritance;
//線程池
executorService = builder.executorService;
}
因為參數(shù)很多所以使用了建造者模式腺办,這也是許多開源框架經(jīng)常使用的模式焰手,對這些參數(shù)暫時不懂沒有關系,繼續(xù)看下面:
/**
* 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.
* 給事件的訂閱者進行注冊怀喉。訂閱者一旦不再接受事件后书妻,必須調(diào)用{@link #unregister(Object)}這個方法來進行
* 取消訂閱。
* 訂閱者的事件處理方法必須用{@link Subscribe}進行注解躬拢,該注解也允許配置 {@link ThreadMode}等屬性
*/
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();//獲取訂閱者對象的Class
//根據(jù)訂閱者對象的Class來獲取訂閱者的事件處理方法的list集合
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
//循環(huán)對訂閱者的方法進行注冊
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
這里
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
subscriberMethodFinder
對象主要封裝了查找訂閱者信息的一系列方法
SubscriberMethod
主要封裝了訂閱方法的信息,比較簡單:
public class SubscriberMethod {
final Method method;//訂閱方法
final ThreadMode threadMode;//線程模式信息
final Class<?> eventType;//事件類
final int priority;//優(yōu)先級
final boolean sticky;//是否黏性事件
/** Used for efficient comparison */
String methodString;//方法簽名
public SubscriberMethod(Method method, Class<?> eventType, ThreadMode threadMode, int priority, boolean sticky) {
this.method = method;
this.threadMode = threadMode;
this.eventType = eventType;
this.priority = priority;
this.sticky = sticky;
}
@Override
public boolean equals(Object other) {
//先比較對象的引用,如果對象不一樣躲履,那么比較方法簽名
if (other == this) {
return true;
} else if (other instanceof SubscriberMethod) {
checkMethodString();
SubscriberMethod otherSubscriberMethod = (SubscriberMethod)other;
otherSubscriberMethod.checkMethodString();
// Don't use method.equals because of http://code.google.com/p/android/issues/detail?id=7811#c6
return methodString.equals(otherSubscriberMethod.methodString);
} else {
return false;
}
}
//拼接方法簽名
private synchronized void checkMethodString() {
if (methodString == null) {
// Method.toString has more overhead, just take relevant parts of the method
StringBuilder builder = new StringBuilder(64);
builder.append(method.getDeclaringClass().getName());
builder.append('#').append(method.getName());
builder.append('(').append(eventType.getName());
methodString = builder.toString();
}
}
@Override
public int hashCode() {
return method.hashCode();
}
}
接下來看看subscriberMethodFinder
中的方法:
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
//首先從緩存中獲取保存的訂閱者方法list集合
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {//如果之前緩存過,就直接返回
return subscriberMethods;
}
//ignoreGeneratedIndex 是否忽略由注解處理器生成的索引, 默認false
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 {
//將key為訂閱者Class,value為subscriberMethods 放入緩存聊闯,并返回
METHOD_CACHE.put(subscriberClass, subscriberMethods);
return subscriberMethods;
}
}
該方法主要是從緩存中查找訂閱者的訂閱信息,如果沒有則通過findUsingInfo(subscriberClass)
方法查找:
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
//FindState 封裝了訂閱對象信息和訂閱方法檢測校驗的類,并可以復用
FindState findState = prepareFindState();
//進行初始化
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
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);
}
//然后繼續(xù)對父類進行查找
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
這里findState
對象其實封裝了一些訂閱信息和查找方法工猜,簡單看下:
static class FindState {
//SubscriberMethod 訂閱方法的集合
final List<SubscriberMethod> subscriberMethods = new ArrayList<>();
//存儲事件類型的集合
final Map<Class, Object> anyMethodByEventType = new HashMap<>();
//方法簽名和訂閱類的集合
final Map<String, Class> subscriberClassByMethodKey = new HashMap<>();
final StringBuilder methodKeyBuilder = new StringBuilder(128);
Class<?> subscriberClass;
Class<?> clazz;
boolean skipSuperClasses;
SubscriberInfo subscriberInfo;
...省略
}
看看prepareFindState
方法,主要是獲取一個FindState對象:
private FindState prepareFindState() {
synchronized (FIND_STATE_POOL) {
for (int i = 0; i < POOL_SIZE; i++) {
FindState state = FIND_STATE_POOL[i];
//從FindState 的緩存數(shù)組中取出一個對象進行復用
if (state != null) {
FIND_STATE_POOL[i] = null;
return state;
}
}
}//如果沒有就直接new一個
return new FindState();
}
繼續(xù)看看initForSubscriber
方法,主要是進行了一些初始化:
static class FindState {
...以上省略
void initForSubscriber(Class<?> subscriberClass) {
this.subscriberClass = clazz = subscriberClass;
skipSuperClasses = false;
subscriberInfo = null;
}
...以下省略
}
之后進入while循環(huán)菱蔬,然后先說不使用注解處理器的情況,會調(diào)用findUsingReflectionInSingleClass(findState);
:
private void findUsingReflectionInSingleClass(FindState findState) {
Method[] methods;
try {
// This is faster than getMethods, especially when subscribers are fat classes like Activities
//注意 getDeclaredMethods 只獲取當前類的方法篷帅,而不獲取父類繼承的方法,因此比getMethods方法速度更快
//比如Activity本身就有大量方法,那么Activity的子類無疑使用getDeclaredMethods更好
// 獲取訂閱對象所有的Method對象數(shù)組
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) {
int modifiers = method.getModifiers();
//判斷方法是否為public,對于方法必須為public的設計,應該是為了提高查找速度,否則需要挨個判斷方法注解
//否則報異常
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
//獲取方法的參數(shù)類型Class數(shù)組
Class<?>[] parameterTypes = method.getParameterTypes();
//參數(shù)只能是一個,否則報錯
if (parameterTypes.length == 1) {
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
//判斷是否是Subscribe注解類型
if (subscribeAnnotation != null) {
Class<?> eventType = parameterTypes[0];
//checkAdd方法主要是檢測事件類型在同一個對象或者父類中是否重復注冊過
if (findState.checkAdd(method, eventType)) {
ThreadMode threadMode = subscribeAnnotation.threadMode();
//然后將訂閱方法的信息封裝為SubscriberMethod,并添加到findState.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");
}
}
}
方法其實比較清晰拴泌,主要是根據(jù)反射和注解獲取方法上的訂閱信息并進行封裝操作,然后比較關鍵的檢測操作findState.checkAdd(method, eventType)
:
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.
//2層檢測魏身,首先檢測eventType,再進行完整的簽名檢測,一般來講訂閱對象不會重復定義監(jiān)聽同樣的event類型的方法
//但是如果有多個同樣event類的訂閱方法蚪腐,那么也是合法的
Object existing = anyMethodByEventType.put(eventType, method);
if (existing == null) {
return true;
} else {
if (existing instanceof Method) {
//如果之前已經(jīng)保存過該方法箭昵,并且父類也具有同樣的訂閱方法,那么拋出異常
if (!checkAddWithMethodSignature((Method) existing, eventType)) {
// Paranoia check
throw new IllegalStateException();
}
// Put any non-Method object to "consume" the existing Method
//如果之前有相同event類型的方法存在回季,那么為了兼容同一個類的多個同樣event的方法
//放入一個不是Method類型的對象家制,這樣根據(jù)不同的判斷就能區(qū)別是父類還是多方法
anyMethodByEventType.put(eventType, this);
}
return checkAddWithMethodSignature(method, eventType);
}
}
private boolean checkAddWithMethodSignature(Method method, Class<?> eventType) {
methodKeyBuilder.setLength(0);
methodKeyBuilder.append(method.getName());
methodKeyBuilder.append('>').append(eventType.getName());
//用方法名和event名稱組成key
String methodKey = methodKeyBuilder.toString();
//獲取訂閱方法所在的Class
Class<?> methodClass = method.getDeclaringClass();
//根據(jù)key,添加methodClass到緩存,并且得到methodClassOld
Class<?> methodClassOld = subscriberClassByMethodKey.put(methodKey, methodClass);
//如果methodClassOld不存在或者是methodClass自身或methodClass的父類,那么返回true,因為繼承關系會繼承事件訂閱
//如果子類有重寫了繼承的訂閱方法,那么只會保留子類的訂閱方法
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
//檢測到父類注冊后正林,否則還是將methodClassOld放回緩存,因為子類重寫了,所以返回false颤殴,不會再父類方法注冊
subscriberClassByMethodKey.put(methodKey, methodClassOld);
return false;
}
}
該方法主要檢測訂閱方法的合法性觅廓,個人的理解是一種情況是用一個類中注冊了多個同樣event類型的方法,這種情況是允許的诅病,另一種是子類重寫父類的繼承方法哪亿,如果是這樣的情況那只會保留子類的訂閱方法,仔細體會這里的寫法贤笆。
檢測完成之后再返回到findUsingReflectionInSingleClass(FindState findState)
方法繼續(xù)看:
ThreadMode threadMode = subscribeAnnotation.threadMode();
findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
很好理解了蝇棉,就是將訂閱方法封裝到findState
對象中了,后面是要取出的芥永。
這之后再返回到findUsingInfo(Class<?> subscriberClass)
方法中:
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
//FindState 封裝了訂閱對象信息和訂閱方法檢測校驗的類,并可以復用
FindState findState = prepareFindState();
//進行初始化
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
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);
}
//然后繼續(xù)對父類進行查找
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
此時還是處在while循環(huán)中,下一步findState.moveToSuperclass();
再進入父類進行循環(huán),直到結束,之后調(diào)用:
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;
}
該方法的作用是將findState
封裝的subscriberMethods
集合返回篡殷,并將findState
對象清空,并放入FIND_STATE_POOL
緩存數(shù)組之中埋涧,方便下次使用板辽,個人理解這里是為了優(yōu)化性能,減少了重復創(chuàng)建對象的開銷棘催。
之后返回到findSubscriberMethods(Class<?> subscriberClass)
方法:
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
...上面代碼省略
//如果找不到注解方法拋出異常
if (subscriberMethods.isEmpty()) {
throw new EventBusException("Subscriber " + subscriberClass
+ " and its super classes have no public methods with the @Subscribe annotation");
} else {
//將key為訂閱者Class,value為subscriberMethods 放入緩存劲弦,并返回
METHOD_CACHE.put(subscriberClass, subscriberMethods);
return subscriberMethods;
}
}
這里METHOD_CACHE
是ConcurrentHashMap集合,key是訂閱對象的Class醇坝,value是該對象中所有的訂閱方法集合,下次查找的時候如果存在了就可以直接從緩存中取出了邑跪。
之后再返回到:
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();//獲取訂閱者對象的Class
//根據(jù)訂閱者對象的Class來獲取訂閱者的事件處理方法的list集合
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
//循環(huán)對訂閱者的方法進行注冊
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
進入for循環(huán),逐個遍歷訂閱者和訂閱方法,看看subscribe(subscriber, subscriberMethod)
方法:
// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class<?> eventType = subscriberMethod.eventType;
//Subscription 就是訂閱對象和訂閱方法的再次封裝
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
//subscriptionsByEventType 就是eventType作為key,Subscription為value的map集合緩存,這里是取出之前的保存Subscription類型的List
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
//subscriptions 是Null就new一個,subscriptionsByEventType并將eventType,subscriptions保存起來
//但subscriptions此時還沒有任何Subscription對象
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {//如果subscriptions已經(jīng)包含了再次封裝的Subscription,那么拋出異常提示
if (subscriptions.contains(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
}
}
//這里會根據(jù)event的優(yōu)先級來插入到subscriptions中,如果優(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;
}
}
//typesBySubscriber 是key為訂閱對象,value為該對象中所有的event的map集合
//該集合作用是后續(xù)取消訂閱的時候,方便移除訂閱對象
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
//subscribedEvents為null的話就new一個,然后typesBySubscriber將訂閱對象和event加入進來
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
//subscribedEvents 將eventType加入進來
subscribedEvents.add(eventType);
//判斷是不是黏性事件
if (subscriberMethod.sticky) {
//eventInheritance 事件繼承,默認true
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);
}
}
}
該方法并不難理解呼猪,Subscription
就是Object subscriber, SubscriberMethod subscriberMethod
這兩個對象的再封裝,subscriptionsByEventType
就是eventType作為key,Subscription為value的map集合緩存画畅,設計用意就是根據(jù)事件類型找出所有的訂閱者信息,這樣當事件發(fā)布的時候能夠方便給所有的訂閱者進行方法的調(diào)用宋距,subscribedEvents
就是以訂閱者subscriber
為key轴踱,事件class為value的map集合緩存,在取消注冊的時候可以方便進行訂閱事件的刪除谚赎。
然后判斷是不是黏性事件淫僻,如果是黏性事件,那么首先會判斷是否支持事件繼承,默認true赴穗,意思就是如果訂閱方法中的事件類如果實現(xiàn)了某些接口,那么其他實現(xiàn)了這些接口的訂閱事件類或者是訂閱事件類的父類同樣會接收到事件信息,檢測通過后就會直接發(fā)送事件预麸,這個在post流程中可以體現(xiàn)出來,后面再進行分析丘损,那么注冊流程基本就是這樣。
發(fā)布事件流程
再來看看發(fā)布流程笑撞,先看發(fā)布方法post(Object event)
:
/** Posts the given event to the event bus. */
public void post(Object event) {
//首先獲取當前線程保存的事件隊列
PostingThreadState postingState = currentPostingThreadState.get();
List<Object> eventQueue = postingState.eventQueue;
//將事件加入隊列
eventQueue.add(event);
//如果沒有在發(fā)布狀態(tài)
if (!postingState.isPosting) {
//利用當前線程的Looper是否是主線程的Looper來判斷當前線程是否為主線程
postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper();
//發(fā)布狀態(tài)為true
postingState.isPosting = true;
//判斷有沒有取消發(fā)布
if (postingState.canceled) {
throw new EventBusException("Internal error. Abort state was not reset");
}
try {
while (!eventQueue.isEmpty()) {
//循環(huán)取出當前隊列的所有事件進行事件發(fā)布
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
該方法的主要作用就是將發(fā)布的事件加入隊列然后,再將當前線程保存的事件隊列全部發(fā)布出去钓觉,看下currentPostingThreadState
:
private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
@Override
protected PostingThreadState initialValue() {
return new PostingThreadState();
}
};
很明顯是保存了PostingThreadState
的ThreadLocal對象茴肥,利用了ThreadLocal的線程特性來獲取當前線程的PostingThreadState
對象,關于ThreadLocal的討論網(wǎng)上已經(jīng)很多,個人也曾做過相關分析Android ThreadLocal及InheritableThreadLocal分析,如有需要可參考荡灾。
這個PostingThreadState
對象就是封裝了當前線程的事件隊列和發(fā)布狀態(tài)等信息:
/** For ThreadLocal, much faster to set (and get multiple values). */
final static class PostingThreadState {
final List<Object> eventQueue = new ArrayList<Object>();
boolean isPosting;
boolean isMainThread;
Subscription subscription;
Object event;
boolean canceled;
}
明白了這些瓤狐,之后回到post
方法中的while循環(huán)方法postSingleEvent(eventQueue.remove(0), postingState)
:
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
//默認true,支持事件繼承
if (eventInheritance) {
//循環(huán)找出事件類的所有接口類
List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
int countTypes = eventTypes.size();
for (int h = 0; h < countTypes; h++) {
Class<?> clazz = eventTypes.get(h);
//開始循環(huán)發(fā)布事件
subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
}
} else {
subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
}
//如果沒有找到訂閱者那么做出提示批幌,并發(fā)布一個NoSubscriberEvent事件
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));
}
}
}
該方法主要是找出當前事件類的所有接口類础锐,并進行循環(huán)發(fā)布事件,如果沒有訂閱者荧缘,那么發(fā)布一個默認的NoSubscriberEvent
事件皆警。先看看是如何查找事件類的所有接口的lookupAllEventTypes(eventClass)
:
/** Looks up all Class objects including super classes and interfaces. Should also work for interfaces. */
private static List<Class<?>> lookupAllEventTypes(Class<?> eventClass) {
synchronized (eventTypesCache) {
//eventTypesCache 就是以事件類為key,以對應的接口類集合為value的map緩存
List<Class<?>> eventTypes = eventTypesCache.get(eventClass);
if (eventTypes == null) {
eventTypes = new ArrayList<>();
Class<?> clazz = eventClass;
while (clazz != null) {
//先加入當前的 事件類,然后再添加接口類
eventTypes.add(clazz);
addInterfaces(eventTypes, clazz.getInterfaces());
//再循環(huán)父類
clazz = clazz.getSuperclass();
}
eventTypesCache.put(eventClass, eventTypes);
}
return eventTypes;
}
}
這里clazz.getInterfaces()
可以找出所有對應的接口集合,看看addInterfaces(eventTypes, clazz.getInterfaces())
方法:
/** Recurses through super interfaces. */
static void addInterfaces(List<Class<?>> eventTypes, Class<?>[] interfaces) {
for (Class<?> interfaceClass : interfaces) {
if (!eventTypes.contains(interfaceClass)) {
eventTypes.add(interfaceClass);
addInterfaces(eventTypes, interfaceClass.getInterfaces());
}
}
}
該方法遞歸調(diào)用獲取到所有的接口類并添加到緩存截粗,看完這里繼續(xù)返回到post
方法中的subscriptionFound = postSingleEventForEventType(event, postingState, eventClass)
信姓,看看該方法中做了什么:
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
//subscriptionsByEventType緩存中根據(jù)事件類取出訂閱者集合
subscriptions = subscriptionsByEventType.get(eventClass);
}
if (subscriptions != null && !subscriptions.isEmpty()) {
for (Subscription subscription : subscriptions) {
postingState.event = event;
postingState.subscription = subscription;
boolean aborted = false;
try {
//發(fā)布事件
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;
}
該方法非常簡單,就是根據(jù)事件類取出之前緩存的訂閱著集合然后進行事件的發(fā)布绸罗,看看postToSubscription(subscription, event, postingState.isMainThread)
方法:
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
//根據(jù)不同的訂閱線程意推,進行不通的處理
switch (subscription.subscriberMethod.threadMode) {
case POSTING:
//post行程的話就直接發(fā)布
invokeSubscriber(subscription, event);
break;
case MAIN:
//主線程的話就直接發(fā)布,否則加入隊列
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
mainThreadPoster.enqueue(subscription, event);
}
break;
case BACKGROUND:
//后臺線程的話珊蟀,如果在主線程就加入后臺隊列菊值,如果不是主線程就直接發(fā)布
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);
}
}
這個方法就是根據(jù)不通的線程屬性做相應的處理,先看下最終的調(diào)用方法:
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);
}
}
就是利用反射調(diào)用方法了系洛,很簡單俊性,然后看下黏性方法的調(diào)用過程,之后我們分析下幾個不同的Poster描扯。
發(fā)送黏性事件的方法:
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);
}
非常簡單定页,首先加入stickyEvents
這個黏性事件的map
集合,然后再調(diào)用普通的post
方法绽诚,這樣保證先注冊該事件的會被調(diào)用典徊,然后再有相關事件被注冊的時候,就回到register(Object subscriber)
中恩够,從stickyEvents
中找到相關的事件消息再直接調(diào)用卒落,保證后注冊的的方法也能被立即調(diào)用到。
明白了這些我們再看看不同的poster
吧蜂桶,這里看看HandlerPoster
為例儡毕,分析之前先看看相關的封裝對象:
final class PendingPost {
//對象緩存池,降低創(chuàng)建對象開銷
private final static List<PendingPost> pendingPostPool = new ArrayList<PendingPost>();
Object event;//事件參數(shù)
Subscription subscription;//訂閱的相關欣欣
PendingPost next;//指向下一個對象
private PendingPost(Object event, Subscription subscription) {
this.event = event;
this.subscription = subscription;
}
//從緩存池中取出對象,如果緩沖池沒有腰湾,則new一個返回
static PendingPost obtainPendingPost(Subscription subscription, Object event) {
synchronized (pendingPostPool) {
int size = pendingPostPool.size();
if (size > 0) {
PendingPost pendingPost = pendingPostPool.remove(size - 1);
pendingPost.event = event;
pendingPost.subscription = subscription;
pendingPost.next = null;
return pendingPost;
}
}
return new PendingPost(event, subscription);
}
//釋放對象雷恃,并將對象加入緩存池,以便復用
static void releasePendingPost(PendingPost pendingPost) {
pendingPost.event = null;
pendingPost.subscription = null;
pendingPost.next = null;
synchronized (pendingPostPool) {
// Don't let the pool grow indefinitely
if (pendingPostPool.size() < 10000) {
pendingPostPool.add(pendingPost);
}
}
}
}
這個PendingPost
對象费坊,很明顯是鏈表設計倒槐,next
指向的就是下一個PendingPost
對象,其中的幾個方法也簡單易懂附井,并設計了緩存池實現(xiàn)復用讨越,考慮細致。
再看看鏈表的設計:
final class PendingPostQueue {
private PendingPost head;//鏈表頭對象
private PendingPost tail;//鏈表尾對象
//對象入隊操作
synchronized void enqueue(PendingPost pendingPost) {
if (pendingPost == null) {
throw new NullPointerException("null cannot be enqueued");
}
//如果尾部對象不為空永毅,那么將尾部next對象指向當前對象
//然后尾部對象再指向當前對象把跨,這樣就將對象插入到尾部
if (tail != null) {
tail.next = pendingPost;
tail = pendingPost;
} else if (head == null) {
//如果頭部對象為空,那么頭尾均為當前對象
head = tail = pendingPost;
} else {
throw new IllegalStateException("Head present, but no tail");
}
//釋放鎖
notifyAll();
}
//取出一個對象卷雕,從頭部取
synchronized PendingPost poll() {
PendingPost pendingPost = head;
if (head != null) {
//每取出一個就將頭部的引用指向下個對象节猿,完成出隊
head = head.next;
if (head == null) {//如果為空了,那么隊列為空
tail = null;
}
}
return pendingPost;
}
//可以讓線程等待一定時間再取對象
synchronized PendingPost poll(int maxMillisToWait) throws InterruptedException {
if (head == null) {
wait(maxMillisToWait);
}
return poll();
}
}
該對象主要實現(xiàn)了單鏈表漫雕,入隊出隊操作也比較簡單明了滨嘱。
弄明白了這個鏈表結構,再看看HandlerPoster
就比較清晰了:
final class HandlerPoster extends Handler {
private final PendingPostQueue queue;//事件隊列
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();
}
//主要完成入隊浸间,并發(fā)送消息功能
void enqueue(Subscription subscription, Object event) {
//對象緩沖池獲取對象
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
synchronized (this) {
queue.enqueue(pendingPost);//入隊操作
if (!handlerActive) {//如果沒有發(fā)送太雨,那么發(fā)送消息
handlerActive = true;
//如果消息沒有發(fā)送成功,那么拋出異常
if (!sendMessage(obtainMessage())) {
throw new EventBusException("Could not send handler message");
}
}
}
}
@Override
public void handleMessage(Message msg) {
boolean rescheduled = false;
try {
long started = SystemClock.uptimeMillis();
while (true) {//進入事件處理循環(huán)
//先獲取一個事件
PendingPost pendingPost = queue.poll();
if (pendingPost == null) {
//如果為null就進行再次同步檢測
synchronized (this) {
// Check again, this time in synchronized
pendingPost = queue.poll();
if (pendingPost == null) {
//如果還是空魁蒜,那么結束循環(huán)
handlerActive = false;
return;
}
}
}
//事件不為空則進行事件的調(diào)用
eventBus.invokeSubscriber(pendingPost);
//這里如果while循環(huán)進行的時間超過規(guī)定時間囊扳,那么會退出下,同時再發(fā)送
//發(fā)送個消息兜看,這樣應該是防止長時間阻塞主線程锥咸,考慮到了事件過多的情況
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;
}
}
}
通過上面的注釋可以知道,HandlerPoster
會把消息入隊并在主線程處理细移,handlerActive
用來控制是否正在處理消息的狀態(tài)搏予,防止發(fā)送過快,并細致地考慮到主線程阻塞的可能性弧轧,這樣就完成了消息的處理雪侥。
另外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();
}
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);
}
}
}
@Override
public void run() {
try {
try {
while (true) {
//這里設定了一個等待時間,如果隊列為null了精绎,那么等待1000ms
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;
}
}
}
代碼和HandlerPoster
比較類似速缨,實現(xiàn)了Runnable
接口,加入了一個等待時間代乃,如果隊列為空了等待1000ms后已經(jīng)取不到對象旬牲,那么久真正退出了,也就是說盡量在同一個后臺線程處理完所有的消息。
看看``AsyncPoster```吧:
class AsyncPoster implements Runnable {
private final PendingPostQueue queue;
private final EventBus eventBus;
AsyncPoster(EventBus eventBus) {
this.eventBus = eventBus;
queue = new PendingPostQueue();
}
public void enqueue(Subscription subscription, Object event) {
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
queue.enqueue(pendingPost);
eventBus.getExecutorService().execute(this);
}
@Override
public void run() {
PendingPost pendingPost = queue.poll();
if(pendingPost == null) {
throw new IllegalStateException("No pending post available");
}
eventBus.invokeSubscriber(pendingPost);
}
}
非常簡單引谜,每次入隊都會在新的線程執(zhí)行一次牍陌,而不是進行while
循環(huán),這樣總是在新的線程進行消息的處理员咽,并且不能保證調(diào)用順序,這三個poster
的區(qū)別可以再慢慢體會贮预。
取消訂閱流程
public synchronized void unregister(Object subscriber) {
//首先根據(jù)注冊類找到所有的注冊事件類型
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
if (subscribedTypes != null) {
//遍歷事件類型贝室,從事件對應的subscriptions集合中,移除訂閱者
for (Class<?> eventType : subscribedTypes) {
unsubscribeByEventType(subscriber, eventType);
}
//然后移除訂閱者所有對應的事件類
typesBySubscriber.remove(subscriber);
} else {
Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
/** Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber. */
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++) {
Subscription subscription = subscriptions.get(i);
//如果找到了訂閱者仿吞,那么移除
if (subscription.subscriber == subscriber) {
subscription.active = false;
subscriptions.remove(i);
i--;
size--;
}
}
}
}
取消注冊流程較為簡單滑频,主要是移除訂閱者自身和所對應的事件信息。
以上就是在不使用注解處理器的情況下的事件發(fā)布和調(diào)用流程唤冈,下篇來分析下使用注解處理器的執(zhí)行過程峡迷。