EventBus 是一款在 Android 開(kāi)發(fā)中使用的發(fā)布/訂閱事件總線框架渴析,基于觀察者模式谷扣,將事件的接收者和發(fā)送者分離渣蜗,避免復(fù)雜且容易出錯(cuò)的依賴關(guān)系和生命周期問(wèn)題,簡(jiǎn)化了組件之間的通信棺耍,使用簡(jiǎn)單、效率高种樱、體積忻膳邸!下邊是官方的 EventBus 原理圖:
一呀舔、使用EventBus
EventBus支持訂閱者方法在不同于發(fā)布事件所在線程的線程中被調(diào)用肢执。你可以使用線程模式來(lái)指定調(diào)用訂閱者方法的線程赃绊。這就需要了解EventBus支持的五種線程模式(ThreadMode):POSTING、MAIN以现、MAIN_ORDERED、BACKGROUND、ASYNC邑遏。
1.線程模式
- POSTING
POSTING是默認(rèn)線程模式佣赖,在哪個(gè)線程發(fā)送事件就在對(duì)應(yīng)線程處理事件,避免了線程切換无宿,效率高茵汰。因此,對(duì)于不要求是主線程并且耗時(shí)很短的簡(jiǎn)單任務(wù)推薦使用該模式孽鸡。在線程模型為POSTING的事件處理函數(shù)中盡量避免執(zhí)行耗時(shí)操作蹂午,因?yàn)樗鼤?huì)阻塞事件的傳遞,甚至有可能會(huì)引起ANR彬碱。 - MAIN
訂閱者方法將在主線程(UI線程)中被調(diào)用豆胸。因此,可以在該模式的訂閱者方法中直接更新UI界面巷疼。事件處理的時(shí)間不能太長(zhǎng)晚胡,長(zhǎng)了會(huì)導(dǎo)致ANR。 - MAIN_ORDERED
訂閱者在主線程中調(diào)用嚼沿。該事件總是排隊(duì)等待以后交付給訂閱者估盘,因此對(duì)post的調(diào)用將立即返回。這為事件處理提供了更嚴(yán)格且更一致的順序骡尽,例如遣妥,在具有MAIN線程模式的事件處理程序中發(fā)布另一個(gè)事件,則第二個(gè)事件處理程序?qū)⒃诘谝粋€(gè)事件處理程序之前完成攀细,事件處理的時(shí)間不能太長(zhǎng)箫踩,長(zhǎng)了會(huì)導(dǎo)致ANR。 - BACKGROUND
訂閱者將在后臺(tái)線程中調(diào)用谭贪。如果發(fā)布線程不是主線程境钟,則將在發(fā)布線程中直接調(diào)用事件處理程序方法。如果發(fā)布線程是主線程俭识,則EventBus使用單個(gè)后臺(tái)線程慨削,該線程將按順序傳遞其所有事件。在此事件處理函數(shù)中禁止進(jìn)行UI更新操作套媚。 - ASYNC
無(wú)論事件在哪個(gè)線程中發(fā)布缚态,該事件處理函數(shù)都會(huì)在新建的子線程中執(zhí)行;同樣凑阶,此事件處理函數(shù)中禁止進(jìn)行UI更新操作猿规。
2.基本使用
EventBus的使用分4步
(1)定義事件
public static class MessageEvent {... }
(2)準(zhǔn)備訂閱者(指定模式)
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {...};
(3)注冊(cè)/注銷訂閱者(注冊(cè)和注銷是成對(duì)出現(xiàn)的)
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Override
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}
(4)發(fā)送事件
EventBus.getDefault().post(new MessageEvent());
3.使用示例
前面已經(jīng)介紹了基本用法,下面看下使用示例:
首先配置gradle宙橱,如下所示:
implementation 'org.greenrobot:eventbus:3.1.1'
(1)定義消息事件類
public class MessageEvent {
private String message;
public MessageEvent(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
(2)訂閱者處理事件
@Subscribe(threadMode = ThreadMode.MAIN)
public void onShowEventMessage(MessageEvent messageEvent){
//將數(shù)據(jù)顯示到頁(yè)面上
tvMessage.setText(messageEvent.getMessage());
}
我們?cè)贛ainActivity中創(chuàng)建一個(gè)訂閱者處理事件onShowEventMessage方法姨俩,添加Subscribe注解蘸拔,ThreadMode設(shè)置為MAIN,事件的處理會(huì)在UI線程中執(zhí)行环葵,用TextView來(lái)展示接收到的消息调窍。
(3)注冊(cè)和取消訂閱事件
public class MainActivity extends AppCompatActivity {
private Button btnJump;
private Button btnReg;
private TextView tvMessage;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnJump = findViewById(R.id.btn_jump);
btnReg = findViewById(R.id.btn_reg);
tvMessage = findViewById(R.id.tv_message);
btnJump.setOnClickListener(v -> {
//跳轉(zhuǎn)到SecondActivity
startActivity(new Intent(MainActivity.this,SecondActivity.class));
});
btnReg.setOnClickListener(v -> {
//注冊(cè)事件
EventBus.getDefault().register(this);
});
}
@Override
protected void onDestroy() {
super.onDestroy();
//注銷事件
EventBus.getDefault().unregister(this);
}
}
我們?cè)贛ainAtivity中點(diǎn)擊按鈕注冊(cè)事件,在onDestroy中注銷事件张遭。當(dāng)我們注冊(cè)事件后邓萨,跳轉(zhuǎn)到SecondActivity中去發(fā)布事件。
(4)發(fā)布者發(fā)布事件
public class SecondActivity extends AppCompatActivity {
private Button btnSend;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
btnSend = findViewById(R.id.btn_send);
btnSend.setOnClickListener(v -> {
//發(fā)送事件
EventBus.getDefault().post(new MessageEvent("我要學(xué)習(xí)EventBus"));
finish();
});
}
}
在SecondActivity中點(diǎn)擊按鈕發(fā)布事件后菊卷,返回到MainActivity中缔恳,可以看到頁(yè)面上顯示的就是我們發(fā)布的消息“我要學(xué)習(xí)EventBus”。在這里就不展示頁(yè)面了洁闰。
EventBus的粘性事件
除了普通事件歉甚,EventBus還支持發(fā)送粘性事件,粘性事件就是在發(fā)送事件之后再訂閱該事件也能收到該事件扑眉,這跟粘性廣播類似纸泄。
(1)訂閱者處理粘性事件
@Subscribe(threadMode = ThreadMode.POSTING, sticky = true)
public void onShowStickyEventMessage(MessageEvent messageEvent){
//顯示粘性事件
tvMessage.setText(messageEvent.getMessage());
}
在MainActivity中新寫一個(gè)方法用來(lái)處理粘性事件,可以看到和普通事件基本類似腰素,在注解中多加了一個(gè)sticky = true屬性聘裁。我們先不注冊(cè)事件,直接跳轉(zhuǎn)到MainActivity中弓千,回來(lái)后在點(diǎn)擊注冊(cè)事件衡便,看看效果。
(2)發(fā)送粘性事件
btnSend.setOnClickListener(v -> {
//發(fā)送事件
EventBus.getDefault().postSticky(new MessageEvent("我要學(xué)習(xí)EventBus的粘性事件"));
finish();
});
我們?cè)赟econdActivity中點(diǎn)擊按鈕發(fā)送粘性事件计呈,我們可以看到普通事件是post()方法砰诵,而粘性事件是postSticky()方法征唬,這點(diǎn)需要注意捌显。再者,我們發(fā)送后总寒,回到MainActivity中點(diǎn)擊注冊(cè)事件扶歪,我們就可以看到發(fā)送的事件了。
三摄闸、源碼解析EventBus
下面我們看下EventBus的源碼
1.注冊(cè)訂閱者
1.1EventBus構(gòu)造方法
EventBus.getDefault().register(this);
我們?cè)谑褂肊ventBus時(shí)善镰,首先會(huì)調(diào)用EventBus.getDefault()來(lái)獲取實(shí)例,其中g(shù)etDefault()是一個(gè)單例方法年枕,保證當(dāng)前只有一個(gè)EventBus實(shí)例:
public static EventBus getDefault() {
if (defaultInstance == null) {
synchronized (EventBus.class) {
if (defaultInstance == null) {
defaultInstance = new EventBus();
}
}
}
return defaultInstance;
}
這個(gè)采用了雙重檢查模式的單例模式炫欺,下面看下EventBus的構(gòu)造方法做了什么事情:
public EventBus() {
this(DEFAULT_BUILDER);
}
this調(diào)用了EventBus的另一個(gè)構(gòu)造方法。而DEFAULT_BUILDER是默認(rèn)的EventBusBuilder 熏兄,用來(lái)構(gòu)造EventBus品洛。
private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();
又創(chuàng)建EventBusBuilder對(duì)象树姨,構(gòu)造一個(gè)EventBusBuilder來(lái)對(duì)EventBus進(jìn)行配置。
EventBus(EventBusBuilder builder) {
logger = builder.getLogger();
subscriptionsByEventType = new HashMap<>();
typesBySubscriber = new HashMap<>();
stickyEvents = new ConcurrentHashMap<>();
//主線程初始化操作
mainThreadSupport = builder.getMainThreadSupport();
mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
//子線程初始化操作
backgroundPoster = new BackgroundPoster(this);
asyncPoster = new AsyncPoster(this);
//得到訂閱信息的數(shù)量
indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
builder.strictMethodVerification, builder.ignoreGeneratedIndex);
logSubscriberExceptions = builder.logSubscriberExceptions;
logNoSubscriberMessages = builder.logNoSubscriberMessages;
//訂閱者發(fā)送事件異常
sendSubscriberExceptionEvent = builder.sendSubscriberExceptionEvent;
//訂閱者沒(méi)有發(fā)送事件
sendNoSubscriberEvent = builder.sendNoSubscriberEvent;
//訂閱者拋出異常
throwSubscriberException = builder.throwSubscriberException;
eventInheritance = builder.eventInheritance;
//訂閱者拋出異常
executorService = builder.executorService;
}
這里采用了建造者模式桥状。當(dāng)然我們也可以通過(guò)配置EventBusBuilder來(lái)更改EventBus的屬性帽揪,如下方式也可以注冊(cè):
EventBus.builder()
.eventInheritance(false)
.logSubscriberExceptions(false)
.build()
.register(this);
下面我們看下register方法
public void register(Object subscriber) {
//獲取類 通過(guò)debug可以看出獲取出來(lái)的結(jié)果是com.monkey.eventbus.MainActivity
Class<?> subscriberClass = subscriber.getClass();
//找出傳進(jìn)來(lái)的訂閱者的所有訂閱方法
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
//進(jìn)行同步操作
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
可以看到register()方法主要分為查找和注冊(cè)兩部分。
1.2查找訂閱者的訂閱方法
我們先看下SubscriberMethod都包含了什么:
public class SubscriberMethod {
final Method method; //方法
final ThreadMode threadMode;//執(zhí)行線程
final Class<?> eventType;//接收的事件類型
final int priority;//優(yōu)先級(jí)
final boolean sticky;//是否粘性事件
/** Used for efficient comparison */
String methodString;
public SubscriberMethod(Method method, Class<?> eventType, ThreadMode threadMode, int priority, boolean sticky) {
this.method = method;
this.threadMode = threadMode;
this.eventType = eventType;
this.priority = priority;
this.sticky = sticky;
}
...
}
我們可以看到SubscriberMethod 類中主要保存了訂閱方法的Method對(duì)象辅斟,線程模式转晰、事件類型、優(yōu)先級(jí)士飒、是否是粘性事件
下面來(lái)看查找的過(guò)程查邢,findSubscriberMethods()方法:
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
//從緩存中查找是否有訂閱方法,返回一個(gè)集合
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
//如果緩存中存在訂閱方法,則直接返回
if (subscriberMethods != null) {
return subscriberMethods;
}
//根據(jù) ignoreGeneratedIndex屬性值來(lái)選擇采用何種方法查找訂閱方法
//ignoreGeneratedIndex默認(rèn)為false酵幕,是否忽略注解器生成的
if (ignoreGeneratedIndex) {
subscriberMethods = findUsingReflection(subscriberClass);
} else {
//會(huì)通過(guò)此方法進(jìn)行查找
subscriberMethods = findUsingInfo(subscriberClass);
}
if (subscriberMethods.isEmpty()) {
throw new EventBusException("Subscriber " + subscriberClass
+ " and its super classes have no public methods with the @Subscribe annotation");
} else {
//找到訂閱方法的集合后侠坎,放入緩存,以免下次繼續(xù)查找
METHOD_CACHE.put(subscriberClass, subscriberMethods);
return subscriberMethods;
}
}
findSubscriberMethods()流程很清晰裙盾,即先從緩存中查找实胸,如果找到則直接返回,否則去做下一步的查找過(guò)程番官,然后緩存查找到的集合庐完。我們?cè)陧?xiàng)目中經(jīng)常通過(guò)EventBus單例模式來(lái)獲取默認(rèn)的EventBus對(duì)象,也就是ignoreGeneratedIndex為false的情況徘熔,這種情況會(huì)調(diào)用findUsingInfo()方法:
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
//FindState其實(shí)就是一個(gè)里面保存了訂閱者和訂閱方法信息的一個(gè)實(shí)體類门躯,包括訂閱類中所有訂閱的事件類型和所有的訂閱方法等。
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
//循環(huán)判斷
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 {
// 通過(guò)反射查找訂閱事件的方法
findUsingReflectionInSingleClass(findState);
}
findState.moveToSuperclass();
}
//獲取方法并進(jìn)行釋放
return getMethodsAndRelease(findState);
}
findUsingInfo()方法會(huì)在當(dāng)前要注冊(cè)的類以及其父類中查找訂閱事件的方法酷师,最后再通過(guò)getMethodsAndRelease方法對(duì)findState做回收處理并返回訂閱方法的List集合讶凉。具體的查找過(guò)程在findUsingReflectionInSingleClass()方法,它主要通過(guò)反射查找訂閱事件的方法:
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;
}
//循環(huán)遍歷當(dāng)前類的方法山孔,篩選出符合條件的
for (Method method : methods) {
int modifiers = method.getModifiers();
if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
// 獲得當(dāng)前方法所有參數(shù)的類型
Class<?>[] parameterTypes = method.getParameterTypes();
//保證只有一個(gè)事件參數(shù)
if (parameterTypes.length == 1) {
//得到注解
Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
// 如果當(dāng)前方法使用了Subscribe注解
if (subscribeAnnotation != null) {
// 得到該參數(shù)的類型
Class<?> eventType = parameterTypes[0];
// checkAdd()方法用來(lái)判斷FindState的anyMethodByEventType map是否已經(jīng)添加過(guò)以當(dāng)前eventType為key的鍵值對(duì)懂讯,沒(méi)添加過(guò)則返回true
if (findState.checkAdd(method, eventType)) {
// 得到Subscribe注解的threadMode屬性值,即線程模式
ThreadMode threadMode = subscribeAnnotation.threadMode();
// 創(chuàng)建一個(gè)SubscriberMethod對(duì)象台颠,并添加到subscriberMethods集合
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");
}
}
}
查找的過(guò)程褐望,就是對(duì)當(dāng)前類中的方法進(jìn)行遍歷,找到符合條件的方法(即添加了Subscribe注解)串前,獲取相關(guān)屬性值瘫里,添加到SubscriberMethod對(duì)象,將符合條件的對(duì)象添加到subscriberMethods集合中荡碾,即保存到findState中谨读。
1.3訂閱者的注冊(cè)過(guò)程
在查找完訂閱者的訂閱方法后,就要對(duì)其進(jìn)行注冊(cè)坛吁。調(diào)用subscribe()方法進(jìn)行注冊(cè)
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
// 得到當(dāng)前訂閱了事件的方法的參數(shù)類型
Class<?> eventType = subscriberMethod.eventType;
// Subscription類保存了要注冊(cè)的類對(duì)象以及當(dāng)前的subscriberMethod
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
// subscriptionsByEventType是一個(gè)HashMap劳殖,保存了以eventType為key,Subscription對(duì)象集合為value的鍵值對(duì)
// 先查找subscriptionsByEventType是否存在以當(dāng)前eventType為key的值
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions == null) {
// 如果不存在贼邓,則創(chuàng)建一個(gè)subscriptions,并保存到subscriptionsByEventType
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) {
// 添加上邊創(chuàng)建的newSubscription對(duì)象到subscriptions中
subscriptions.add(i, newSubscription);
break;
}
}
// typesBySubscriber也是一個(gè)HashMap闷尿,保存了以當(dāng)前要注冊(cè)類的對(duì)象為key塑径,注冊(cè)類中訂閱事件的方法的參數(shù)類型的集合為value的鍵值對(duì)
// 查找是否存在對(duì)應(yīng)的參數(shù)類型集合
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
// 不存在則創(chuàng)建一個(gè)subscribedEvents,并保存到typesBySubscriber
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
// 保存當(dāng)前訂閱了事件的方法的參數(shù)類型
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>).
// stickyEvents就是發(fā)送粘性事件時(shí)填具,保存了事件類型和對(duì)應(yīng)事件
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);
}
}
}
subscribe()方法需要兩個(gè)參數(shù)统舀,第一個(gè)是訂閱者,第二個(gè)是訂閱者方法劳景,然后將符合條件的訂閱者信息保存到subscriptionsByEventType誉简、typesBySubscriber兩個(gè)HashMap中,當(dāng)我們?cè)诎l(fā)送事件的時(shí)候要用到subscriptionsByEventType盟广,完成事件的處理闷串,當(dāng)取消 EventBus 注冊(cè)的時(shí)候要用到typesBySubscriber、subscriptionsByEventType筋量,完成相關(guān)資源的釋放烹吵。
處理粘性事件就是在 EventBus 注冊(cè)時(shí),遍歷stickyEvents桨武,如果當(dāng)前要注冊(cè)的事件訂閱方法是粘性的肋拔,并且該方法接收的事件類型和stickyEvents中某個(gè)事件類型相同或者是其父類,則取出stickyEvents中對(duì)應(yīng)事件類型的具體事件呀酸,通過(guò)checkPostStickyEventToSubscription()方法做進(jìn)一步處理凉蜂。
private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
if (stickyEvent != null) {
// If the subscriber is trying to abort the event, it will fail (event is not tracked in posting state)
// --> Strange corner case, which we don't take care of here.
postToSubscription(newSubscription, stickyEvent, isMainThread());
}
}
最終還是通過(guò)postToSubscription()方法完成粘性事件的處理,這就是粘性事件的整個(gè)處理流程性誉。
注冊(cè)訂閱者總結(jié):
(1).首先用register()方法注冊(cè)一個(gè)訂閱者
(2).獲取該訂閱者的所有訂閱的方法
(3).根據(jù)該訂閱者的所有訂閱的事件類型窿吩,將訂閱者存入到每個(gè)以 事件類型為key 以所有訂閱者為values的map集合中
(4).然后將訂閱事件添加到以訂閱者為key 以訂閱者所有訂閱事件為values的map集合中
(5).如果是訂閱了粘性事件的訂閱者,從粘性事件緩存區(qū)獲取之前發(fā)送過(guò)的粘性事件错览,響應(yīng)這些粘性事件纫雁。
2.注銷訂閱者
下面看下注銷訂閱者
EventBus.getDefault().unregister(this);
注銷訂閱者是調(diào)用unregister()方法
public synchronized void unregister(Object subscriber) {
// 得到當(dāng)前注冊(cè)類對(duì)象 對(duì)應(yīng)的訂閱事件方法的參數(shù)類型的集合
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
if (subscribedTypes != null) {
// 遍歷參數(shù)類型集合,釋放之前緩存的當(dāng)前類中的Subscription
for (Class<?> eventType : subscribedTypes) {
unsubscribeByEventType(subscriber, eventType);
}
// 刪除以subscriber為key的鍵值對(duì)
typesBySubscriber.remove(subscriber);
} else {
logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
// 得到當(dāng)前參數(shù)類型對(duì)應(yīng)的Subscription集合
List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions != null) {
int size = subscriptions.size();
for (int i = 0; i < size; i++) {
Subscription subscription = subscriptions.get(i);
if (subscription.subscriber == subscriber) {
// 如果當(dāng)前subscription對(duì)象對(duì)應(yīng)的注冊(cè)類對(duì)象和要取消注冊(cè)的注冊(cè)類對(duì)象相同蝗砾,則刪除當(dāng)前subscription對(duì)象
subscription.active = false;
subscriptions.remove(i);
i--;
size--;
}
}
}
}
我們可以看到在unregister()方法中先较,釋放了typesBySubscriber携冤、subscriptionsByEventType中緩存的資源悼粮。
注銷訂閱者總結(jié):
(1).首先通過(guò)unregister方法拿到要取消的訂閱者
(2).得到該訂閱者的所有訂閱事件類型
(3).遍歷事件類型,根據(jù)每個(gè)事件類型獲取到所有的訂閱者集合曾棕,并從集合中刪除該訂閱者
3.發(fā)送及處理事件
當(dāng)我們要發(fā)送普通事件扣猫,就需要用到post()方法,
當(dāng)我們要發(fā)送粘性事件翘地,就需要用到postSticky()方法
//發(fā)送普通事件
EventBus.getDefault().post("我要學(xué)習(xí)EventBus")申尤;
//發(fā)送粘性事件
EventBus.getDefault().postSticky(new MessageEvent("我要學(xué)習(xí)EventBus的粘性事件"));
3.1粘性事件 postSticky()方法
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()方法主要做了兩件事癌幕,先將事件類型和對(duì)應(yīng)事件保存到stickyEvents中,方便后續(xù)使用昧穿;然后執(zhí)行post(event)繼續(xù)發(fā)送事件勺远,這個(gè)post()方法就是之前發(fā)送普通消息的post()方法。所以时鸵,如果在發(fā)送粘性事件前胶逢,已經(jīng)有了對(duì)應(yīng)類型事件的訂閱者,不是非粘性的饰潜,依然可以接收到發(fā)送出的粘性事件初坠。
3.2普通事件 post()方法
public void post(Object event) {
//currentPostingThreadState是一個(gè)PostingThreadState類型的ThreadLocal
// PostingThreadState類保存了事件隊(duì)列和線程模式等信息
PostingThreadState postingState = currentPostingThreadState.get();
List<Object> eventQueue = postingState.eventQueue;
// 將要發(fā)送的事件添加到事件隊(duì)列
eventQueue.add(event);
// isPosting默認(rèn)為false
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()) {
// 發(fā)送單個(gè)事件,并且從事件隊(duì)列移除事件
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
post()方法先將發(fā)送的事件保存的事件隊(duì)列,然后通過(guò)循環(huán)出隊(duì)列彭雾,將事件交給postSingleEvent()方法處理:
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
// eventInheritance默認(rèn)為true碟刺,表示是否向上查找事件的父類
if (eventInheritance) {
// 查找當(dāng)前事件類型的Class,連同當(dāng)前事件類型的Class保存到集合
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));
}
}
}
postSingleEvent()方法中薯酝,根據(jù)eventInheritance屬性半沽,決定是否向上遍歷事件的父類型,然后用postSingleEventForEventType()方法進(jìn)一步處理事件:
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
// 獲取事件類型對(duì)應(yīng)的Subscription集合
subscriptions = subscriptionsByEventType.get(eventClass);
}
if (subscriptions != null && !subscriptions.isEmpty()) {
for (Subscription subscription : subscriptions) {
// 記錄事件
postingState.event = event;
// 記錄對(duì)應(yīng)的subscription
postingState.subscription = subscription;
boolean aborted = false;
try {
// 最終的事件處理
postToSubscription(subscription, event, postingState.isMainThread);
aborted = postingState.canceled;
} finally {
//將postingState置為初始狀態(tài)
postingState.event = null;
postingState.subscription = null;
postingState.canceled = false;
}
if (aborted) {
break;
}
}
return true;
}
return false;
}
遍歷發(fā)送的事件類型對(duì)應(yīng)的Subscription集合吴菠,然后調(diào)用postToSubscription()方法處理事件抄囚。
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
// 判斷訂閱事件方法的線程模式
switch (subscription.subscriberMethod.threadMode) {
case POSTING:
// 默認(rèn)的線程模式,在那個(gè)線程發(fā)送事件就在那個(gè)線程處理事件
invokeSubscriber(subscription, event);
break;
case MAIN:
if (isMainThread) {
// 如果在主線程發(fā)送事件橄务,則直接在主線程通過(guò)反射處理事件
invokeSubscriber(subscription, event);
} else {
// 如果是在子線程發(fā)送事件幔托,則將事件入隊(duì)列,通過(guò)Handler切換到主線程執(zhí)行處理事件
mainThreadPoster.enqueue(subscription, event);
}
break;
case MAIN_ORDERED:
if (mainThreadPoster != null) {
// 無(wú)論在那個(gè)線程發(fā)送事件蜂挪,都先將事件入隊(duì)列重挑,然后通過(guò) Handler 切換到主線程,依次處理事件棠涮。
mainThreadPoster.enqueue(subscription, event);
} else {
// temporary: technically not correct as poster not decoupled from subscriber
invokeSubscriber(subscription, event);
}
break;
case BACKGROUND:
if (isMainThread) {
// 如果在主線程發(fā)送事件谬哀,則先將事件入隊(duì)列,然后通過(guò)線程池依次處理事件
backgroundPoster.enqueue(subscription, event);
} else {
// 如果在子線程發(fā)送事件严肪,則直接在發(fā)送事件的線程通過(guò)反射處理事件
invokeSubscriber(subscription, event);
}
break;
case ASYNC:
// 無(wú)論在那個(gè)線程發(fā)送事件史煎,都將事件入隊(duì)列,然后通過(guò)線程池處理驳糯。
asyncPoster.enqueue(subscription, event);
break;
default:
throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
}
}
可以看到篇梭,postToSubscription()方法就是根據(jù)訂閱事件方法的線程模式、以及發(fā)送事件的線程來(lái)判斷如何處理事件酝枢。
發(fā)送事件總結(jié):
(1).首先獲取當(dāng)前線程的事件隊(duì)列
(2).將要發(fā)送的事件添加到事件隊(duì)列中
(3).根據(jù)發(fā)送事件類型獲取所有的訂閱者
(4).根據(jù)響應(yīng)方法的執(zhí)行模式恬偷,在相應(yīng)線程通過(guò)反射執(zhí)行訂閱者的訂閱方法
4.Subscribe注解
EventBus3.0 開(kāi)始用Subscribe注解配置事件訂閱方法,不再使用方法名了帘睦,如:
@Subscribe(threadMode = ThreadMode.MAIN)
public void onShowEventMessage(MessageEvent messageEvent) {
tvMessage.setText(messageEvent.getMessage());
}
看下Subscribe注解的實(shí)現(xiàn):
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
// 指定事件訂閱方法的線程模式袍患,即在那個(gè)線程執(zhí)行事件訂閱方法處理事件坦康,默認(rèn)為POSTING
ThreadMode threadMode() default ThreadMode.POSTING;
// 是否支持粘性事件,默認(rèn)為false
boolean sticky() default false;
// 指定事件訂閱方法的優(yōu)先級(jí)诡延,默認(rèn)為0滞欠,如果多個(gè)事件訂閱方法可以接收相同事件的,則優(yōu)先級(jí)高的先接收到事件
int priority() default 0;
}
所以在使用Subscribe注解時(shí)可以根據(jù)需求指定threadMode肆良、sticky仑撞、priority三個(gè)屬性。關(guān)于threadMode的屬性妖滔,文章開(kāi)始已經(jīng)介紹了隧哮。
關(guān)于EventBus的源碼就學(xué)習(xí)到這里,如理解有誤座舍,還望指正沮翔!