EventBus的使用和原始碼解析
為什麼要使用EventBus
在Android開發過程中經常有這樣的場景–>棧頂Activity需要關閉非棧頂的Activity,或呼叫其中的某些方法。
案例:我有三個Activity,主介面Activity,個人中心Activity,登入介面Activity,主介面打開了個人中心頁面,個人中心頁面有個退出登入的功能,點選後退出登入關閉主介面Activity和當前Activity,開啟登入頁面.
這只是個簡單的案例,當然關閉當前Activity和開啟登入頁面的Activity都是沒什麼難度的東西。
我們來討論主介面關閉的一些實現方案。
得到主介面Activity的物件。這邊可能需要某個靜態方法來持有這個物件,以前見過一種寫法是寫一個ActivityManager來持有這些Activity物件,然後手動管理這些Activity。
傳送一個廣播通知主介面Activity.
在當前Activity銷燬時傳一個resultCode,主介面看到這個resultCode來判斷是否是否要做關閉操作。
暫時能想到的就這三種實現方案,分析一下三種實現:
第三種實現方案並不適用於比如我現在是主介面–>個人主頁–>個人中心,做第三種方案的話程式碼寫到哭暈在廁所,並且第三種方案程式碼寫起來會很複雜,頁面可能還沒關掉就把自己繞暈了(個人想法,不推薦!)
第二種實現方案:傳送一個廣播做通知,其實是一個蠻不錯的選擇,傳送廣播然後實現廣播類做回撥通知。但是如果現在需求是對一個Fragment做通知。這個時候廣播就派不上用場了。畢竟廣播只能再Activity中註冊傳送廣播。
第一種實現方案:這種方案在以前還是蠻常見的,以前學Android的時候會看到一些大牛們,自己寫一個ActivityManager用來存放Activity物件做生命週期管理。但是這樣做其實是會有問題的。因為持有的是Activity的物件,這就導致了Activity只能手動銷燬。每次寫關閉頁面的時候都要呼叫這個方法來銷燬Activity,不然分分鐘就OOM了。還有另外一個缺點就是需要遍歷整個棧來確定存活的Activity物件。找到你要找的Activity,然後呼叫其中的方法。如果是要銷燬則還要將它移除棧中。(心很累)
想想這些方法真的都好難用呀。
這個時候就需要來個上帝來管理這些東西了。EventBus一臉害羞的跳出來了。一種全域性的觀察者模式,你可以把你的願望(Event)請求post給這位上帝,它提供多執行緒的處理方案,來讓你的(願望)在你指定的地方實現。
EventBus使用
在Github上可以看到gradle所需配置
compile 'org.greenrobot:eventbus:3.0.0'
在gradle檔案做上如上配置就可以了
分三個步驟
- 定義一個作為通知的實體類Event;
- 在需要訂閱的地方做註冊和取消註冊,寫一個訂閱方法(方法需要寫註解 @Subscribe(threadMode = ThreadMode.XXX))
- 在任意地方呼叫EventBus.getDefault().post(Event());
簡單程式碼如下:
在MainActivity註冊和解除註冊,並寫出訂閱方法
package com.example.cncn.myapplication;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//註冊一個EventBus
EventBus.getDefault().register(this);
findViewById(R.id.tvClickView).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(MainActivity.this, UserInfoActivity.class));
}
});
}
@Subscribe
public void onMainEvent(Event event) {
//列印物件的地址資訊
Log.d("MainActivity", "EventInfo" + event.toString() + "_" + event.eventInfo);
finish();
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.d("MainActivity","我被關掉了");
//如果已經註冊,千萬別忘了取消註冊
if (EventBus.getDefault().isRegistered(this))
EventBus.getDefault().unregister(this);
}
}
在UserInfoActivity傳送通知
package com.example.cncn.myapplication;
import android.content.Intent;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import org.greenrobot.eventbus.EventBus;
/**
* Created by xiaolong on 2017/3/24.
* Email:[email protected]
*/
public class UserInfoActivity extends AppCompatActivity {
private TextView tvClickView;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvClickView = (TextView) findViewById(R.id.tvClickView);
tvClickView.setText("點我退出登入!");
tvClickView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(UserInfoActivity.this, LoginActivity.class));
Event event = new Event(110, "關閉頁面吧!!");
Log.d("UserInfoActivity", event.toString());
EventBus.getDefault().post(event);
finish();
}
});
}
}
LoginActivity
package com.example.cncn.myapplication;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.TextView;
/**
* Created by xiaolong on 2017/3/24.
* Email:[email protected]
*/
public class LoginActivity extends AppCompatActivity {
private TextView tvClickView;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvClickView = (TextView) findViewById(R.id.tvClickView);
tvClickView.setText("點我登入!");
tvClickView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(LoginActivity.this,MainActivity.class));
}
});
}
}
實體類
package com.example.cncn.myapplication;
/**
* Created by xiaolong on 2017/3/24.
* Email:[email protected]
*/
public class Event {
public int eventCode;
public String eventInfo;
public Event(int eventCode, String eventInfo) {
this.eventCode = eventCode;
this.eventInfo = eventInfo;
}
}
執行結果
如此簡單便捷的使用方式,確實是做APP層訊息通知的不二選擇。不過在註冊過EventBus的類中,千萬別忘了在結束的時候取消註冊。這麼好用的東西還是需要去了解一下其內部實現的。而且EventBus內部實現其實並不複雜。
EventBus原始碼解析
註冊EventBus的時候需要執行,EventBus.getDefault().register(this);
我們先從getDefault方法入手,看下里面做了些什麼
/** 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物件,保證所有的EventBus物件是同一個。
進入 EventBus構造看一下
/**
* Creates a new EventBus instance; each instance is a separate scope in which events are delivered. To use a
* central bus, consider {@link #getDefault()}.
*/
public EventBus() {
this(DEFAULT_BUILDER);
}
EventBus(EventBusBuilder builder) {
subscriptionsByEventType = new HashMap<>();
typesBySubscriber = new HashMap<>();
stickyEvents = new ConcurrentHashMap<>();
mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
backgroundPoster = new BackgroundPoster(this);
asyncPoster = new AsyncPoster(this);
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;
eventInheritance = builder.eventInheritance;
executorService = builder.executorService;
}
兩個構造方法,一個是無參的public的方法,另外一個是內部方法,帶引數的。
第一個方法預設呼叫有引數的構造方法,來生成一個EventBus物件。
這邊DEFAULT_BUILDER宣告如下
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
存放著一個預設的EventBusBuilder物件;
進入EventBusBuilder中看
public class EventBusBuilder {
private final static ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool();
boolean logSubscriberExceptions = true;
boolean logNoSubscriberMessages = true;
boolean sendSubscriberExceptionEvent = true;
boolean sendNoSubscriberEvent = true;
boolean throwSubscriberException;
boolean eventInheritance = true;
boolean ignoreGeneratedIndex;
boolean strictMethodVerification;
ExecutorService executorService = DEFAULT_EXECUTOR_SERVICE;
List<Class<?>> skipMethodVerificationForClasses;
List<SubscriberInfoIndex> subscriberInfoIndexes;
EventBusBuilder() {
}
public EventBusBuilder logSubscriberExceptions(boolean logSubscriberExceptions) {
this.logSubscriberExceptions = logSubscriberExceptions;
return this;
}
public EventBusBuilder logNoSubscriberMessages(boolean logNoSubscriberMessages) {
this.logNoSubscriberMessages = logNoSubscriberMessages;
return this;
}
public EventBusBuilder sendSubscriberExceptionEvent(boolean sendSubscriberExceptionEvent) {
this.sendSubscriberExceptionEvent = sendSubscriberExceptionEvent;
return this;
}
public EventBusBuilder sendNoSubscriberEvent(boolean sendNoSubscriberEvent) {
this.sendNoSubscriberEvent = sendNoSubscriberEvent;
return this;
}
public EventBusBuilder throwSubscriberException(boolean throwSubscriberException) {
this.throwSubscriberException = throwSubscriberException;
return this;
}
public EventBusBuilder eventInheritance(boolean eventInheritance) {
this.eventInheritance = eventInheritance;
return this;
}
public EventBusBuilder executorService(ExecutorService executorService) {
this.executorService = executorService;
return this;
}
public EventBusBuilder skipMethodVerificationFor(Class<?> clazz) {
if (skipMethodVerificationForClasses == null) {
skipMethodVerificationForClasses = new ArrayList<>();
}
skipMethodVerificationForClasses.add(clazz);
return this;
}
public EventBusBuilder ignoreGeneratedIndex(boolean ignoreGeneratedIndex) {
this.ignoreGeneratedIndex = ignoreGeneratedIndex;
return this;
}
public EventBusBuilder strictMethodVerification(boolean strictMethodVerification) {
this.strictMethodVerification = strictMethodVerification;
return this;
}
public EventBusBuilder addIndex(SubscriberInfoIndex index) {
if(subscriberInfoIndexes == null) {
subscriberInfoIndexes = new ArrayList<>();
}
subscriberInfoIndexes.add(index);
return this;
}
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;
}
}
public EventBus build() {
return new EventBus(this);
}
}
裡面是一些EventBus的基礎配置。列印異常資訊,傳送未被訂閱的訊息等。
特別需要注意的是 ignoreGeneratedIndex 這個變數是用來確定EventBus用什麼方式來獲取訂閱方法。
true代表的是不優先使用索引,用反射的方式,false代表的是優先使用索引。預設是false。
這邊有個addIndex方法是用來新增訂閱方法索引的,這是3.0版本的新特性。
在EventBus帶引數的建構函式裡面初始化了 SubscriberMethodFinder 這個類,三個引數分別為索引列表,
是否嚴格方法定義,是否無視索引。
getDefault方法總結,該方法返回一個單例的EventBus物件,如果物件沒被建立就建立一個預設的EventBus物件。
接下來從註冊方法下手
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方法找到了一個訂閱者中的所有訂閱方法,返回一個 List,然後之後遍歷所有的訂閱方法,做訂閱操作。進入到findSubscriberMethods看看如何實現的。
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;
}
}
findSubscriberMethods這個方法宣告SubscriberMethodFinder這個類中,類中聲明瞭一個ConcurrentHashMap用來做SubscriberMethod的快取。
回到findSubscriberMethods中,首先是通過類來當key,查詢在當前快取中是否存在這個類的訂閱方法列表。有就直接return 快取中的列表。
沒有就進入下一步,這邊有兩種方式來獲取類中的訂閱方法通過ignoreGeneratedIndex來確定獲取訂閱方法的方式為true時會使用反射方法獲取訂閱者的事件處理函式,為false時會優先使用subscriber Index(訂閱方法索引)生成的SubscriberInfo來獲取訂閱者的事件處理函式(減少反射耗時),預設false。(使用訂閱方法索引需自己構建一個EventBus物件,將在後面提及,目前的使用並不能用到索引)
進入findUsingInfo這個方法
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
FindState findState = prepareFindState();//這個方法用來從物件池中獲取一個FindState物件,如果物件池都沒有可用物件的話新建一個。用來節省建立物件的花銷
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);
}
- 從FindState池——FIND_STATE_POOL中獲取一個FindState物件。(SubscriberMethodFinder這個類中聲明瞭一個FIND_STATE_POOL的物件池,這個用來存放空(並非NULL)的FIND_STATE物件,減少建立物件的花銷)
- 該方法會將findState.clazz域中使用了@subscribe標註、方法中只有一個引數、且方法修飾符為public的方法,建立一個SubscriberMethod物件,並新增到findState的List集合中。
- 將findState.clazz域更新為clazz = clazz.getSuperclass(); 如果該超類名字以java. javax. android.開頭則clazz變成null;不再往上尋找父類;
- 拷貝一份findState的List集合並返回,最後回收findState物件,回收的只是釋放這個物件內部的變數的資源佔用,但這個物件還是存在的,放回物件池中,下次可以再取出來用;
看一下如何用反射獲取訂閱方法。
private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
findUsingReflectionInSingleClass(findState);
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
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");
}
}
}
- 獲取訂閱類宣告的所有方法; 然後對獲取到的方法全部遍歷一遍
- 獲取方法的修飾符:即方法前面的public、private等關鍵字。
- 如果該類方法使用了@subscribe標註、方法中只有一個引數、且方法修飾符為public。findState.checkAdd(method, eventType) 如果之前沒有存在過則返回true
- 判斷@Subscribe標註中的threadMode對應的值,預設模式ThreadMode.POSTING
- 建立一個SubscriberMethod物件,該物件很簡單就是儲存有方法、方法引數型別、執行緒模式、訂閱的優先順序、sticky標誌位。與Retrofit類似只是這裡建立了一個SubscriberMethod物件。並將該物件新增到FindSate的List集合中。
回到註冊方法,來看一下subscribe方法的實現
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class<?> eventType = subscriberMethod.eventType; //note1
Subscription newSubscription = new Subscription(subscriber, subscriberMethod); //note2
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType); //note3
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(); //note4
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); //note5
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
if (subscriberMethod.sticky) { //note6
if (eventInheritance) {
Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
for (Map.Entry<Class<?>, Object> entry : entries) {
Class<?> candidateEventType = entry.getKey();
if (eventType.isAssignableFrom(candidateEventType)) { //從stickyEvents獲取對應的事件交給當前事件訂閱者處理
Object stickyEvent = entry.getValue();
checkPostStickyEventToSubscription(newSubscription, stickyEvent); //該方法底層還是會執行postToSubscription方法
}
}
} else {
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
- 獲取方法引數型別;注意:使用@Subscribe標註的方法有且僅有一個引數
- 利用訂閱者物件及其事件處理方法構建一個Subscription物件,該物件儲存有Object、SubscriberMethod物件
- 從subscriptionsByEventType集合中獲取當前事件對應的Subscription物件集合; 如果得到的集合為空則建立一個這樣的集合,並將剛建立的Subscription物件新增進subscriptionsByEventType集合中;如果得到的集合不為空且剛建立的Subscription物件已經存在該集合中則丟擲異常,即同一個物件不能註冊兩次!
- 將第二步建立的Subscription物件按照優先順序存入Subscription物件集合中,該集合中的元素都是按照優先順序從高到低存放.
- 以subscriber物件為鍵,從typesBySubscriber獲取該物件對應的接收事件型別集合,沒有則建立一個這樣的集合,然後當前事件型別新增到該集合中,最後將整個集合新增進typesBySubscriber集合中;有則直接新增到接收事件型別集合中;
- 該值預設為false,除非在註冊事件方法時使用瞭如下的標註@Subscribe(sticky = true);那麼就會執行到這裡。stickyEvent也是EventBus3.0的一大特點,該類事件一旦傳送給EventBus,那麼EventBus就會將它存入Map
public void post(Object event) {
PostingThreadState postingState = currentPostingThreadState.get(); //note1
List<Object> eventQueue = postingState.eventQueue;
eventQueue.add(event); //note2
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);//note3
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
- 獲取當前執行緒對應的一個PostingThreadState()物件;回顧一下我們在EventBus中建立的如下域
private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
@Override protected PostingThreadState initialValue() { return new PostingThreadState(); } //當前推送執行緒狀態
};
ThreadLocal型別的特點是當呼叫currentPostingThreadState.get()方法的時候,會返回當前執行緒所持有的一個 PostingThreadState物件;在不同的執行緒中執行同樣一行程式碼它們得到的物件是不同的。PostingThreadState也很簡單,就是定義了一堆資料,沒有任何方法。下面就是它的所有原始碼
final static class PostingThreadState {
final List<Object> eventQueue = new ArrayList<Object>(); //待派送的事件佇列
boolean isPosting; //當前PostingThreadState物件是否正在派送事件的標誌位
boolean isMainThread; //當前PostingThreadState物件是否是工作在UI執行緒的標誌位
Subscription subscription; //事件處理器
Object event; //待處理事件
boolean canceled; //是否取消事件派送的標誌位
}
- 向PostingThreadState的事件佇列中新增一個事件
- 從PostingThreadState的事件佇列——eventQueue中移出一個事件,並呼叫postSingleEvent方法進行派送
postSingleEvent方法
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
if (eventInheritance) {
List<Class<?>> eventTypes = lookupAllEventTypes(eventClass); //note2
int countTypes = eventTypes.size();
for (int h = 0; h < countTypes; h++) { //note3
Class<?> clazz = eventTypes.get(h);
subscriptionFound |= postSingleEventForEventType(event, postingState, clazz);
}
} else {
subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
}
if (!subscriptionFound) { //note4
if (logNoSubscriberMessages) {
Log.d(TAG, "No subscribers registered for event " + eventClass);
}
if (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&eventClass != SubscriberExceptionEvent.class) {
post(new NoSubscriberEvent(this, event));
}
}
- eventInheritance該標誌位預設為true,表示只要 滿足訂閱事件是該事件的父類或者實現了該事件同樣介面或介面父類 中的任何一個條件的訂閱者都會來處理該事件。
- 該方法從名字來看就是獲取eventClass的所有父類往上都能追溯到Object、和所有實現的介面、以及介面父類;EventBus進行了優化採用了快取機制Map
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
//第一個是帶處理的原始事件,第三個引數是原始事件的關聯類
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
subscriptions = subscriptionsByEventType.get(eventClass);//note1
}
if (subscriptions != null && !subscriptions.isEmpty()) {
for (Subscription subscription : subscriptions) {
postingState.event = event;
postingState.subscription = subscription;
boolean aborted = false;
try {
postToSubscription(subscription, event, postingState.isMainThread); //note2
aborted = postingState.canceled;
} finally {
postingState.event = null;
postingState.subscription = null;
postingState.canceled = false;
}
if (aborted) {
break;
}
}
return true;
}
return false;
}
- 獲取原始事件的關聯類對應的所有Subscription物件
- 將上述Subscription物件集合進行遍歷,使用postToSubscription方法處理原始事件
postToSubscription方法
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
//第一個引數是事件處理器、第二個引數是待處理事件、第三個為當前執行緒是否是UI執行緒的標誌位
switch (subscription.subscriberMethod.threadMode) {
case POSTING: //note1
invokeSubscriber(subscription, event);
break;
case MAIN: //note2
if (isMainThread) {
invokeSubscriber(subscription, event);
} else {
mainThreadPoster.enqueue(subscription, event);
}
break;
case BACKGROUND: //note3
if (isMainThread) {
backgroundPoster.enqueue(subscription, event);
} else {
invokeSubscriber(subscription, event);
}
break;
case ASYNC: //note4
asyncPoster.enqueue(subscription, event);
break;
default:
throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
}
}
- POSTING直接在當前執行緒執行,呼叫invokeSubscriber方法
- MAIN即UI執行緒執行:如果當前執行緒就是UI執行緒則直接呼叫invokeSubscriber方法,否則將任務交給mainThreadPoster——HandlerPoster(this, Looper.getMainLooper(), 10)物件非同步執行,最終會呼叫invokeSubscriber(PendingPost pendingPost)方法;
- BACKGROUND背景執行緒執行:其實就在非UI執行緒中執行,如果當前執行緒是非UI執行緒則直接呼叫invokeSubscriber方法,否則將任務交給backgroundPoster——BackgroundPoster(this)物件非同步執行,最終會呼叫invokeSubscriber(PendingPost pendingPost)方法;
- ASYNC:將任務交給 asyncPoster—— AsyncPoster(this)物件非同步執行,最終會呼叫invokeSubscriber(PendingPost pendingPost)方法;
- 就是根據不同的Post模式,選擇不同的方式反射執行方法。
接下來看取消註冊的方法
public synchronized void unregister(Object subscriber) {
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);//note1
if (subscribedTypes != null) {
for (Class<?> eventType : subscribedTypes) {
unsubscribeByEventType(subscriber, eventType); //
}
typesBySubscriber.remove(subscriber); //note3
} else {
Log.w(TAG, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
- 從typesBySubscriber獲取該物件接收的事件型別集合;
- 對得到的接收事件型別集合中的每個事件型別呼叫unsubscribeByEventType進行處理;跟著我們就分析該方法
- 該物件從typesBySubscriber集合中移除;
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
List<Subscription> subscriptions = subscriptionsByEventType.get(eventType); //note1
if (subscriptions != null) {
int size = subscriptions.size();
for (int i = 0; i < size; i++) {
Subscription subscription = subscriptions.get(i);
if (subscription.subscriber == subscriber) { //note2
subscription.active = false;
subscriptions.remove(i);
i--;
size--;
}
}
}
}
1、從subscriptionsByEventType集合中獲取該事件型別對應的Subscription集合
2、如果集合中的元素——Subscription的subscriber域等於目標subscriber,則將該Subscription從subscriptionsByEventType集合中移除出去;
原始碼部分就講解到這裡
由於對註解不是很瞭解,所以在通過索引獲取訂閱方法的時候我也是一臉蒙逼,因為索引列表是在建構函式中生成的。
但是addIndex從來沒被呼叫過。我就很好奇索引列表是怎麼來的,後面才明白是另外有方法來新增訂閱者索引列表,下面我們來看一下如何使用索引。
索引的好處
EventBus提供了一個擴充套件包 EventBusAnnotationProcessor 這個方法可以在編譯的時候通過註解,自動生成訂閱方法索引列表的類。由於是在編譯階段就執行的,這樣做可以避免反射獲取所需的耗時。缺點可能是會佔用一小部分記憶體。
如何使用索引
使用索引需要在專案gradle下新增
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
如下
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
mavenCentral()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.0'
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
然後在app的gradle中新增
//apply 需新增
apply plugin: 'com.neenbedankt.android-apt'
//android中需新增
android {
apt{
arguments{
//路徑加類名,用來確定存放的位置,可以自己定義
eventBusIndex "org.greenrobot.eventbus.EventBusTestIndex"
}
}
}
//dependencies需新增
dependencies {
provided 'org.greenrobot:eventbus-annotation-processor:3.0.1'
}
其中eventBusIndex 是路徑加類名,用來確定存放的位置,可以自己定義。
配置完之後先Rebuild一把
可以看到在debug目錄下生成了這個EventBusTestIndex這個類。
類中方法如下
package org.greenrobot.eventbus;
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 EventBusTestIndex implements SubscriberInfoIndex {
private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;
static {
SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();
putIndex(new SimpleSubscriberInfo(com.cncn.tourenforce.base.BaseFuncActivity.class, true,
new SubscriberMethodInfo[] {
new SubscriberMethodInfo("logoutEvent", com.cncn.tourenforce.bean.event.LogoutEvent.class),
}));
putIndex(new SimpleSubscriberInfo(com.cncn.tourenforce.ui.mine.AboutActivity.class, true,
new SubscriberMethodInfo[] {
new SubscriberMethodInfo("downLoadSuccess", com.cncn.tourenforce.bean.event.UpdateVersionEvent.class),
}));
putIndex(new SimpleSubscriberInfo(com.cncn.tourenforce.ui.check.CheckReasonEditActivity.class, true,
new SubscriberMethodInfo[] {
new SubscriberMethodInfo("checkSuccess", com.cncn.tourenforce.bean.event.CheckSuccessEvent.class),
}));
putIndex(new SimpleSubscriberInfo(com.cncn.tourenforce.ui.check.SignListFragment.class, true,
new SubscriberMethodInfo[] {
new SubscriberMethodInfo("resetList", com.cncn.tourenforce.bean.event.SignSelectResetEvent.class),
}));
}
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;
}
}
}
就是一個靜態的Map物件用來存放所有訂閱方法字典。然後提供一個get方法用來獲取這個字典中的物件。
然後就是使用了
EventBus.builder().addIndex(subscriberClass -> new EventBusTestIndex().getSubscriberInfo(subscriberClass)).installDefaultEventBus();
這邊再貼一遍installDefaultEventBus這個方法的原始碼
/**
* 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;
}
}
可以看出這個方法用來自定義一個EventBusBuider物件配置到EventBus中去,並替換掉defaultInstance。
注意:這裡需要在Default EventBus 物件還沒生成的時候執行這段程式碼。如果已經有預設的EventBus物件存在了再installDefaultEventBus就會拋異常。
EvenBus在一些細節上面的異常處理值得借鑑。在開發者使用框架的時候。如果這段話沒拋異常。估計自己都要調半天了。
所以我們需要在APP啟動的時候把這個方法加進去,新建一個App類繼承Application
import android.app.Application;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.EventBusTestIndex;
public class App extends Application {
@Override
public void onCreate() {
super.onCreate();
EventBus.builder().addIndex(subscriberClass -> new EventBusTestIndex().getSubscriberInfo(subscriberClass)).installDefaultEventBus();
}
}
AndroidManifest中別忘了把Application改成當前App
<application
android:name=".base.App"
android:allowBackup="t