一、基本用法
- 添加依賴和注解處理器
android {
defaultConfig {
javaCompileOptions {
annotationProcessorOptions {
arguments = [eventBusIndex: 'org.greenrobot.eventbusperf.EventBusIndex']
}
}
}
}
dependencies {
implementation 'org.greenrobot:eventbus:3.1.1'
annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.0.1'
}
- 在Application添加注解處理器自動生成的類
EventBus.builder().addIndex(new EventBusIndex()).installDefaultEventBus();
- 注冊為事件觀察者和聲明接收事件的方法靴跛,注意@Subscribe注解的方法要為public,不然編譯報錯
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
@Override
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMsg(String str) {
Log.d(TAG, "onEventMsg: " + str);
}
- 發(fā)送事件
EventBus.getDefault().post("發(fā)送事件");
二、擴展
看看@Subscribe 注解的定義
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
ThreadMode threadMode() default ThreadMode.POSTING;
boolean sticky() default false;
int priority() default 0;
}
可以看到使用該注解時我們有3個變量可以配置
@Subscribe(threadMode = ThreadMode.MAIN, priority = -2, sticky = true)
public void onEventMsg(String str) {
Log.d(TAG, "onEventMsg: " + str);
}
//發(fā)送粘性事件
EventBus.getDefault().postSticky("發(fā)送事件");
- threadMode:用來聲明接收事件的方法運行的線程,有5種取值
- sticky:是否接收粘性事件
- priority:事件優(yōu)先級,priority越大優(yōu)先級越高
ThreadMode有5種不同的取值:
- POSTING:發(fā)送事件在哪個線程處理事件就在哪個線程
- MAIN:處理事件在主線程
- MAIN_ORDERED:和MAIN的區(qū)別就是即使發(fā)送事件是在主線程,也通過Handler發(fā)送消息來執(zhí)行處理事件的方法滨彻,可以保證non-blocking
- BACKGROUND:如果發(fā)送事件是子線程梁厉,那么處理事件就在該子線程,如果發(fā)送事件是主線程,那么處理事件在線程池的子線程,值得注意的是淑玫,所有事件都在同一子線程中執(zhí)行
- ASYNC:無論發(fā)送事件在哪個線程侮穿,處理事件都在線程池的子線程
三、源碼分析
一、注解處理器生成類
編譯階段注解處理器會掃描@Subscribe注解巾乳,生成.java文件仆抵,我們需要在Application初始化時創(chuàng)建這些自動生成的類的對象娱两,添加到EventBus對象中去。
生成的.java文件在build/generated/source/apt路徑下,文件名為我們在gradle聲明的名稱,下面看看生成的文件內(nèi)容
public class EventBusIndex implements SubscriberInfoIndex {
private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;
static {
SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();
putIndex(new SimpleSubscriberInfo(com.yjx.myapplication.ScrollingActivity.class, true,
new SubscriberMethodInfo[] {
new SubscriberMethodInfo("onEventMsg", String.class, ThreadMode.MAIN),
new SubscriberMethodInfo("onEventMsg2", String.class, ThreadMode.MAIN),
}));
}
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;
}
}
}
可以看到EventBusIndex類維護了一個HashMap虎忌,key為訂閱者挑围,value為訂閱者接收事件的方法信息捶朵。接下去看下在Application的操作:
EventBus.builder().addIndex(new EventBusIndex()).installDefaultEventBus();
public EventBusBuilder addIndex(SubscriberInfoIndex index) {
if (subscriberInfoIndexes == null) {
subscriberInfoIndexes = new ArrayList<>();
}
subscriberInfoIndexes.add(index);
return this;
}
比較簡單并淋,不多說兔毙,主要是通過構(gòu)建者模式創(chuàng)建一個EventBus單例對象哑姚,將生成的類的對象添加到subscriberInfoIndexes這個集合绞佩。
二肘交、注冊為觀察者和反注冊
直接看register方法
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
// 關(guān)鍵方法1 查找該觀察者所聲明的所有接受事件的方法沿侈,即@Subscribe注解的方法
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
// 關(guān)鍵方法1
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
// 先從緩存找
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
// 3.0以上ignoreGeneratedIndex的值默認為false
if (ignoreGeneratedIndex) {
// 通過反射獲取接收事件的方法的信息
subscriberMethods = findUsingReflection(subscriberClass);
} else {
// 通過注解器生成的EventBusIndex類中獲得該訂閱者的接收事件的方法的信息
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這個方法可以看到篡腌,有兩種方式獲取該訂閱者的接收事件的方法的信息杨伙,一種是通過反射限匣,一種是使用注解處理器生成的類米死。還有一個小細節(jié)脯丝,如果最后沒找到@Subscribe注解的方法,則會拋異常闸溃,所以,一旦你調(diào)用register方法妆距,你就應(yīng)該聲明接收事件的方法。
先看看從注解處理器尋找的操作
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
// FindState 此類可以理解為一個保存當前尋找狀態(tài)的類
// 因為在尋找訂閱者接收事件的方法時,不僅在當前類,還在其父類找
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
// 關(guān)鍵方法1
findState.subscriberInfo = getSubscriberInfo(findState);
if (findState.subscriberInfo != null) {
// 關(guān)鍵方法2
SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();
for (SubscriberMethod subscriberMethod : array) {
if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
// 將接收事件的方法添加到findState對象的List<SubscriberMethod>
findState.subscriberMethods.add(subscriberMethod);
}
}
} else {
findUsingReflectionInSingleClass(findState);
}
// 指向父類动分,重新執(zhí)行相同的操作
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
// 關(guān)鍵方法1
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) {
// 遍歷我們在Application初始化時添加的EventBusIndex對象
for (SubscriberInfoIndex index : subscriberInfoIndexes) {
// 調(diào)用注解處理器幫我們生成的方法拿到接收事件方法的信息
SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
if (info != null) {
return info;
}
}
}
return null;
}
// 關(guān)鍵方法2蝶防,根據(jù)方法信息構(gòu)造SubscriberMethod數(shù)組
@Override
public synchronized SubscriberMethod[] getSubscriberMethods() {
int length = methodInfos.length;
SubscriberMethod[] methods = new SubscriberMethod[length];
for (int i = 0; i < length; i++) {
SubscriberMethodInfo info = methodInfos[i];
methods[i] = createSubscriberMethod(info.methodName, info.eventType, info.threadMode,
info.priority, info.sticky);
}
return methods;
}
再看看通過反射的方式是怎么獲取接收事件的方法信息
private List<SubscriberMethod> findUsingReflection(Class<?> subscriberClass) {
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
while (findState.clazz != null) {
findUsingReflectionInSingleClass(findState);
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
主要是反射獲取類里面的public的方法,獲取方法的注解牧牢,將方法的信息保存到SubscriberMethod對象。
我們再回到register方法焚鲜,findSubscriberMethods不管通過哪種方式掌唾,最終得到都是一個List<SubscriberMethod>集合,下面繼續(xù)看
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
// 關(guān)鍵方法1 查找該觀察者所聲明的所有接受事件的方法忿磅,即@Subscribe注解的方法
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
// 關(guān)鍵方法2
subscribe(subscriber, subscriberMethod);
}
}
}
// 關(guān)鍵方法2
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
Class<?> eventType = subscriberMethod.eventType;
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
// 1.獲取訂閱同一事件類型的所有@Subscribe的方法
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();
// 2.根據(jù)優(yōu)先級的順序插入到列表里
for (int i = 0; i <= size; i++) {
if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
subscriptions.add(i, newSubscription);
break;
}
}
// 3.獲取該訂閱者的接收事件的集合糯彬,將當前接收事件方法添加到集合中
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
// 4.是否是粘性事件
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);
}
}
} else {
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
}
大致的流程引用EventBus 3.0 源碼分析的圖片,非常清晰
接下來看反注冊unregister方法
public synchronized void unregister(Object subscriber) {
// 獲取該訂閱者的接收事件的集合
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
if (subscribedTypes != null) {
for (Class<?> eventType : subscribedTypes) {
// 從訂閱同一事件類型的列表中移除該訂閱者
unsubscribeByEventType(subscriber, eventType);
}
// 移除該訂閱者
typesBySubscriber.remove(subscriber);
} else {
logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
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) {
subscription.active = false;
subscriptions.remove(i);
i--;
size--;
}
}
}
}
三葱她、發(fā)送事件
public void post(Object event) {
// 1.通過ThreadLocal獲取當前線程的PostingThreadState對象情连,該對象記錄著當前線程發(fā)送事件的狀態(tài)
PostingThreadState postingState = currentPostingThreadState.get();
// 2.將事件添加到事件隊列里
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()) {
// 3.循環(huán)取出隊列里的時間進行處理
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
post方法主要做的事情就是上面標注的3步,下面重點看看postSingleEvent方法
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
// 是否要匹配父類和接口览效,默認為true
if (eventInheritance) {
// 1.尋找當前時間類型的父類和接口却舀,例如你發(fā)送String類型,那么接收Object類型的方法也可以接收該事件
List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
int countTypes = eventTypes.size();
for (int h = 0; h < countTypes; h++) {
Class<?> clazz = eventTypes.get(h);
// 2. 具體發(fā)送事件的方法
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) {
// 1.獲取訂閱該事件類型的訂閱者
subscriptions = subscriptionsByEventType.get(eventClass);
}
if (subscriptions != null && !subscriptions.isEmpty()) {
for (Subscription subscription : subscriptions) {
postingState.event = event;
postingState.subscription = subscription;
boolean aborted = false;
try {
// 2.根據(jù)treadmode發(fā)送事件锤灿,subscription保存的是接收事件的方法信息
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;
}
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
switch (subscription.subscriberMethod.threadMode) {
case POSTING:
// 直接反射調(diào)用接收事件的方法挽拔,即不切換線程,發(fā)送事件和處理事件在同一線程
invokeSubscriber(subscription, event);
break;
case MAIN:
if (isMainThread) {
// 如果發(fā)送事件是在主線程但校,那么不用切換線程
invokeSubscriber(subscription, event);
} else {
// 通過Handler發(fā)送消息在主線程中處理
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) {
// 如果是發(fā)送事件在主線程螃诅,那么在線程池的子線程調(diào)用接收事件的方法
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);
}
}
我們來看看幾個Poster,Poster是一個interface,里面只有一個enqueue方法术裸,它有3個實現(xiàn)類倘是,分別是HandlerPoster、BackgroundPoster袭艺、AsyncPoster搀崭。下面分別看看這3個類
public class HandlerPoster extends Handler implements Poster {
// 接收事件的方法隊列
private final PendingPostQueue queue;
public 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;
}
}
}
// 反射調(diào)用處理事件的方法
eventBus.invokeSubscriber(pendingPost);
...
}
} finally {
handlerActive = rescheduled;
}
}
}
HandlerPoster其實就是一個Handler,里面維護了一個隊列猾编,一個PendingPost就代表一個接收事件的方法瘤睹,隊列里保存的是所有接收事件的方法。當調(diào)用enqueue方法時答倡,就往隊列里面添加一個PendingPost轰传,然后發(fā)送消息到主線程,處理消息
final class BackgroundPoster implements Runnable, Poster {
private final PendingPostQueue queue;
public void enqueue(Subscription subscription, Object event) {
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
synchronized (this) {
queue.enqueue(pendingPost);
if (!executorRunning) {
executorRunning = true;
// 線程池執(zhí)行
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;
}
}
}
// 反射調(diào)用處理事件方法
eventBus.invokeSubscriber(pendingPost);
}
} catch (InterruptedException e) {
...
}
} finally {
executorRunning = false;
}
}
}
BackgroundPoster是一個Runnable瘪撇,里面也維護了一個隊列获茬,尤其需要注意的是,隊列里的每一個方法只會在線程池里的同一個線程執(zhí)行倔既,不會同時開啟多個子線程锦茁。主要由executorRunning 這個標志位來控制,這一點和下面的AsyncPoster不同叉存。
class AsyncPoster implements Runnable, Poster {
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也是一個Runnable码俩,里面也維護了一個隊列,它每enqueue一個PendingPost時歼捏,都會使用一個新的空閑線程稿存。
post發(fā)送事件的流程大致的過程就分析完了,最后我們看看postSticky發(fā)送粘性事件
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);
}
其實就多了把事件放進一個保存粘性事件的Map這一步瞳秽,接下去還是調(diào)用post方法瓣履。在上面分析register過程中我們知道,如果是接收事件的方法的sticky為true练俐,會做以下的操作
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);
}
}
} else {
Object stickyEvent = stickyEvents.get(eventType);
checkPostStickyEventToSubscription(newSubscription, stickyEvent);
}
}
private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
if (stickyEvent != null) {
postToSubscription(newSubscription, stickyEvent, isMainThread());
}
}
@Subscribe注解的sticky如果為true袖迎,那么在register時,就會取出粘性事件調(diào)用發(fā)送事件的方法腺晾,即postToSubscription