Android—EventBus使用与源码分析
EventBus
安卓事件發布/訂閱框架
事件傳遞既可用于Android四大組件間通訊
EventBus的優點是代碼簡潔,使用簡單,并將事件發布和訂閱充分解耦
在onStart進行注冊,onStop進行注銷。
implementation 'org.greenrobot:eventbus:3.1.1'使用:
作為事件的發布者,需要定義所發布的事件的類:
public class MessageEvent{public final String message;public static MessageEvent getInstance(String message) {return new MessageEvent(message);}private MessageEvent(String message) {this.message = message;}}聲明和注釋訂閱方法,選擇指定線程模式
@Subscribe(sticky = true, threadMode = ThreadMode.MAIN)public void onGetMessage(MessageEvent message) {Log.e("....",message.message)}發送消息
btn.setOnClickListener {EventBus.getDefault().post(MessageEvent.getInstance("發送消息"))}Android引入了StickyBroadcast,在廣播發送結束后會保存剛剛發送的廣播(Intent),這樣當接收者注冊完 Receiver后就可以接收到剛才已經發布的廣播,通過注解的方式設置sticky為true,那么事件處理函數則可以處理上一次的事件對象。
事件訂閱者可以通過注解的方式選擇處理事件的方法所在的線程:
訂閱者同時需要在總線上注冊和注銷自己
override fun onStart() {super.onStart()EventBus.getDefault().register(this)}override fun onStop() {super.onStop()EventBus.getDefault().unregister(this)}發送事件
btn.setOnClickListener { EventBus.getDefault().post(MessageEvent.getInstance("發送消息"))}源碼分析
注冊發送消息都要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;}//將DEFAULT_BUILDER傳入EventBus的構造函數public EventBus() {this(DEFAULT_BUILDER);}private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();單例模式,運用到雙重檢查鎖,只有一個defaultInstance。
所以getDefault方法最后是返回一個傳入EventBusBuilder的EventBus對象,采用建造者模式。
我們現看看EvenBus類的注冊register方法
public void register(Object subscriber) { //獲取上下文,我們傳入的注冊者是activityClass<?> subscriberClass = subscriber.getClass(); //根據上下文找訂閱方法List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass); //將注冊者和注冊事件綁定起來synchronized (this) {for (SubscriberMethod subscriberMethod : subscriberMethods) {subscribe(subscriber, subscriberMethod);}}}List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) { //在緩存中查找class對象的訂閱方法列表List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);if (subscriberMethods != null) {return subscriberMethods;}if (ignoreGeneratedIndex) { //通過反射機制得到訂閱者類class對象對應的訂閱事件方法列表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 { //緩存此class對象的訂閱方法列表METHOD_CACHE.put(subscriberClass, subscriberMethods);return subscriberMethods;}}接下來看看subscribe方法
// Must be called in synchronized blockprivate void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {Class<?> eventType = subscriberMethod.eventType; //將class對象和其訂閱方法列表封裝成Subscription對象,這里才是真正的綁定。Subscription newSubscription = new Subscription(subscriber, subscriberMethod);//subscriptions存放多個Subscription對象,意味著存放多個class對象和各個對象對應的訂閱方法。 //且這些class對象的訂閱方法的EventType相同。 //subscriptionsByEventType隊列又存放著多個subscriptions對象。 //通過訂閱方法的eventType屬性得到隊列中的subscriptions對象。CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);if (subscriptions == null) {subscriptions = new CopyOnWriteArrayList<>();subscriptionsByEventType.put(eventType, subscriptions);} else { //如果這個對象已經在subscriptions列表當中,則拋出異常,已經注冊過了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;}}//通過訂閱者class對象得到其對應的訂閱方法的EventType列表。List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);if (subscribedEvents == null) {subscribedEvents = new ArrayList<>();typesBySubscriber.put(subscriber, subscribedEvents);} //添加EventType到class對象對應的EventType列表當中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);}}}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方法,這個方法到后面再分析。
register方法總結:
下面到EvenBus的Post方法
/** Posts the given event to the event bus. */public void post(Object event) { //通過ThreadLocal機制得到當前線程的postingState對象,線程獨有的,不會共享線程數據PostingThreadState postingState = currentPostingThreadState.get(); //獲取事件隊列List<Object> eventQueue = postingState.eventQueue; //在此線程的eventQueue中添加此事件對象eventQueue.add(event);if (!postingState.isPosting) { //判斷當前線程是否UI線程postingState.isMainThread = isMainThread();postingState.isPosting = true;if (postingState.canceled) {throw new EventBusException("Internal error. Abort state was not reset");}try { //遍歷此線程消息隊列,處理消息隊列中的消息事件 //每次執行postSingleEvent()都會從隊列中取出一個事件,這樣不停循環取出事件處理,直到隊列全部取完。 while (!eventQueue.isEmpty()) {postSingleEvent(eventQueue.remove(0), postingState);}} finally {postingState.isPosting = false;postingState.isMainThread = false;}}}ThreadLocal是一個線程內部的數據存儲類,通過它可以在指定的線程中存儲數據,而這段數據是不會與其他線程共享的。其內部原理是通過生成一個它包裹的泛型對象的數組,在不同的線程會有不同的數組索引值,通過這樣就可以做到每個線程通過 get() 方法獲取的時候,取到的只能是自己線程所對應的數據。?
核心方法是postSingleEvent
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {Class<?> eventClass = event.getClass();boolean subscriptionFound = false; //eventInheritance表示一個子類事件能否響應父類的onEvent() 方法if (eventInheritance) { //獲取到eventClass所有父類的集合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));}}}lookupAllEventTypes()它通過循環和遞歸一起用,將一個類的父類,接口,父類的接口,父類接口的父類,全部添加到全局靜態變量集合eventTypes中。之所以用全局靜態變量的好處在于用全局靜態變量只需要將那耗時又復雜的循環+遞歸方法執行一次就夠了,下次只需要通過 key:事件類名來判斷這個事件是否以及執行過 lookupAllEventTypes() 方法。
然后我們繼續往下,看發送方法?postSingleEventForEventType()
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {CopyOnWriteArrayList<Subscription> subscriptions;synchronized (this) { //所有訂閱了eventClass的事件集合subscriptions = subscriptionsByEventType.get(eventClass);}if (subscriptions != null && !subscriptions.isEmpty()) { //回調subscription的響應方法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;}跟注冊的粘性事件一樣,最后同樣是在postToSubscription方法中發送消息。
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 subscriberinvokeSubscriber(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()反射訂閱者類中的以onEvent開頭的方法以執行。
Post方法總結:
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());}}/** Only updates subscriptionsByEventType, not typesBySubscriber! Caller must update typesBySubscriber. */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--;}}}}Unregister方法總結:
- 根據subscriber獲取他的EventType列表。
- 再根據EventType列表,從subscriptionsByEventType列表中獲取subscriber們和他們對應訂閱方法的集合subscriptions。
- 最后remove掉subscriptions集合中的subscriber以達到注冊效果。
總結
以上是生活随笔為你收集整理的Android—EventBus使用与源码分析的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 并行计算的强大
- 下一篇: 10个管理工作时间的小技巧