在app
下的build.gradle
的dependencies
中進行引入,固然高版本也容易出現問題。java
implementation 'org.greenrobot:eventbus:3.2.0'
複製代碼
使用三步驟:設計模式
(1) 定義事件緩存
public class MessageEvent {
public final String message;
public MessageEvent(String message) {
this.message = message;
}
}
複製代碼
(2)定義註冊和註銷app
// 當對應的消息發送到時會被調用的方法
// 根據對應的實體類來對應處理的方式
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {
Toast.makeText(getActivity(), event.message, Toast.LENGTH_SHORT).show();
}
@Subscribe
public void handleSomethingElse(SomeOtherEvent event) {
doSomethingWith(event);
}
// 生命週期start中註冊
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
// 生命週期stop中註銷
@Override
public void onStop() {
EventBus.getDefault().unregister(this);
super.onStop();
}
複製代碼
(3)發送消息框架
EventBus.getDefault().post(new MessageEvent("Hello everyone!"));
複製代碼
每次在看源碼以前,咱們都須要乾的事情是知道這個東西要幹什麼。 EventBus
做爲消息的發佈/訂閱總線框架,最終基於的仍是一個觀察者模式。而方法將是咱們對源碼的整個切入點,因此咱們要知道他能幹什麼,才知道怎麼去讀源碼。異步
(1)Subscribe註解的使用ide
(2)註冊和註銷函數
(3)發送和處理事件post
(4)粘性事件學習
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
// 線程的模式,默認爲POSTING
ThreadMode threadMode() default ThreadMode.POSTING;
// 是否堅持粘性事件,默認不支持
boolean sticky() default false;
// 一個優先級標識,默認爲0
int priority() default 0;
}
複製代碼
關於上述中的線程的模式也是大有文章。
public enum ThreadMode {
// 在當前的線程中直接處理。
POSTING,
// 在主線程中發送事件,則直接處理,可是要求處理量小;在子線程中,就經過Handler發送後,再由主線程處理
MAIN,
// 老是入隊列進行等待,經過Handler發送事件後,再由主線程處理
MAIN_ORDERED,
// 在主線程中發送事件,入隊列依次處理;在子線程中發送事件,則直接處理。
BACKGROUND,
// 老是入隊列等待,經過線程池異步處理。
ASYNC
}
複製代碼
EventBus.getDefault().register(this);
EventBus.getDefault().unregister(this);
複製代碼
通常來講麻煩的都是註冊的過程,因此咱們反其道而行之,咱們先看註銷。
public synchronized void unregister(Object subscriber) {
// 得到context對應的訂閱事件的參數類型集合
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
if (subscribedTypes != null) {
// 循環遍歷,刪去對應的數據
for (Class<?> eventType : subscribedTypes) {
unsubscribeByEventType(subscriber, eventType);
}
// 本質就是一個map,而context就是一個key,刪去key罷了。
typesBySubscriber.remove(subscriber);
} else {
logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
複製代碼
上面講過了註銷,當前就是重頭戲的註冊了。 在整個的代碼中,其實我尚未介紹getDefault()
這個方法,因此先看看這個方法。
static volatile EventBus defaultInstance;
public static EventBus getDefault() {
EventBus instance = defaultInstance;
if (instance == null) {
synchronized (EventBus.class) {
instance = EventBus.defaultInstance;
if (instance == null) {
instance = EventBus.defaultInstance = new EventBus();
}
}
}
return instance;
}
複製代碼
DCL不清楚的讀者能夠看看聊一聊設計模式(二)-- 建立型設計模式
很輕鬆,就是一個DCL的方式來建立一個單例,那麼這個時候咱們也就擁有了EventBus
的一個實例了,天然就能註冊了。
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
// 其實就是尋找到帶有@Subscribe註解的方法集合
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
// 同步方式完成訂閱事件的綁定
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
複製代碼
從上述的代碼中,咱們知道他的大體流程其實就是分爲兩步,查詢和註冊。
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
// 先是從緩存中取,有則直接返回,無則尋找
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
// 由於咱們通常使用的都是getDefault()來進行一個註冊,因此ignoreGeneratedIndex默認爲false。去查看EventBusBuilder便可,會看到未賦值,也就是默認值的false
if (ignoreGeneratedIndex) {
subscriberMethods = findUsingReflection(subscriberClass);
} else {
// 默認調用的方法
subscriberMethods = findUsingInfo(subscriberClass); // 1
}
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;
}
}
複製代碼
這個工做流程是很是清晰的,咱們的下一步就是要進入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 {
// 通常進入這個方法,經過反射的方式判斷方法使用是否知足一些基本條件
// 是不是public修飾,是否爲一個參數,是否爲靜態,是否爲抽象類
findUsingReflectionInSingleClass(findState);
}
// 對subscriberClass的父類再進行查詢
findState.moveToSuperclass();
}
// 對遍歷得到的方法進行重構
// 對findState進行回收
return getMethodsAndRelease(findState);
}
複製代碼
到此爲止咱們就可以得到咱們打過註解的方法們了。
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class<?> eventType = subscriberMethod.eventType;
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
// 若是subscriptionsByEventType中並不存在對應的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;
}
}
// 以context做爲key,來存儲訂閱事件
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
// 對粘性事件的一個具體操做
if (subscriberMethod.sticky) {
// 。。。。。。
}
}
複製代碼
這麼一大串看下來,想來不少人很跳過,畢竟代碼真的太多了。其實仍是同樣抽主幹,它針對的變量只有兩個typesBySubscriber
、subscriptionsByEventType
。爲何這麼說的?就是一個反推的思想,註冊中要保存的東西,在註銷的時候是必定要刪除的,那咱們去看看註銷就知道了。
EventBus.getDefault().post(new MessageEvent("MessageEvent"));
複製代碼
這裏惟一的須要瞭解的就是post()
這個函數。
public void post(Object event) {
// 一個PostingThreadState 類型的ThreadLocal
// PostingThreadState是存儲了事件隊列,線程模式等等信息的類
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集合
subscriptions = subscriptionsByEventType.get(eventClass);
}
if (subscriptions != null && !subscriptions.isEmpty()) {
for (Subscription subscription : subscriptions) {
postingState.event = event;
postingState.subscription = subscription;
boolean aborted;
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()
方法,最後斷定就是咱們最開始說的threadMode
,根據設置來給出相對應的處理方式。
這裏只講解一種,由於其他的終究仍是基於Handler
來進行通訊,因此只挑默認方法的POSTING
講解。
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
case POSTING:
invokeSubscriber(subscription, event);
break;
// 。。。。。
}
}
複製代碼
這是發送部分的未講解部分,其實已經和處理部分重合,因此就一塊兒講解。 咱們說過POSTING
方法是直接在當前的線程做出處理的。 查看源碼也可以發現是不對isMainThread
這個變量進行斷定的。
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);
}
}
複製代碼
顯然最後也就是調用了一個Method.invoke()
方法來對咱們的方法進行處理,這裏也就直接對應了咱們的相對應的定義的方法了。
EventBus.getDefault().postSticky(new MessageEvent("MessageEvent"));
複製代碼
經過postSticky()
方法來進行一個調用
public void postSticky(Object event) {
synchronized (stickyEvents) {
stickyEvents.put(event.getClass(), event);
}
// 用鎖機制存放後再發送,防止直接消費
post(event);
}
複製代碼
最後調用的仍是一個post()
方法,可是這裏咱們須要想起的是咱們以前還沒有分析的subscribe()
中針對粘性事件作出處理的方法。(能夠回到註冊中進行查看)
if (subscriberMethod.sticky) {
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)) {
Object stickyEvent = entry.getValue();
checkPostStickyEventToSubscription(newSubscription, stickyEvent); // 1
}
}
} else {
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent); // 1
}
}
// 不管是if仍是else最終都會調用到的註釋1方法
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());
}
}
複製代碼
其實粘性事件的最終和發送事件異曲同工,都是須要調用postToSubscription()
來將方法進行一個發送,只是,他多作了一個緩存的地方, 也就是stickyEvents
這個變量拿來對粘性事件進行一個存儲操做。
圖片轉載自SheHuan大佬的文章
register | post | unregister |
---|---|---|
以上就是個人學習成果,若是有什麼我沒有思考到的地方或是文章內存在錯誤,歡迎與我分享。
相關文章推薦: