EventBus這是一個目前被普遍使用的,用於在不一樣的界面(Activity/Fragment)之間傳遞數據的三方庫,其簡單的使用深受廣大開發者喜歡。java
相比起Bundle或者接口回調,EventBus使用起來更加簡單快捷,但有一點須要注意,再使用EventBus的時候,你須要對本身業務中的通知分發有很清晰的瞭解,否則很容易致使分發過多的無用通知,致使性能的消耗.git
本文會對EventBus作一個簡單的介紹github
1.oncreate()中註冊併發
EventBus.getDefault().register(this);
複製代碼
2.onDestroy()銷燬async
EventBus.getDefault().unregister(this);
複製代碼
3.使用@Subscribe
註解方法,用來接收事件通知ide
@Subscribe
public void onMainEvent(String str){
System.out.println("event = "+str);
}
複製代碼
4.發送通知函數
EventBus.getDefault().post("12456");
複製代碼
上述4個步驟就能完成一次簡單的事件分發,這也是EventBus被普遍使用的部分緣由。源碼分析
在深刻源碼以前先解釋幾個主要的成員post
Subscription.class (訂閱信息)性能
final class Subscription {
//訂閱者對象,通常狀況下多爲activity/fragment等
final Object subscriber;
//訂閱者方法對象,主要包括訂閱的
final SubscriberMethod subscriberMethod;
}
複製代碼
SubscriberMethod.class (訂閱者方法對象)
public class SubscriberMethod {
//方法
final Method method;
//線程模式
final ThreadMode threadMode;
//事件類型
final Class<?> eventType;
//優先級
final int priority;
//是不是粘性事件
final boolean sticky;
}
複製代碼
FindState.class (查找狀態,主要用來保存查找過程當中的變量和結果)
static class FindState {
final List<SubscriberMethod> subscriberMethods = new ArrayList<>();
//事件type爲key,method爲value
final Map<Class, Object> anyMethodByEventType = new HashMap<>();
//method爲key,訂閱者class對象爲value
final Map<String, Class> subscriberClassByMethodKey = new HashMap<>();
final StringBuilder methodKeyBuilder = new StringBuilder(128);
}
複製代碼
另外還有幾個比較重要的Map對象
CopyOnWriteArrayList<Subscription>
爲value,從這裏能夠看出EventBus中一個type類型能夠對應不少個訂閱者。METHOD_CACHE
採用的是ConcurrentHashMap()
這個數據模型,對於多併發作了必定的優化。接下來咱們之前看看這個強大的函數庫的內部實現原理。
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
複製代碼
register過程的主要工做
1.獲取訂閱者的類名 (好比MainActivity)
2.經過findSubscriberMethods
方法查找訂閱者的訂閱方法 (@Subscribe註解的而且是Public修飾的)
2.1 查找訂閱方法
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
if (subscriberMethods != null) {
return subscriberMethods;
}
//ignoreGeneratedIndex屬性表示是否忽略註解器生成的MyEventBusIndex,默認狀況下爲false
if (ignoreGeneratedIndex) {
subscriberMethods = findUsingReflection(subscriberClass);
} else {
subscriberMethods = findUsingInfo(subscriberClass);
}
//若是訂閱者類中沒有被 @Subscribe且public聲明的方法就會報異常
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;
}
}
複製代碼
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);
}
複製代碼
其中以線程池的形式獲取FindState對象,並初始化Subscriber訂閱者對象
FindState findState = prepareFindState();
findState.initForSubscriber(subscriberClass);
複製代碼
正常狀況下第一次使用if (findState.subscriberInfo != null)
這個判斷會爲false,接下來進入findUsingReflectionInSingleClass(findState);
流程
另外須要注意的是每一次的循環都會調用findState.moveToSuperclass()
檢索父類的方法,因此對於一個訂閱者來講,子類和父類中的方法都會被檢索到,順序是子類->父類
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");
}
}
}
複製代碼
findState.checkAdd(method, eventType)
校驗是否能夠加入到list中findState.checkAdd(method, eventType)
這裏分兩種狀況
anyMethodByEventType()
中沒有的直接返回trueanyMethodByEventType()
中有的,作2次校驗,此次根據 方法名>參數名進行完整校驗,由於同一個類中同名同參的函數是不存在的,而同名不一樣參的在前一步已經被過濾了,因此這裏確保在一個類中不會重複註冊.
但若是子類重寫父類的方法的話,就會出現相同的methodKey。這時EventBus會作一次驗證,並保留子類的訂閱信息。因爲掃描是由子類向父類的順序,故此時應當保留methodClassOld而忽略methodClass。若是代碼上的註釋 Revert the put
if (methodClassOld == null || methodClassOld.isAssignableFrom(methodClass)) {
// Only add if not already found in a sub class
return true;
} else {
// Revert the put, old class is further down the class hierarchy
subscriberClassByMethodKey.put(methodKey, methodClassOld);
return false;
}
複製代碼
ThreadMode threadMode = subscribeAnnotation.threadMode();
findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
複製代碼
添加到findState中,以一個arrayList保存
接下來回到 getMethodsAndRelease()
方法中,return 一個方法集合 List
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;
}
複製代碼
3.回到開頭的 register()
方法中
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
複製代碼
相對而言註冊流程仍是比較簡單的,主要是讓最開始提到的兩個map(subscriptionsByEventType和typesBySubscriber)裏面填加數據,留着事件分發時候使用。最多加一些優先級/粘性事件的判斷。
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);
}
}
}
複製代碼
一樣先介紹幾個重要的類
PostingThreadState.class (事件分發狀態)
final static class PostingThreadState {
//事件隊列
final List<Object> eventQueue = new ArrayList<>();
//是否正在分發,防止併發狀態下同一個事件發出屢次
boolean isPosting;
//是否在主線程
boolean isMainThread;
Subscription subscription;
Object event;
boolean canceled;
}
複製代碼
ThreadLocal.class (線程數據的存儲類)
在指定線程存儲數據,數據存儲以後,只有在指定線程才能獲取到以前存儲過的數據。
post流程
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;
}
}
}
複製代碼
核心部分只有下面這句,遍歷發送eventQueue隊列中的event,而且移除已經發送的event
postSingleEvent(eventQueue.remove(0), postingState);
複製代碼
postSingleEvent.class
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));
}
}
}
複製代碼
eventInheritance
變量默認狀況下被賦值爲true,其中lookupAllEventTypes
函數會遍歷eventclass,獲得其父類和接口的class類,事件派發的核心部分在postSingleEventForEventType()
postSingleEventForEventType.class
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;
}
複製代碼
這個函數的邏輯也是十分簡單,根據eventClass從subscriptionsByEventType
列表中獲取訂閱者列表,接着遍歷訂閱者列表,以此將event回調。讀了這裏時,發現將eventClass做爲key,而不是event做爲key,估計是由於class對象能追溯到其父類和接口實現吧。
到此,post流程也結束了,比起註冊流程還要簡單。
幾種poster類型
1.mainThreadPoster 建立於HandlerPoster
HandlerPoster.class
主要變量
//隊列
private final PendingPostQueue queue;
//最大存在秒數 一般爲10s,超過則會報錯,這就跟廣播的onReciver回調10s沒處理完成就會報ANR錯誤有些相似
private final int maxMillisInsideHandleMessage;
private final EventBus eventBus;
//標誌是否活動可用的
private boolean handlerActive;
複製代碼
核心邏輯 handleMessage
@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;
}
}
}
eventBus.invokeSubscriber(pendingPost);
long timeInMethod = SystemClock.uptimeMillis() - started;
if (timeInMethod >= maxMillisInsideHandleMessage) {
if (!sendMessage(obtainMessage())) {
throw new EventBusException("Could not send handler message");
}
rescheduled = true;
return;
}
}
} finally {
handlerActive = rescheduled;
}
}
複製代碼
遍歷隊列,執行eventBus.invokeSubscriber(pendingPost)
方法
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);
}
}
複製代碼
**2.BackgroundPoster:**實現 Runnable,會將PendingPostQueue隊列中全部的事件分發出去
**3.AsyncPoster:**一樣實現 Runnable,只會分發一個PendingPostQueue隊列中的事件
postToSubscription.class
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);
}
}
複製代碼
邏輯也很好理解,相同線程時直接invokeSubscriber
反射回調,不一樣線程則發到相同線程去回調。
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());
}
}
複製代碼
主要工做分兩步
另外看過的一篇博文上寫了有關於EventBus的優化操做,記錄下來
hashMap.put()
hashMap.put() 會根據key返回對應的value值
若是put的時候沒有對應的key值,則添加到map中,若是有 先返回後添加
如
map.put("222", "222");
String value2=map.put("222","333");
複製代碼
此時的value2爲"222",而map中 key="222"的value爲"333"
instanceof, isinstance,isAssignableFrom的區別
instanceof運算符 只被用於對象引用變量,檢查左邊的被測試對象 是否是 右邊類或接口的 實例化。若是被測對象是null值,則測試結果老是false。 形象地:自身實例或子類實例 instanceof 自身類 返回true
String s=new String("javaisland");
System.out.println(s instanceof String); //true
複製代碼
Class類的isInstance(Object obj)方法,obj是被測試的對象,若是obj是調用這個方法的class或接口 的實例,則返回true。這個方法是instanceof運算符的動態等價。 形象地:自身類.class.isInstance(自身實例或子類實例) 返回true
String s=new String("javaisland");
System.out.println(String.class.isInstance(s)); //true
複製代碼
Class類的isAssignableFrom(Class cls)方法,若是調用這個方法的class或接口 與 參數cls表示的類或接口相同,或者是參數cls表示的類或接口的父類,則返回true。 形象地:自身類.class.isAssignableFrom(自身類或子類.class) 返回true 例:
System.out.println(ArrayList.class.isAssignableFrom(Object.class)); //false
System.out.println(Object.class.isAssignableFrom(ArrayList.class)); //true
複製代碼