Android開源項目原理系列
[搞定開源] 第一篇 okhttp 3.10原理
[搞定開源] 第二篇 okio 1.14原理
[搞定開源] 第三篇 retrofit 2.4.0與設計模式
[搞定開源] 第四篇 手動實現RxJava2線程切換
EventBus是Android的發佈訂閱開源庫,優勢和使用方法不用說了,簡單過一下源碼,瞭解核心原理。
當前EventBus最新版本是3.1.1,和前代相比,方法名可以自定義,新增註解支持。
EventBus構造函數
優秀開源庫的基本特徵是,常用功能用一個入口類就可以玩轉,EventBus也是。
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
獲取EventBus對象常規使用getDefault,典型的Double Check獲取單例。如果有特別需求,可以通過EventBusBuilder,典型的建造者模式。
EventBus構造函數創建了十多個對象,關注其中的:
EventBus(EventBusBuilder builder) {
logger = builder.getLogger();
//...
mainThreadSupport = builder.getMainThreadSupport();
mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
backgroundPoster = new BackgroundPoster(this);
//...
}
Log
EventBus的日誌打印使用Logger接口,已經實現了:
- AndroidLogger
- JavaLogger
- SystemOutLogger
static final boolean ANDROID_LOG_AVAILABLE;
static {
boolean android = false;
try {
android = Class.forName("android.util.Log") != null;
} catch (ClassNotFoundException e) {
// OK
}
ANDROID_LOG_AVAILABLE = android;
}
public static boolean isAndroidLogAvailable() {
return ANDROID_LOG_AVAILABLE;
}
對於Android,直接嘗試獲取Log類,同時用來判斷當前是否Android。
獲取Android的UI線程
EventBus如何讓事件執行在Android的UI線程呢?
MainThreadSupport getMainThreadSupport() {
if (mainThreadSupport != null) {
return mainThreadSupport;
} else if (Logger.AndroidLogger.isAndroidLogAvailable()) {
Object looperOrNull = getAndroidMainLooperOrNull();
return looperOrNull == null ? null :
new MainThreadSupport.AndroidHandlerMainThreadSupport((Looper) looperOrNull);
} else {
return null;
}
}
Object getAndroidMainLooperOrNull() {
try {
return Looper.getMainLooper();
} catch (RuntimeException e) {
// Not really a functional Android (e.g. "Stub!" maven dependencies)
return null;
}
}
這裏用到了上面判斷Android的方法,如果是,創建AndroidHandlerMainThreadSupport,保存了Looper.getMainLooper()。很熟悉吧,這就是Android的UI線程Looper。
Poster
EventBus的event可以指定運行在UI線程或者後臺線程,靠的就是Poster,下文會說到,透露一丟丟:
- UI線程:MainLooper
- 後臺線程:線程池
註冊/取消註冊
想要接收EventBus發送的事件,對象必須先註冊,停止接收時取消註冊。註冊過程,本質是通知EventBus查找對象中的訂閱方法。
Subscribe註解
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent4Main(MsgEvent event) {}
EventBus3之後,訂閱方法名改爲自定義,通過註解描述。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
ThreadMode threadMode() default ThreadMode.POSTING;
boolean sticky() default false;
int priority() default 0;
}
除了基礎的ThreadMode線程模型,還可以定義粘性事件和優先級。
註解信息,在註冊過程最後由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);
}
}
}
註冊時傳入訂閱對象,兩部操作:
- 分析訂閱對象的類,SubscriberMethodFinder從中查找訂閱方法,包裝爲SubscriberMethod。
- 調用subscribe,數據轉化爲EventBus內部的兩個Map。
List<SubscriberMethod>
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
//1
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
//2
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 {
//3
METHOD_CACHE.put(subscriberClass, subscriberMethods);
return subscriberMethods;
}
}
- 查找目標類的訂閱方法前,先查找緩存METHOD_CACHE,用的是ConcurrentHashMap,因爲要處理多線程。
Map<Class<?>, List<SubscriberMethod>> METHOD_CACHE = new ConcurrentHashMap<>()
- 默認創建的EventBus對象ignoreGeneratedIndex=false,先看findUsingInfo
- 獲取List成功後,保存在緩存METHOD_CACHE
SubscriberMethodFinder.FindState
上面第二步重點是使用SubscriberMethodFinder的內部類FindState執行查找過程:
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
//1
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
//2
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 {
//3
findUsingReflectionInSingleClass(findState);
}
//4
findState.moveToSuperclass();
}
//5
return getMethodsAndRelease(findState);
}
第一步是調用prepareFindState獲取FindState:
private static final int POOL_SIZE = 4;
private static final FindState[] FIND_STATE_POOL = new FindState[POOL_SIZE];
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的創建不是直接new,而是嘗試從FIND_STATE_POOL中獲取(最多保存4個),目測是享元模式,沒有才new對象。
第二步進入循環,從當前類開始,遍歷父類查找訂閱方法。
這裏涉及EventBus3新特性,運行時反射查找方法當然會慢點,可以使用Subscriber Index在編譯期操作加速,具體不展開,直接看else部分。
第三步的findUsingReflectionInSingleClass,使用反射查找,細節不用展開。
第四步將當前查找目標切換到父類,繼續循環查找。
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,放回FIND_STATE_POOL,下次繼續利用。
subscribe
register函數對每個SubscriberMethod執行subscribe,注意這裏synchronized當前EventBus對象。
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class<?> eventType = subscriberMethod.eventType;
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions == null) {
subscriptions = new CopyOnWriteArrayList<>();
subscriptionsByEventType.put(eventType, subscriptions);
} else {
if (subscriptions.contains(newSubscription)) {
throw new EventBusException("Subscriber " + subscriber.getClass() + " already registered to event "
+ eventType);
}
}
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;
}
}
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
//... 粘性事件略
}
subscribe不外乎是對數據的挪騰,目的是構建subscriptionsByEventType和typesBySubscriber兩個Map:
subscriptionsByEventType
private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
Subscription包裝了訂閱對象和訂閱方法,因此Map的key/value對應如下:
event類型 -> List<訂閱對象,訂閱方法>
表示了event由哪些對象的哪個方法訂閱了。
typesBySubscriber
private final Map<Object, List<Class<?>>> typesBySubscriber;
typesBySubscriber簡單些,表示訂閱對象訂閱了哪些event類型:
訂閱對象 -> List<event類型>
取消註冊
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 {
logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
取消註冊的邏輯很簡單,將當前訂閱者的信息從subscriptionsByEventType和typesBySubscriber中移除。
發送
PostingThreadState(每個線程一份)
EventBus發送event調用post方法,首先來認識EventBus核心的內部類PostingThreadState。
private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
@Override
protected PostingThreadState initialValue() {
return new PostingThreadState();
}
};
final static class PostingThreadState {
final List<Object> eventQueue = new ArrayList<>();
boolean isPosting;
boolean isMainThread;
Subscription subscription;
Object event;
boolean canceled;
}
PostingThreadState包含eventQueue和一些標誌變量,通過ThreadLocal在每個線程中維護一個對象(ThreadLocal的initialValue方法初始創建PostingThreadState對象)。
沒有深奧的玄妙,看數據結構大概可以知道,EventBus發送原理是將event入隊在線程上的PostingThreadState,再查找訂閱者發送。
event進出隊
public void post(Object event) {
PostingThreadState postingState = currentPostingThreadState.get();
List<Object> eventQueue = postingState.eventQueue;
eventQueue.add(event);
if (!postingState.isPosting) {
postingState.isMainThread = isMainThread();
postingState.isPosting = true;
if (postingState.canceled) {
throw new EventBusException("Internal error. Abort state was not reset");
}
try {
while (!eventQueue.isEmpty()) {
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
發送event的邏輯很明確,取出當前線程的PostingThreadState,將event入隊。當不在發送狀態時,循環調用postSingleEvent發送所有event。
發送event
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
//1
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
if (eventInheritance) {
List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
int countTypes = eventTypes.size();
for (int h = 0; h < countTypes; h++) {
Class<?> clazz = eventTypes.get(h);
subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
}
} else {
subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
}
//2
if (!subscriptionFound) {
if (logNoSubscriberMessages) {
logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
}
if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
eventClass != SubscriberExceptionEvent.class) {
post(new NoSubscriberEvent(this, event));
}
}
}
第一部分先收集event的類型,eventInheritance在EventBus默認構造函數裏爲true,表示會向上遍歷event的父類。然後調用postSingleEventForEventType執行發送過程,得到發送結果subscriptionFound。
第二部分,如果找不到訂閱者,EventBus將會發送NoSubscriberEvent這個事件,業務上可以接收處理。
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
subscriptions = subscriptionsByEventType.get(eventClass);
}
if (subscriptions != null && !subscriptions.isEmpty()) {
for (Subscription subscription : subscriptions) {
postingState.event = event;
postingState.subscription = subscription;
boolean aborted = false;
try {
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;
}
postSingleEventForEventType根據event類型從subscriptionsByEventType獲取所有訂閱者,對每個訂閱者執行postToSubscription。
訂閱方法各有執行線程的要求,postToSubscription其實就是對各個線程模型的分開處理,下面分析常用的POSTING、MAIN和BACKGROUND。
POSTING
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);
}
}
POSTING,在哪個線程發送,就在哪個線程處理事件,直接反射invoke。
MAIN
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
mainThreadPoster.enqueue(subscription, event);
}
如果當前已經在UI線程,直接調用invokeSubscriber,否則調用mainThreadPoster將event入隊。
前面分析了AndroidHandlerMainThreadSupport保存了UI線程的Looper,通過Looper創建HandlerPoster,mainThreadPoster正是HandlerPoster的對象。有了UI線程的Handler,接下來的都是順理成章的代碼。
public void enqueue(Subscription subscription, Object event) {
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
synchronized (this) {
queue.enqueue(pendingPost);
if (!handlerActive) {
handlerActive = true;
if (!sendMessage(obtainMessage())) {
throw new EventBusException("Could not send handler message");
}
}
}
}
訂閱者信息Subscription和event包裝爲PendingPost,通過next字段,多個PendingPost構建隊列PendingPostQueue。
@Override
public void handleMessage(Message msg) {
boolean rescheduled = false;
try {
long started = SystemClock.uptimeMillis();
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;
}
}
}
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;
}
}
通過發送空message,切換線程到UI線程,從隊列中取出event調用invokeSubscriber。注意到,在UI線程有執行時長限制,超時會拋出異常。
BACKGROUND
if (isMainThread) {
backgroundPoster.enqueue(subscription, event);
} else {
invokeSubscriber(subscription, event);
}
- 如果事件在UI線程中發佈,那麼該事件就會在新的線程中運行
- 如果事件在子線程中發佈,那麼該事件直接在發佈事件的線程中執行
BackgroundPoster處理事件在新線程中的運行,同樣的創建PendingPost併入隊。
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);
}
}
}
線程池從EventBus對象獲取,看定義,是一個線程數幾乎無限制的CachedThreadPool。
private final static ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool();
最終在線程池的執行過程也是PendingPost出隊,調用invokeSubscriber。(代碼嵌套很深!)
@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) {
eventBus.getLogger().log(Level.WARNING, Thread.currentThread().getName() + " was interruppted", e);
}
} finally {
executorRunning = false;
}
}
後記
EventBus源碼沒有太難的地方,快速通過。