做者:郭孝星java
校對:郭孝星android
關於項目git
Android Open Framework analysis項目主要用來分析Android平臺主流開源框架的源碼與原理實現。github
文章目錄跨域
EventBus是一個Android/Java平臺基於訂閱與發佈的通訊框架,能夠用於Activities, Fragments, Threads, Services等組件的通訊,也能夠用於多線程通訊。網絡
EventBus在應用裏的應用是十分普遍的,那麼除了EventBus這種應用通訊方式外,還有哪些手段呢?🤔多線程
至關於這些方式EventBus的優勢在於使用簡單,事件的訂閱者和發佈者解耦,可是它也有有本身的問題,例如大量Event類的管理,這個咱們後續會說。框架
Event bus for Android and Java that simplifies communication between Activities, Fragments, Threads, Services, etc. Less code, better quality.async
咱們先來看一下EventBus的源碼結構,以下所示:ide
主要包含了兩個部分:
咱們先來一個簡單的Demo,從Demo入手分析事件的訂閱和發佈流程。
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.btn_post_event).setOnClickListener(this);
}
@Override
protected void onStart() {
super.onStart();
// 訂閱事件
EventBus.getDefault().register(this);
}
@Override
protected void onStop() {
super.onStop();
// 取消訂閱s事件
EventBus.getDefault().unregister(this);
}
// 接收事件Event
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEvent(Event event) {
Toast.makeText(this, event.getMessage(), Toast.LENGTH_SHORT).show();
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.btn_post_event:
// 發佈事件Event
EventBus.getDefault().post(new Event("Event Message"));
break;
}
}
}
複製代碼
總體的流程仍是比較簡單的,以下所示:
咱們具體來看一下。
訂閱事件是經過如下方法來完成的:
EventBus.getDefault().register(this);
複製代碼
getDefault()用來獲取EventBus實例,固然你也能夠經過EventBusBuilder本身構建實例。
public class EventBus {
public void register(Object subscriber) {
// 1. 獲取訂閱者的類名。
Class<?> subscriberClass = subscriber.getClass();
// 2. 查找當前訂閱者的全部響應函數。
List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
synchronized (this) {
// 3. 循環每一個事件響應函數
for (SubscriberMethod subscriberMethod : subscriberMethods) {
subscribe(subscriber, subscriberMethod);
}
}
}
}
複製代碼
SubscriberMethod用來描述onEvent()這些方法的信息,包含方法名、線程、Class類型、優先級、是不是粘性事件。
整個函數的調用流程所示:
接着調用subscribe()進行事件註冊,以下所示:
public class EventBus {
// 訂閱者隊列
private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
// 後續準備取消的事件隊列
private final Map<Object, List<Class<?>>> typesBySubscriber;
// 粘性事件隊列
private final Map<Class<?>, Object> stickyEvents;
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
// 事件類型(xxxEvent)
Class<?> eventType = subscriberMethod.eventType;
Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
// 1. 獲取該事件類型的全部訂閱者信息。
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. 按照事件優先級將其插入訂閱者列表中。
for (int i = 0; i <= size; i++) {
if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
subscriptions.add(i, newSubscription);
break;
}
}
// 3. 獲得當前訂閱者訂閱的全部事件隊列,存放在typesBySubscriber中,用於後續取消事件訂閱。
List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
if (subscribedEvents == null) {
subscribedEvents = new ArrayList<>();
typesBySubscriber.put(subscriber, subscribedEvents);
}
subscribedEvents.add(eventType);
// 4. 是不是粘性事件,若是是粘性事件,則從stickyEvents隊列中取出最後一個該類型的事件發送給訂閱者。
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包含了訂閱者subscriber和訂閱函數subscriberMethod兩個信息。
該方法的調用流程以下所示:
發送事件Event是經過如下方法完成的,以下所示:
EventBus.getDefault().post(new Event("Event Message"));
複製代碼
public class EventBus {
public void post(Object event) {
// 1. 獲取當前線程的PostingThreadState對象,該對象包含事件隊列,保存在ThreadLocal中。
PostingThreadState postingState = currentPostingThreadState.get();
List<Object> eventQueue = postingState.eventQueue;
// 2. 將當前事件加入到該線程的事件隊列中。
eventQueue.add(event);
// 3. 判斷事件是否在分發中。若是沒有則遍歷事件隊列進行實際分發。
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()) {
// 4. 進行事件分發。
postSingleEvent(eventQueue.remove(0), postingState);
}
} finally {
postingState.isPosting = false;
postingState.isMainThread = false;
}
}
}
}
複製代碼
PostingThreadState用來描述發送事件的線程的相關狀態信息,包含事件隊列,是不是主線程、訂閱者、事件Event等信息。
而後調用postSingleEvent()進行事件分發。
public class EventBus {
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
Class<?> eventClass = event.getClass();
boolean subscriptionFound = false;
// 1. 若是事件容許繼承,則查找該事件類型的全部父類和接口,依次進行循環。
if (eventInheritance) {
List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
int countTypes = eventTypes.size();
for (int h = 0; h < countTypes; h++) {
Class<?> clazz = eventTypes.get(h);
// 2. 查找該事件的全部訂閱者。
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()方法查詢當前事件的全部訂閱者,以下所示:
public class EventBus {
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
CopyOnWriteArrayList<Subscription> subscriptions;
synchronized (this) {
// 1. 獲取當前事件的全部訂閱者。
subscriptions = subscriptionsByEventType.get(eventClass);
}
if (subscriptions != null && !subscriptions.isEmpty()) {
// 2. 遍歷全部訂閱者。
for (Subscription subscription : subscriptions) {
postingState.event = event;
postingState.subscription = subscription;
boolean aborted = false;
try {
// 3. 根據訂閱者所在線程,調用事件響應函數onEvent()。
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()方法根據訂閱者所在線程,調用事件響應函數onEvent(),這便涉及到接收事件Event的處理了,咱們接着來看。
public class EventBus {
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);
}
}
}
複製代碼
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEvent(Event event) {
Toast.makeText(this, event.getMessage(), Toast.LENGTH_SHORT).show();
}
複製代碼
如上所示,onEvent函數上是能夠加Subscribe註解了,該註解標明瞭onEvent()函數在哪一個線程執行。主要有如下幾個線程:
這裏我線程執行和EventBus的成員變量對應,它們都實現了Runnable與Poster接口,Poster接口定義了事件排隊功能,這些本質上都是個Runnable,放在線程池裏執行,以下所示:
private final Poster mainThreadPoster; private final BackgroundPoster backgroundPoster; private final AsyncPoster asyncPoster; private final SubscriberMethodFinder subscriberMethodFinder; private final ExecutorService executorService;
取消註冊訂閱者調用的是如下方法:
EventBus.getDefault().unregister(this);
複製代碼
具體以下所示:
public class EventBus {
public synchronized void unregister(Object subscriber) {
// 1. 獲取當前訂閱者訂閱的全部事件類型。
List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
if (subscribedTypes != null) {
// 2. 遍歷事件隊列,解除事件註冊。
for (Class<?> eventType : subscribedTypes) {
unsubscribeByEventType(subscriber, eventType);
}
// 3. 移除事件訂閱者。
typesBySubscriber.remove(subscriber);
} else {
logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
}
}
}
複製代碼
取消註冊訂閱者的流程也十分簡單,以下所示:
當猴調用unsubscribeByEventType()移除訂閱者,以下所示:
public class EventBus {
private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
// 1. 獲取全部訂閱者信息。
List<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
if (subscriptions != null) {
// 2. 遍歷訂閱者
int size = subscriptions.size();
for (int i = 0; i < size; i++) {
Subscription subscription = subscriptions.get(i);
// 3. 移除該訂閱對象。
if (subscription.subscriber == subscriber) {
subscription.active = false;
subscriptions.remove(i);
i--;
size--;
}
}
}
}
}
複製代碼
以上即是EventBus核心的實現,仍是比較簡單的。