根據(jù)上一篇文章淺析EventBus 3.0實(shí)現(xiàn)思想 對EventBus的概括,本文針對其中一些重要且比較有意思的知識(shí)點(diǎn)喳逛,做一下如下的匯總整理 :
FindState的妙用
在EventBus中,會(huì)根據(jù)class信息棵里,來獲取SubscriberMethod
,這里會(huì)在SubscriberMethodFinder
中進(jìn)行處理润文,提供了兩種方式來進(jìn)行獲取:
- 通過
findUsingInfo(Class<?> subscriberClass)
在apt中進(jìn)行查找獲取 - 使用'findUsingReflection(Class<?> subscriberClass)'方法殿怜,進(jìn)行反射來獲取
而在這里典蝌,EventBus采用了一個(gè)中間器FindState
,來看一下它的結(jié)構(gòu):
static class FindState {
final List<SubscriberMethod> subscriberMethods = new ArrayList<>();
Class<?> subscriberClass;
Class<?> clazz;
boolean skipSuperClasses;
SubscriberInfo subscriberInfo;
}
這里對查找的狀態(tài)值做了一些封裝头谜,其中有訂閱類subscriberClass
,事件對象clazz
,以及查找的結(jié)果subscriberMethods
骏掀、subscriberInfo
等,另外柱告,還有一個(gè)判斷的標(biāo)志量skipSuperClasses
截驮,用來標(biāo)記是否需要進(jìn)行父類的查看查找。
同時(shí)际度,我們可以看出在使用EventBus定義訂閱方法的時(shí)候葵袭,有些通用的邏輯,是可以抽象放置在父類中的乖菱。
為什么要使用FindState呢坡锡?首先是面向?qū)ο蠓庋b的采用,那么看看它給我們提供了哪些方法窒所?
void initForSubscriber(Class<?> subscriberClass) {
...
}
boolean checkAdd(Method method, Class<?> eventType) {
...
}
private boolean checkAddWithMethodSignature(Method method, Class<?> eventType) {
...
}
void moveToSuperclass() {
...
}
方法中的initForSubscriber
是用來初始化傳入訂閱類的鹉勒,兩個(gè)check方法則是用來檢查方法信息的,這樣用來保證獲取的訂閱方法都是合法的吵取。moveToSuperClass
則是需要查看父類中的訂閱方法禽额。這樣對方法檢查的邏輯,我們就把它們抽象在了FindState中海渊。
緩存的使用
使用java的绵疲,應(yīng)該要知道頻繁地創(chuàng)建對象哲鸳,是非常消耗資源的,在jvm垃圾回收時(shí)候盔憨,會(huì)出現(xiàn)內(nèi)存抖動(dòng)的問題徙菠。所以,我們在這里郁岩,一定要注意緩存的使用婿奔。
上文中提到的中間器FindState,就采用了緩存:
private static final int POOL_SIZE = 4;
private static final FindState[] FIND_STATE_POOL = new FindState[POOL_SIZE];
指定了FindState的緩存大小為4问慎,并使用一維的靜態(tài)數(shù)組萍摊,所以這里需要注意線程同步的問題:
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, 可以看到的是對這段緩存的獲取使用了synchronized
關(guān)鍵字,來將緩存中FindState的獲取如叼,變?yōu)橥綁K冰木。
而在subscriberMethod的獲取的同時(shí),則對FindState的緩存做了添加的操作笼恰,同樣是也必須是同步代碼塊:
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;
}
另外踊沸,EventBus也對subsciberMethod的獲取,也做了緩存的操作社证,這樣進(jìn)行SubscriberMethod查找的時(shí)候逼龟,則優(yōu)先進(jìn)行緩存的查找:
private static final Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>();
這里,使用的是數(shù)據(jù)結(jié)構(gòu)是ConcurrentHashMap
追葡,就可以不必寫大量的同步代碼塊了腺律。
反射類方法的使用
反射雖然是比較浪費(fèi)性能的,但對我們Java開發(fā)者來說宜肉,這又是必須掌握的一個(gè)技能匀钧,現(xiàn)在來熟悉一下EventBus中通過@Subscribe
注解對SubscriberMethod
的查找:
private void findUsingReflectionInSingleClass(FindState findState) {
Method[] methods;
// 優(yōu)先使用getDeclareMethods方法,如注釋中所說谬返,比getMethods方法塊榴捡。
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
methods = findState.clazz.getMethods();
findState.skipSuperClasses = true;
}
for (Method method : methods) {
int modifiers = method.getModifiers();
// 通過訪問符只獲取public
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
Class<?>[] parameterTypes = method.getParameterTypes();
// 方法的參數(shù)(事件類型)長度只能為1
if (parameterTypes.length == 1) {
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
if (subscribeAnnotation != null) {
Class<?> eventType = parameterTypes[0];
// 獲取到annotation中的內(nèi)容,進(jìn)行subscriberMethod的添加
if (findState.checkAdd(method, eventType)) {
ThreadMode threadMode = subscribeAnnotation.threadMode();
findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
}
}
} else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
//拋出方法參數(shù)只能為1的異常
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)) {
//拋出方法訪問符只能為public的異常
String methodName = method.getDeclaringClass().getName() + "." + method.getName();
throw new EventBusException(methodName +
" is a illegal @Subscribe method: must be public, non-static, and non-abstract");
}
}
}
其中朱浴,最核心的類便是Method
和Class
,通過Class
的getDeclaredMethods
及getMethods
來進(jìn)行方法信息的獲却镆翰蠢;使用Method
類的getParameterTypes
獲取方法的參數(shù)及getAnnotation
獲取方法的注解類。
線程處理類信息的使用
在EventBus類中啰劲,定義了4種線程處理的策略:
public enum ThreadMode {
POSTING,
MAIN,
BACKGROUND,
ASYNC
}
POSTING
采用與事件發(fā)布者相同的線程梁沧,MAIN
指定為主線程,BACKGROUND
指定為后臺(tái)線程蝇裤,而ASYNC
相比前三者不同的地方是可以處理耗時(shí)的操作廷支,其采用了線程池频鉴,且是一個(gè)異步執(zhí)行的過程,即事件的訂閱者可以立即得到執(zhí)行恋拍。
這里垛孔,我們主要看兩個(gè)Poster, BackgroundPoster
和AsyncPoster
:
BackgroundPoster - 后臺(tái)任務(wù)執(zhí)行
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) {
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;
}
}
}
代碼中施敢,主要通過enqueue
方法周荐,將當(dāng)前的訂閱者添加至隊(duì)列PendingPostQueue
中,是否立即執(zhí)行僵娃,則需要判斷當(dāng)前隊(duì)列是否還有正在執(zhí)行的任務(wù)概作,若沒有的話,則立即執(zhí)行默怨,若還有執(zhí)行任務(wù)的話讯榕,則只進(jìn)行隊(duì)列的添加。這樣匙睹,保證了后臺(tái)任務(wù)永遠(yuǎn)只會(huì)在一個(gè)線程執(zhí)行愚屁。
AsyncPoster - 異步任務(wù)執(zhí)行
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);
}
}
這段代碼就很簡單了,直接通過線程池調(diào)用執(zhí)行垃僚,相比BackgroundPoster
執(zhí)行來說集绰,則沒有等待的過程。
事件執(zhí)行隊(duì)列 PendingPostQueue
EventBus對事件的執(zhí)行谆棺,采用隊(duì)列的數(shù)據(jù)結(jié)構(gòu):
final class PendingPostQueue {
private PendingPost head;
private PendingPost tail;
synchronized void enqueue(PendingPost pendingPost) {
if (pendingPost == null) {
throw new NullPointerException("null cannot be enqueued");
}
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();
}
}
而對PendingPost
的封裝栽燕,使用了數(shù)據(jù)緩存池:
final class PendingPost {
private final static List<PendingPost> pendingPostPool = new ArrayList<PendingPost>();
Object event;
Subscription subscription;
PendingPost next;
// 對PendingPost的獲取,優(yōu)先從緩存池中拿
private PendingPost(Object event, Subscription subscription) {
this.event = event;
this.subscription = subscription;
}
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);
}
// 對PendingPost釋放時(shí)改淑,將其添加到緩存池中
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);
}
}
}
}
可以看到其對緩存的大小限制到10000碍岔,好任性啊。朵夏。
總結(jié)
EventBus給我們提供了相當(dāng)強(qiáng)大的功能蔼啦,同時(shí)它的寫法也相當(dāng)有味道,值得我們深深地去研究仰猖∧笾總的來說,其中EventBus采用了Facade模式饥侵,方便開發(fā)者的統(tǒng)一調(diào)用鸵赫;另外不同的線程策略,以及反射代碼躏升,Apt處理代碼生成以及緩存的大量使用辩棒。
轉(zhuǎn)載請注明原文鏈接:http://alighters.com/blog/2016/05/24/eventbus-3-dot-0-indepth-knowledge/