Evenbus是基於java反射機制來實現事件分發的
- EventBus.getDefault().register(this);這裏的this一般情況下是Activity類,但其實它只要是一個Object就可以了,Eventbus支持任何對象的事件分發
Eventbus的regist流程
public void register(Object subscriber) {
register(subscriber, false, 0);
}
private synchronized void register(Object subscriber, boolean sticky, int priority) {
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriber.getClass());
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod, sticky, priority);
}
}
當Eventbus註冊之後實際上是在這裏面對這個類的所有方法做了一個遍歷檢測,然後篩選出符合條件的方法,這裏面有一個訂閱的概念,相當於Eventbus幫我們篩選出合適的方法,然後封裝成一個SubscriberMethod對象,這個對象裏面包含了篩選出來的方法的屬性,包括方法名,參數類型,也就是說Eventbus幫我們訂閱監聽了這些方法,這些事情是在subscriberMethodFinder.findSubscriberMethods(subscriber.getClass());裏面做的:
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
String key = subscriberClass.getName();
List<SubscriberMethod> subscriberMethods;
synchronized (methodCache) {
subscriberMethods = methodCache.get(key);
}
if (subscriberMethods != null) {
return subscriberMethods;
}
subscriberMethods = new ArrayList<SubscriberMethod>();
Class<?> clazz = subscriberClass;
HashSet<String> eventTypesFound = new HashSet<String>();
StringBuilder methodKeyBuilder = new StringBuilder();
while (clazz != null) {
String name = clazz.getName();
if (name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("android.")) {
// Skip system classes, this just degrades performance
break;
}
// Starting with EventBus 2.2 we enforced methods to be public (might change with annotations again)
Method[] methods = clazz.getDeclaredMethods();
for (Method method : methods) {
String methodName = method.getName();
if (methodName.startsWith(ON_EVENT_METHOD_NAME)) {
int modifiers = method.getModifiers();
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length == 1) {
String modifierString = methodName.substring(ON_EVENT_METHOD_NAME.length());
ThreadMode threadMode;
if (modifierString.length() == 0) {
threadMode = ThreadMode.PostThread;
} else if (modifierString.equals("MainThread")) {
threadMode = ThreadMode.MainThread;
} else if (modifierString.equals("BackgroundThread")) {
threadMode = ThreadMode.BackgroundThread;
} else if (modifierString.equals("Async")) {
threadMode = ThreadMode.Async;
} else {
if (skipMethodVerificationForClasses.containsKey(clazz)) {
continue;
} else {
throw new EventBusException("Illegal onEvent method, check for typos: " + method);
}
}
Class<?> eventType = parameterTypes[0];
methodKeyBuilder.setLength(0);
methodKeyBuilder.append(methodName);
methodKeyBuilder.append('>').append(eventType.getName());
String methodKey = methodKeyBuilder.toString();
if (eventTypesFound.add(methodKey)) {
// Only add if not already found in a sub class
subscriberMethods.add(new SubscriberMethod(method, threadMode, eventType));
}
}
} else if (!skipMethodVerificationForClasses.containsKey(clazz)) {
Log.d(EventBus.TAG, "Skipping method (not public, static or abstract): " + clazz + "."
+ methodName);
}
}
}
clazz = clazz.getSuperclass();
}
if (subscriberMethods.isEmpty()) {
throw new EventBusException("Subscriber " + subscriberClass + " has no public methods called "
+ ON_EVENT_METHOD_NAME);
} else {
synchronized (methodCache) {
methodCache.put(key, subscriberMethods);
}
return subscriberMethods;
}
}
這裏面還做了一個緩存優化,如果之前這個對象已經regist過並且還未unRegist,那麼對象方法的訂閱信息會保存在methodCache裏面,key就是類名,這時候要是有同一個類的對象,無論是不是同一個對象,都將直接去methodCache裏面去取相關的方法訂閱信息,如果methodCache沒有這個類的訂閱信息,那麼就繼續往下執行,首先:
if (name.startsWith("java.") || name.startsWith("javax.") || name.startsWith("android.")) {
// Skip system classes, this just degrades performance
break;
}
首先,jdk android sdk系統類全部屏蔽,接着方法名必須是以“onEvent”(ON_EVENT_METHOD_NAME = “onEvent”)開頭的,方法參數必須是隻有一個參數,方法必須是public類型,且不能是static,abstract修飾的方法,然後就接下去分析方法全名:
String modifierString = methodName.substring(ON_EVENT_METHOD_NAME.length());
ThreadMode threadMode;
if (modifierString.length() == 0) {
threadMode = ThreadMode.PostThread;
} else if (modifierString.equals("MainThread")) {
threadMode = ThreadMode.MainThread;
} else if (modifierString.equals("BackgroundThread")) {
threadMode = ThreadMode.BackgroundThread;
} else if (modifierString.equals("Async")) {
threadMode = ThreadMode.Async;
}
從這裏可知訂閱的方法必須是onEvent,onEventMainThread,onEventBackground,onEventAsync四種纔可被訂閱
Eventbus的4種線程模型
先總結下能被成功訂閱的方法條件:
1. 只有onEvent,onEventMainThread,onEventBackground,onEventAsync這四種方法能被訂閱
2. public方法,並且不能是static,abstract修飾
3. 方法的參數只能是一個
然後我們來看下Eventbus裏面定義的一些線程模型:
1. PostThread:當前線程,對應onEvent()方法,也就是說onEvent()方法裏面執行的代碼和當前調用onEvent()方法的線程屬於同一個線程,可能是主線程,也可能是單獨的線程
2. MainThread:主線程,對應onEventMainThread(),onEventMainThread()方法裏面的代碼一定只會在主線程中執行,所以可以在onEventMainThread()方法中直接進行UI更新操作,但是不能進行耗時操作
3. BackgroundThread:後臺線程,對應onEventBackground(),如果當前線程是主線程,那麼onEventBackground()裏面的代碼會被單獨開一個線程來執行,如果當前的線程已經屬於後臺線程,那麼onEventBackground()裏面的代碼將會直接在這個後臺線程裏面執行,不能執行UI操作,
4. Async:單獨的異步線程,對應onEventAsync(),無論onEventAsync()所在的線程是在主線程或者是在後臺線程,onEventAsync()裏面的代碼都會被單獨開闢一個新的線程來執行,所以在這裏面可以執行一些異步操作,但是不能執行UI操作
比較:BackgroundThread和Async的區別就在於Async裏面可以執行耗時較長操作,因爲每個操作都是一個單獨的線程,但是BackgroundThread就不能執行耗時較長的操作,BackgroundThread的後臺線程是排隊執行的,如果有一個耗時較長的操作在BackgroundThread裏面執行,那麼會導致其他的BackgroundThread事件分發延遲,因爲BackgroundThread正在執行上一個任務
繼續往下分析:
SubscriberMethod是一個訂閱方法的信息類,一個訂閱方法對應一個SubscriberMethod,如果訂閱類裏面有多個訂閱方法,這個訂閱類將會有一個SubscriberMethod的列表信息,裏面保存了訂閱方法,被訂閱的方法的線程模型,以及訂閱方法的參數類型,以下就是SubscriberMethod的信息:
final Method method;
final ThreadMode threadMode;
final Class<?> eventType;
/** Used for efficient comparison */
String methodString;
SubscriberMethod(Method method, ThreadMode threadMode, Class<?> eventType) {
this.method = method;
this.threadMode = threadMode;
this.eventType = eventType;
}
在訂閱方法都遍歷初始化完成後會生成一個SubscriberMethod的List,然後把這個List保存在一個全局的map當中,至此註冊結束
Eventbus的事件post流程
public void post(Object event) {
PostingThreadState postingState = currentPostingThreadState.get();
List<Object> eventQueue = postingState.eventQueue;
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 {
while (!eventQueue.isEmpty()) {
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
事件對象首先是被添加到一個List列表中,其中PostingThreadState是當前線程的分發事件的一些狀態記錄,其中也包含了一個List事件列表,只要是同一個線程的事件分發都會被添加到這裏面來,然後就開始反覆的從List列表中去取出事件並且post出去,執行的是postSingleEvent(eventQueue.remove(0), postingState):
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
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);
}
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));
}
}
}
我們不看其他的條件判斷,直接看postSingleEventForEventType(event, postingState, eventClass),繼續把事件信息往下傳遞:
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;
}
剛纔註冊的時候不是把訂閱對象的信息都保存到一個map裏面了嗎,就是這個subscriptionsByEventType,這裏從subscriptionsByEventType中get出跟事件對象類型相匹配的訂閱方法信息,繼續把事件對象往下傳遞postToSubscription(subscription, event, postingState.isMainThread):
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
case PostThread:
invokeSubscriber(subscription, event);
break;
case MainThread:
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
mainThreadPoster.enqueue(subscription, event);
}
break;
case BackgroundThread:
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);
}
}
好了,一層一層傳遞下來這裏纔是真正調用剛纔訂閱的那四個方法(onEvent,onEvent…)的代碼,很明顯,採用的是反射機制來調用的,大家最後都調用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);
}
}
直接採用反射執行上述四個方法,於是乎,你在regist的時候的那個Object裏面的四個onEvent等方法就能被成功調用執行了
Eventbus保證線程模型執行實現
現在來討論下Eventbus是如果實現後臺線程,主線程運行的,可以看到主線程採用mainThreadPoster.enqueue(subscription, event)執行,後臺線程採用backgroundPoster.enqueue(subscription, event)執行,單獨的異步線程採用asyncPoster.enqueue(subscription, event)執行,一個一個來看;
mainThreadPoster.enqueue(subscription, event):
mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
構造方法:
HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) {
super(looper);
this.eventBus = eventBus;
this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage;
queue = new PendingPostQueue();
}
mainThreadPoster是一個Handler,它直接跟主線程的Looper綁定,mainThreadPoster裏面維護了一個隊列PendingPostQueue,所以把事件對象加入隊列後,反覆的從隊列中去出事件對象然後用反射執行方法,代碼如下:
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");
}
}
}
}
@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;
}
}
所以對於多個事件分發到主線程中執行是一個一個執行的,而不是併發的,可以保證先post的先執行
BackgroundPoster是一個Runnable:
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;
}
}
}
話不多,還是用隊列來保證先post的先執行
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);
}
}
AsyncPoster跟BackgroundPoster實現類似,區別就在於BackgroundPoster在事件壓入隊列後在run方法裏面循環poll出事件來執行,而AsyncPoster是把事件壓入隊列後直接單獨開一個線程執行。沒聽明白是不?前面不是提到BackgroundThread和Async線程模型的區別在於BackgroundThread要是執行耗時較長的任務會導致後面的事件延遲執行而Async則不會,因爲AsyncPoster的enqueue()方法在把事件壓入事件隊列後立刻eventBus.getExecutorService().execute(this),而eventBus.getExecutorService()是Executors.newCachedThreadPool()創建的線程池,沒有線程數的限制,所以事件來幾個就執行幾個;而BackgroundPoster的enqueue()在把事件壓入事件隊列後只會執行一次eventBus.getExecutorService().execute(this),然後在run方法中開啓循環來從事件隊列裏面取出事件來執行eventBus.invokeSubscriber(pendingPost),invokeSubscriber()方法就是反射執行onEventBackGround()方法,如果在onEventBackGround()裏面耗時太久,那麼這次循環就一直沒結束,無法進入下一次循環所以會造成BackgroundThread線程模型的事件一直分發下來壓入隊列,但是一直無法執行,只有等上一個耗時任務執行完成才能繼續下一個
Eventbus總結
總結,Eventbus的幾個核心類
1. EventBus,這是個單例,主要是維護註冊對象的訂閱方法信息,對外提供Eventbus的使用接口
2. SubscriberMethod 訂閱方法信息封裝對象
3. AsyncPoster,BackgroundPoster,HandlerPoster保證訂閱方法在各個執行的線程模型裏面執行
4. PendingPostQueue訂閱事件的隊列實現
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();
}
}
其實PendingPostQueue只有一個頭部和尾部,說是隊列,其實應該端鏈表,PendingPost不斷的next連着下一個PendingPost