android EventBus原始碼解析
尊重原創,轉載請標明出處 http://blog.csdn.net/abcdef314159
eventBus3.1.1
eventBus主要用於資料之間的傳遞,使用也非常簡單,就幾個主要的方法,一個是register和unregister,這兩個要成對出現,一般在onCreate中註冊,在onDestroy中取消註冊。還有幾個方法post,postSticky,removeAllStickyEvents。其中post必須在register之後才有效,否則接收不到資訊,postSticky可以在register之前和之後都可以。先看一下register方法
/** * 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. */ public void register(Object subscriber) { Class<?> subscriberClass = subscriber.getClass(); List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass); synchronized (this) { for (SubscriberMethod subscriberMethod : subscriberMethods) { subscribe(subscriber, subscriberMethod); } } }
這裡面主要看一下findSubscriberMethods方法,他是找到你所在註冊類的註解方法,因為一般情況下要想接收資料,必須要加註解的方法,比如@Subscribe(threadMode = ThreadMode.MAIN)或者@Subscribe(threadMode = ThreadMode.MAIN, sticky = true),當然你也可以修改threadMode指定在其他執行緒中操作。
POSTING :表示傳送事件和接收事件在相同的執行緒
MAIN :表示在主執行緒中處理資料
MAIN_ORDERED:和MAIN一樣是在主執行緒中操作,但需要排隊
BACKGROUND :在後臺執行緒中執行
ASYNC :在另起一個非同步執行緒中執行
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) { List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass); if (subscriberMethods != null) { return subscriberMethods; } 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 { METHOD_CACHE.put(subscriberClass, subscriberMethods); return subscriberMethods; } }
上面程式碼中ignoreGeneratedIndex預設情況下是false,其中findUsingReflection和findUsingInfo有可能最終呼叫的都是findUsingReflectionInSingleClass,為啥說是有可能,是因為findUsingInfo取值的時候會從先從subscriberInfoIndexes中取,如果有就返回,沒有就會呼叫findUsingReflectionInSingleClass方法,所以來看一下findUsingInfo方法
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
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);
}
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
注意這裡有個方法moveToSuperclass和上面的while迴圈,moveToSuperclass是獲取FindState欄位clazz的父類,就是在當前類中查詢之後還要在父類中查詢註解的方法,不斷往上找,直到父類為空為止。其中FindState是一個數據池FindState的物件,預設值為4,private static final int POOL_SIZE = 4;如果有就從池中取,沒有就建立,也是為了提高速度,看一下第一行程式碼prepareFindState
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();
}
來看一下上面的getSubscriberInfo方法,如果獲取為空,就會執行findUsingReflectionInSingleClass方法,來看一下getSubscriberInfo的具體實現
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;
}
下面的subscriberInfoIndexes是由SubscriberMethodFinder的構造方法傳進來的值,這裡有一個官方提供的優化方法,就是從subscriberInfoIndexes中取,這個最後在介紹。我們先往上看,會發現無論是findUsingInfo還是findUsingReflection方法,在最後都會呼叫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;
}
其實就相當於FindState的回收和訂閱方法的返回。下面再來看一下重量級方法findUsingReflectionInSingleClass
private void findUsingReflectionInSingleClass(FindState findState) {
Method[] methods;
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();
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
Class<?>[] parameterTypes = method.getParameterTypes();
if (parameterTypes.length == 1) {
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
if (subscribeAnnotation != null) {
Class<?> eventType = parameterTypes[0];
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)) {
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");
}
}
}
通過反射的方式找到註解的方法,從上面程式碼可以看出,註解的方法只能有一個引數,其中checkAdd是根據方法和引數進行驗證。然後把找到的存到FindState中。OK,關於註解方法的查詢也就這些,下面看回過頭來看一下register發具體實現,在subscriberMethodFinder.findSubscriberMethods(subscriberClass)方法查詢之後,然後進行遍歷
// Must be called in synchronized block
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);
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);
}
}
}
程式碼比較簡單,這裡要記住一下subscriptionsByEventType欄位,為什麼在register之前post會收不到訊息,而在register之前postSticky確能收到訊息。因為這裡會把當前訂閱的方法存入到subscriptionsByEventType中,post的時候如果還沒有register,那麼subscriptionsByEventType就會為空,當然收不到訊息,而postSticky是粘性事件,會儲存在stickyEvents中,在register的時候還可以在觸發。看一下上面程式碼的第31行,如果之前傳送的是粘性事件,也就是postSticky,那麼這裡就會執行下面的checkPostStickyEventToSubscription方法。上面的boolean eventInheritance = true;預設值為true。checkPostStickyEventToSubscription方法會呼叫postToSubscription,來看一下postToSubscription方法
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 MAIN_ORDERED:
if (mainThreadPoster != null) {
mainThreadPoster.enqueue(subscription, event);
} else {
// temporary: technically not correct as poster not decoupled from subscriber
invokeSubscriber(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);
}
}
這裡就是上面說的幾種執行緒中的操作,看一下MAIN和MAIN_ORDERED。我們以MAIN執行緒為例繼續看,來看一下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);
}
}
這回是徹底明白了,找到訂閱的方法,然後通過反射進行呼叫。這裡只是在register中的呼叫,他只能呼叫sticky的事件。下面看一下最主要的兩個方法post和postSticky
/**
* Posts the given event to the event bus and holds on to the event (because it is sticky). The most recent sticky
* event of an event's type is kept in memory for future access by subscribers using {@link Subscribe#sticky()}.
*/
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);
}
我們看到postSticky呼叫了post,和post唯一的區別就是他儲存了event物件,儲存在stickyEvents中,所以postSticky的事件可以在register之前呼叫原理就在這,把當前訂閱的類儲存在stickyEvents中,然後register的時候就可以呼叫,而post沒有儲存,所以register的時候自然沒法觸發,這裡要注意在上面分析的subscribe方法中,我們知道他只能觸發sticky的事件,我們接著往下看post方法
/** 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);
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;
}
}
}
這裡就不在過多介紹,主要看一下postSingleEvent方法
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) {
logger.log(Level.FINE, "No subscribers registered for event " + eventClass);
}
if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
eventClass != SubscriberExceptionEvent.class) {
post(new NoSubscriberEvent(this, event));
}
}
}
接著看postSingleEventForEventType方法
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;
}
這裡我們先看一下postToSubscription,就是上面剛分析的,就不在說了,這裡我們來看一下subscriptionsByEventType,我們上面分析的subscriptionsByEventType是在register的時候才會把訂閱的事件儲存,如果在register之前呼叫post和postSticky方法,那麼這裡subscriptionsByEventType返回的自然是空,所以也就不會執行下面的程式碼了,但postSticky不同,雖然他不能執行,但它把訂閱的物件儲存在了stickyEvents中,在register的時候就會觸發了。下面再來說說上面遺留的問題,上面說道subscriberInfoIndexes中取值的時候的問題,這個欄位是在SubscriberMethodFinder構造方法中帶過來的,而SubscriberMethodFinder是在EventBus類的建構函式中初始化的,而EventBus的建構函式傳入的是EventBusBuilder,使用的是建造者模式,且subscriberInfoIndexes預設為空,我們看一下他傳值的方法
/** Adds an index generated by EventBus' annotation preprocessor. */
public EventBusBuilder addIndex(SubscriberInfoIndex index) {
if (subscriberInfoIndexes == null) {
subscriberInfoIndexes = new ArrayList<>();
}
subscriberInfoIndexes.add(index);
return this;
}
這個很簡單,實際上還可以使用一種更加高效的方法,自動為我們生成一個MyEventBusIndex類,它裡面會包含我們註解的方法。具體實現是在app的defaultConfig中新增下面程式碼
javaCompileOptions {
annotationProcessorOptions {
arguments = [ eventBusIndex : 'com.example.myapp.MyEventBusIndex' ]
}
}
然後在dependencies中新增下面程式碼
implementation 'org.greenrobot:eventbus:3.1.1'
annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.1.1'
就會自動為我們生成一個類,具體位置如下,
這裡我寫了兩個類,每個類都寫了兩個註解的方法
@Subscribe(threadMode = ThreadMode.MAIN)
public void onReceiveEvent(String event) {
Log.d("wld_____", "FirstActivity:onReceiveEvent1:" + event);
}
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
public void onReceiveStickyEvent(String event) {
Log.d("wld_____", "FirstActivity:onReceiveStickyEvent2:" + event);
}
和
@Subscribe(threadMode = ThreadMode.MAIN)
public void onReceiveEvent(String event) {
Log.d("wld_____", "SecondActivity:onReceiveEvent:" + event);
}
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
public void onReceiveStickyEvent(String event) {
Log.d("wld_____", "SecondActivity:onReceiveStickyEvent:" + event);
}
我們來看一下生成的MyEventBusIndex類
package com.example.myapp;
import org.greenrobot.eventbus.meta.SimpleSubscriberInfo;
import org.greenrobot.eventbus.meta.SubscriberMethodInfo;
import org.greenrobot.eventbus.meta.SubscriberInfo;
import org.greenrobot.eventbus.meta.SubscriberInfoIndex;
import org.greenrobot.eventbus.ThreadMode;
import java.util.HashMap;
import java.util.Map;
/** 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(yiquan.xianquan.com.myapplication.SecondActivity.class, true,
new SubscriberMethodInfo[] {
new SubscriberMethodInfo("onReceiveEvent", String.class, ThreadMode.MAIN),
new SubscriberMethodInfo("onReceiveStickyEvent", String.class, ThreadMode.MAIN, 0, true),
}));
putIndex(new SimpleSubscriberInfo(yiquan.xianquan.com.myapplication.FirstActivity.class, true,
new SubscriberMethodInfo[] {
new SubscriberMethodInfo("onReceiveEvent", String.class, ThreadMode.MAIN),
new SubscriberMethodInfo("onReceiveStickyEvent", String.class, ThreadMode.MAIN, 0, true),
}));
}
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;
}
}
}
至於他的使用,可以這樣
EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();
EventBus eventBus = EventBus.getDefault();
這裡最好把EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();放到Application中,只初始化一次,如果多次初始化會直接拋異常,我們看一下原始碼
/**
* Installs the default EventBus returned by {@link EventBus#getDefault()} using this builders' values. Must be
* done only once before the first usage of the default EventBus.
*
* @throws EventBusException if there's already a default EventBus instance in place
*/
public EventBus installDefaultEventBus() {
synchronized (EventBus.class) {
if (EventBus.defaultInstance != null) {
throw new EventBusException("Default instance already exists." +
" It may be only set once before it's used the first time to ensure consistent behavior.");
}
EventBus.defaultInstance = build();
return EventBus.defaultInstance;
}
}
所以他只能初始化一次。OK,EventBus的原理基本已經分析完畢,下面來看一下具體使用。下面有兩個類FirstActivity和SecondActivity,我們暫且標記為A和B
public class FirstActivity extends AppCompatActivity {
private Button button1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_first);
EventBus.getDefault().register(this);
button1 = findViewById(R.id.button1);
button1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
EventBus.getDefault().postSticky("111111");
startActivity(new Intent(FirstActivity.this, SecondActivity.class));
}
});
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onReceiveEvent(String event) {
Log.d("wld_____", "FirstActivity:onReceiveEvent:" + event);
}
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
public void onReceiveStickyEvent(String event) {
Log.d("wld_____", "FirstActivity:onReceiveStickyEvent:" + event);
}
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
}
public class SecondActivity extends AppCompatActivity {
private Button button2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
EventBus.getDefault().register(this);
button2 = findViewById(R.id.button2);
button2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
finish();
}
});
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onReceiveEvent(String event) {
Log.d("wld_____", "SecondActivity:onReceiveEvent:" + event);
}
@Subscribe(threadMode = ThreadMode.MAIN, sticky = true)
public void onReceiveStickyEvent(String event) {
Log.d("wld_____", "SecondActivity:onReceiveStickyEvent:" + event);
}
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
}
執行一下,看一下列印的log
結果顯然是正確的,因為在A和B中都是先註冊,所以會獲得他們註解的方法,當在A中傳送訊息的時候A的兩個方法都是可以接收到訊息的,但在B中由於B還沒有啟動,所以當B啟動的時候只能接收到sticky註冊的方法。改一下程式碼再看一下
EventBus.getDefault().post("111111");
startActivity(new Intent(FirstActivity.this, SecondActivity.class));
看一下列印log,
我們發現只有A的兩個方法執行了,B的方法一個也沒執行,這個也很好理解,因為B還沒有註冊就開始傳送訊息,所以收不到。再來改一下程式碼看看,調整一下順序
startActivity(new Intent(FirstActivity.this, SecondActivity.class));
EventBus.getDefault().post("111111");
看一下列印log
一樣B不會列印,這是因為Activity的啟動是耗時的,而B還沒啟動就開始傳送訊息,自然是接收不到的,我們再改一下,延遲30毫秒在傳送,30毫秒的時間Activity應該完全啟動了吧,我們看一下
startActivity(new Intent(FirstActivity.this, SecondActivity.class));
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
EventBus.getDefault().post("111111");
}
}, 30);
}
再來看一下列印log
看到沒,A和B的兩個註解的方法都執行了,這是因為延遲之後A和B都已經啟動了,但A的onDestroy還沒有執行,所以兩個類的註解方法都會執行的。OK,到這裡EventBus的原理就已經分析完了。