日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

Android—EventBus使用与源码分析

發布時間:2023/12/18 Android 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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,那么事件處理函數則可以處理上一次的事件對象。

事件訂閱者可以通過注解的方式選擇處理事件的方法所在的線程:

  • POSTING:默認,表示事件處理函數的線程跟發布事件的線程在同一個線程。
  • MAIN:表示事件處理函數的線程在主線程(UI)線程,因此在這里不能進行耗時操作。
  • BACKGROUND:表示事件處理函數的線程在后臺線程,因此不能進行UI操作。如果發布事件的線程是主線程(UI線程),那么事件處理函數將會開啟一個后臺線程,如果果發布事件的線程是在后臺線程,那么事件處理函數就使用該線程。
  • ASYNC:表示無論事件發布的線程是哪一個,事件處理函數始終會新建一個子線程運行,同樣不能進行UI操作。
  • 訂閱者同時需要在總線上注冊和注銷自己

    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方法總結:

  • 遍歷傳入的class對象找到所有onEvent以開頭訂閱方法,然后一起封裝成Subscription對象。
  • 判斷是否注冊過該EventType,有則按照優先級將Subscription對象裝入Subscriptions隊列。
  • 沒有就新建Subscriptions隊列,并把Subscriptions隊列加入到subscriptionsByEventType<EventType,List<訂閱這個EventType的訂閱者>>隊列的Value的List中。
  • 再將訂閱方法的EventType存入到typesBySubscriber<訂閱者,List<訂閱的EventType>>隊列的Value的List中。
  • 最后判斷是否為粘性事件,是則將黏性事件發送到指定的訂閱方法當中并根據threadMode進行相應處理。
  • 下面到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方法總結:

  • ThreadLocal機制獲取當前線程的封裝數據,并從封裝的數據中拿到發送事件的事件隊列。
  • 將要發送的事件加入到事件隊列中去。
  • 循環,每次發送隊列中的一條事件給所有訂閱了這個EventType的訂閱者。
  • 如果是子事件可以響應父事件的事件模式,需要先將這個事件的所有父類、接口、父類的接口、父類接口的父類都找到,并讓訂閱了這些父類信息的訂閱者也都響應這條事件。
  • 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使用与源码分析的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。