簡言:
相信大家都使用過EventBus,用起來真的是奧利給啊,使用簡單,開銷還小,這種異步框架真的是讓人愛不釋手啊,當然了,有一大部分人更加喜歡Rxjava這個框架,畢竟這個框架更NB, 但是今天得主角不是Rxjava,是EventBus,帶大家看看他的內部實現是什麼?當然在講源碼之前,還是先介紹一下EventBus,我保證,只是介紹啊, 畢竟我也不想囉嗦。。。
1.簡介
EvenBus是一個android端優化的消息總線,簡化了應用程序內各組件,組件與後臺線程間的通信,比如請求網絡,等網絡返回時通過Handler或者BroadCast通知UI,兩個Fragement之間需要通過listener通信,這些需求都可以通過Evenbus實現;
evenBus是一款針對 android 優化的發佈/訂閱事件總線。主要功能是代替Intent handler Broacaset 在fragement activity
service線程之間的傳遞消息,優點是開銷小,代碼更優雅,以及將發送者和接收者解耦.
2.基本的使用:
1,自定義一個類,可以是空類;
2.在要接收消息的頁面註冊
3.發送消息
4.接收消息的頁面實現(共有四個函數,各功能不同)
5.解除註冊
3.EventBus的流程 (如圖:官網找的)
publisher是一個發佈器,然後將我們的事件Event通過post()方法發送到EventBus的主線當中,然後在這個EventBus的主線中,會通過事件(Event)類型匹配相應的訂閱者 Subscriber。
4.源碼分析(EventBus)
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//發送消息
EventBus.getDefault().post(new MyEvents("hello eventbus"));
}
});
}
@Override
protected void onStart() {
super.onStart();
//註冊EventBus
EventBus.getDefault().register(this);
}
@Override
protected void onStop() {
super.onStop();
//註銷EventBus
EventBus.getDefault().unregister(this);
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MyEvents events){
Toast.makeText(this,events.message,Toast.LENGTH_SHORT).show();
}
這個是Event的使用類,我們看在使用EventBus時,無論註冊還是註銷發送消息,都有getDefault()這個方法,所以我們先看看這個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;
}
public static EventBusBuilder builder() {
return new EventBusBuilder();
}
/** For unit test primarily. */
public static void clearCaches() {
SubscriberMethodFinder.clearCaches();
eventTypesCache.clear();
}
/**
* 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的構造函數,DEFAULT_BUILDER,我們跟進去看一下:
我們可以看出,這個EventBus最終是構建者模式來創建對象的。我們看看他是如何創建的,
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;
}
subscriptionsByEventType = new HashMap<>();
typesBySubscriber = new HashMap<>();
stickyEvents = new ConcurrentHashMap<>();
這三個HashMap,都是做什麼?
第一個:它是以Event爲key,subscript爲value,當發送Event時,都可以通過這個HashMap找到對應訂閱者。
第二個:它是以Subscriber爲key,event爲value,當我們做反註冊的時候都會操作這個hashMap
第三個:這個是維護的粘性事件,我之前講過粘性事件的定義,這裏就不追溯了。
mainThreadPoster = new HandlerPoster(this, Looper.getMainLooper(), 10);
backgroundPoster = new BackgroundPoster(this);
asyncPoster = new AsyncPoster(this);
我們在看看這三個post(重要)
1)HandlerPoster:我們看參數,傳遞的是主線程的Looper,是handler現實的,我們追進去看看handerPoster做了什麼?
final class HandlerPoster extends Handler {
private final PendingPostQueue queue;
private final int maxMillisInsideHandleMessage;
private final EventBus eventBus;
private boolean handlerActive;
HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) {
super(looper);
this.eventBus = eventBus;
this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage;
queue = new PendingPostQueue();
}
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;
}
}
}
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;
}
}
}
這個HandlerPoser繼承Handler,
PendingPostQueue queue; 這是一個隊列
int maxMillisInsideHandleMessage; post這個事件在hanlder中最大的時間值
EventBus eventBus;
handlerActive; 他標識的是是否運行起來了
我們關注一下handleMessage方法做了什麼?
通過do,while這個循環,從隊列中獲取數據,並調用eventBus,invokeSubscriber()分發事件,每分發完一次事件,就對比一下時間,判斷這個時間,與上邊定義的最大時間值,做比較,如果大於就跳出循環。
2)backgroundPoster (處理後臺操作的)
final class BackgroundPoster implements Runnable {
private final PendingPostQueue queue;
private final EventBus eventBus;
private volatile boolean executorRunning;
BackgroundPoster(EventBus eventBus) {
this.eventBus = eventBus;
queue = new PendingPostQueue();
}
public void enqueue(Subscription subscription, Object event) {
PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
synchronized (this) {
queue.enqueue(pendingPost);
if (!executorRunning) {
executorRunning = true;
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;
}
}
}
eventBus.invokeSubscriber(pendingPost);
}
} catch (InterruptedException e) {
Log.w("Event", Thread.currentThread().getName() + " was interruppted", e);
}
} finally {
executorRunning = false;
}
}
}
他實現的是Runnalbe,我們直接看他的run()方法:run()主要是不斷的從我們隊列中獲取消息,然後通過invokeSubscruber進行事件分發。直到取完爲止。
3)asyncPoster
class AsyncPoster implements Runnable {
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);
}
}
它也是實現的Runnable,
他的run方法,是獲取隊列中的一個,進行事件分發,與上邊的post是不同的,
上邊三個post是EventBus中最核心的類,
3.subscriberMethodFinder(這個也是重要的,他是註解的找尋器)
二,關於註解的分析
@Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MyEvents events){
Toast.makeText(this,events.message,Toast.LENGTH_SHORT).show();
}
我們還是看一下@Subscribe的源碼:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
ThreadMode threadMode() default ThreadMode.POSTING;
/**
* If true, delivers the most recent sticky event (posted with
* {@link EventBus#postSticky(Object)}) to this subscriber (if event available).
*/
boolean sticky() default false;
/** Subscriber priority to influence the order of event delivery.
* Within the same delivery thread ({@link ThreadMode}), higher priority subscribers will receive events before
* others with a lower priority. The default priority is 0. Note: the priority does *NOT* affect the order of
* delivery among subscribers with different {@link ThreadMode}s! */
int priority() default 0;
}
ThreadMode 線程模式。這個是非常重要的,我們分析一下:
public enum ThreadMode {
POSTING,
MAIN,
BACKGROUND,
ASYNC
}
1)POSTING:一種默認線程模式,表示在執行post事件操作的時候線程直接調訂閱者的方法,無論該線程是否在主線程。
2)MAIN:表示在主線程中執行這個方法,
3)BACKGROUND:在後臺線程中執行相應的方法。
4)ASYNC:無論發佈的是否在主線程。它都會發佈一個空線程進行處理。它線程獨立,不會出現卡頓。
2.sticky: 這是定義的粘性事件,
3.priority: 優先級,默認情況下是POSTING.
三,register() 註冊訂閱
public void register(Object subscriber) {
Class<?> subscriberClass = subscriber.getClass();
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
第一行代碼:通過反射獲取到我們的subscriberClass對象,
第二行代碼:通過我們獲取到的對象找到對應的集合。subscriberMethodFinder這個找尋器進行尋找。
我們看一下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;
}
}
我們看一下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 {
findUsingReflectionInSingleClass(findState);
}
findState.moveToSuperclass();
}
return getMethodsAndRelease(findState);
}
while這個循環,通過getSubScriberInfo這個方法返回的不爲null,
首先獲取訂閱方法的集合,通過for循序遍歷我們的訂閱方法,並通過checkAdd這個方法進行過濾,將符合的添加到subscriberMethods這個集合中。
如果etSubScriberInfo這個方法返回的爲null,會走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");
}
}
}
methods = findState.clazz.getDeclaredMethods(); 通過反射做的,我們猜的沒錯,這裏就獲取到所有訂閱者的方法,
然後對獲取的方法進行了依次的遍歷,通過getParameterTpyes方法獲取到我們的參數,通過getAnnotation這個方法獲取到我們的Subscribe對象,這裏其實主要做的就是過濾出被Subscribe修飾過的方法,subscribeAnnotation.threadMode,獲取線程模式,通過這個線程模式進行調度,
2.我們繼續分析register 中的subscribe
// Must be called in synchronized block
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);
}
}
}
Subscription 這個類是做什麼的?我們看一下:
final class Subscription {
final Object subscriber;
final SubscriberMethod subscriberMethod;
/**
* Becomes false as soon as {@link EventBus#unregister(Object)} is called, which is checked by queued event delivery
* {@link EventBus#invokeSubscriber(PendingPost)} to prevent race conditions.
*/
volatile boolean active;
Subscription(Object subscriber, SubscriberMethod subscriberMethod) {
this.subscriber = subscriber;
this.subscriberMethod = subscriberMethod;
active = true;
}
@Override
public boolean equals(Object other) {
if (other instanceof Subscription) {
Subscription otherSubscription = (Subscription) other;
return subscriber == otherSubscription.subscriber
&& subscriberMethod.equals(otherSubscription.subscriberMethod);
} else {
return false;
}
}
@Override
public int hashCode() {
return subscriber.hashCode() + subscriberMethod.methodString.hashCode();
}
}
這裏處理了訂閱者,和封裝的一些訂閱方法,線程模式,等,
我們回去繼續看:
subscriptions這個爲null.證明這個事件還沒有註冊過,我們就新創建一個CopyOnWriteArrayList,並添加到SubScriptByEventType這個hashMap當中,
subscriptions爲null的話就會拋出異常,證明這個時間已經註冊過了。
subscriptions.size回去這個集合大小,
通過for循環遍歷,通過優先級的條件添加到subscriptions中。
這裏對粘性事件也做了處理,這裏不講粘性事件,
最後 checkPostStickyEventToSubscription(newSubscription, stickyEvent);完成了註冊。
總結:subscribe
1.判斷是否有註冊過該事件
2.然後按照優先級加入到SubscriptionByEventType的value的list中
3.然後再添加到typesBySubscriber的value的List中。
4.分發事件