EventBus3.2詳解和使用(三)

前言: 懶是人天生的惰性,明知道的不足缺,卻不努力彌補。

一、概述

  上兩篇文章對EventBus和使用詳細分析了。瞭解到EventBus是發佈/訂閱者模式,適用於Android和Java的發佈/訂閱事件總線。主要功能是替代Intent、Handler、BroadCast在Activity、Fragment、Service線程之間傳遞消息。它能夠簡化應用組件間的通信,解耦(有效分離)事件的發送者和接收者,避免複雜和容易出錯的依賴和生命週期問題,開銷小,代碼更優雅。
在這裏插入圖片描述
EventBus的官網原理圖我們再看一遍,發佈者通過EventBus發佈事件,訂閱者通過EventBus訂閱事件,當發佈者發送事件時,訂閱該事件的訂閱者的事件處理方法將被調用。從圖中看出,發佈者發送一個事件時,則該事件將會同時傳遞給一個或多個該事件的訂閱者。

二、源碼解析EventBus

2.1 註冊事件

EventBus的使用這裏就不講解了,我們主要講解一下EventBus的源碼的實現。註冊事件的方式如下:

EventBus.getDefault().register(this);

通過EventBus.getDefault()獲取EventBus實例,getDefault()只是一個雙重鎖檢查單例模式,保證全局只有一個實例:

    public static EventBus getDefault() {
        EventBus instance = defaultInstance;
        if (instance == null) {
            synchronized (EventBus.class) {//加鎖
                instance = EventBus.defaultInstance;
                if (instance == null) {
                    instance = EventBus.defaultInstance = new EventBus();
                }
            }
        }
        return instance;
    }

下面看看EventBus的構造方法裏面做了什麼事情:

   public EventBus() {
        this(DEFAULT_BUILDER);
    }

this()表示EventBus的一個構造方法,DEFAULT_BUILDER表示默認構建的EventBusBuilder,用於構造EventBus。

 	private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();

EventBus的構造方法中,通過EventBusBuilder來進行配置:

  EventBus(EventBusBuilder builder) {
        logger = builder.getLogger();
        subscriptionsByEventType = new HashMap<>();
        typesBySubscriber = new HashMap<>();
        stickyEvents = new ConcurrentHashMap<>();
        mainThreadSupport = builder.getMainThreadSupport();//主線程初始化操作
        mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
        backgroundPoster = new BackgroundPoster(this);//子線程發送事件初始化操作
        asyncPoster = new AsyncPoster(this);
        //得到訂閱信息數量
        indexCount = builder.subscriberInfoIndexes != null ? builder.subscriberInfoIndexes.size() : 0;
    }

通過建造者模式EventBusBuilder對EventBus的相關信息配置,當然我們也可以通過EventBusBuilder更改EventBus的配置:

    EventBus.builder()
            .sendSubscriberExceptionEvent(false)
            .sendNoSubscriberEvent(false)
            .build()
            .register(this);

通過build()構建一個當前配置的EventBus實例,register(this)也可以註冊事件。

   public void register(Object subscriber) {
        Class<?> subscriberClass = subscriber.getClass();
        //1.找出當前訂閱者的所有訂閱方法
        List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
        synchronized (this) {//同步
            for (SubscriberMethod subscriberMethod : subscriberMethods) {
                subscribe(subscriber, subscriberMethod);//2.對訂閱者方法註冊
            }
        }
    }

註冊事件裏面只做了兩件事:

  • 找出當前訂閱者的所有訂閱方法;
  • 對訂閱者方法進行註冊。

查找訂閱者方法返回一個SubscriberMethod對象的集合,SubscriberMethod是方法的相關屬性對象:

public class SubscriberMethod {
    final Method method;//方法對象
    final ThreadMode threadMode;//執行線程
    final Class<?> eventType;//接收事件的類型
    final int priority;//事件優先級
    final boolean sticky;//是否爲粘性事件
    /** Used for efficient comparison */
    String methodString;
    }

findSubscriberMethods()開始查找方法:

    List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
    	//METHOD_CACHE是一個ConcurrentHashMap,根據subscriberClass保存了SubscriberMethod,提高保存效率,防止重複查找
    	//從緩存中查找是否有訂閱的方法集合,如果有則直接返回
        List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
        if (subscriberMethods != null) {
            return subscriberMethods;
        }
		//ignoreGeneratedIndex默認爲false,如果使用默認的EventBusBuilder,則忽略註解生成器
        if (ignoreGeneratedIndex) {
            subscriberMethods = findUsingReflection(subscriberClass);
        } else {
            subscriberMethods = findUsingInfo(subscriberClass);
        }     
        METHOD_CACHE.put(subscriberClass, subscriberMethods);//緩存查找到的訂閱事件方法
        return subscriberMethods; 
    }

findSubscriberMethods()首先去緩存中查找,如果找到則直接返回;如果找不到則去下一級查找,找到後緩存起來,如果我們使用默認方式獲取EventBus對象,即ignoreGeneratedIndex = false,那麼來到findUsingInfo()

    private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
    	//FindState是包含訂閱者方法和訂閱者信息的對象,包含了所有訂閱者的方法、事件類型、保存方法的key等信息
        FindState findState = prepareFindState();
        findState.initForSubscriber(subscriberClass);
        //循環判斷
        while (findState.clazz != null) {//初始狀態下的findState.clazz就是subscriberClass
            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();//修改findState.clazz爲subscriberClass的父類class
        }
        //將findState中的SubscriberMethod集合放到一個新的List中並且返回,釋放掉findState
        return getMethodsAndRelease(findState);
    }

findUsingInfo()會在當前要註冊的類以及父類中查找訂閱者方法,FindState類用來輔助查找訂閱事件方法,具體過程在findUsingReflectionInSingleClass()方法中,它通過反射查找訂閱事件方法:

   private void findUsingReflectionInSingleClass(FindState findState) {
        Method[] methods;
    	······
        //遍歷當前類方法,查找到符合條件的
        for (Method method : methods) {
            int modifiers = method.getModifiers();//獲得方法修飾符
            //如果是public,但非abstract、static等
            if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
            	//獲得當前方法所有參數類型
                Class<?>[] parameterTypes = method.getParameterTypes();
                //如果只有一個參數
                if (parameterTypes.length == 1) {
                	//如果方法使用了Subscribe註解
                    Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                    if (subscribeAnnotation != null) {
                    	//得到該參數類型
                        Class<?> eventType = parameterTypes[0];
                        //判斷findState的anyMethodByEventType是否添加過eventType的方法,沒有則返回true
                        if (findState.checkAdd(method, eventType)) {
                        	//獲取線程模式
                            ThreadMode threadMode = subscribeAnnotation.threadMode();
                            //通過實踐類型、線程模式、優先級、是否爲粘性事件創建一個SubscriberMethod,添加到subscriberMethods方法集合中
                            findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
                                    subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
                        }
                    }
                }
            } 
        }
    }

到這裏register()findSubscriberMethods()查找訂閱者事件方法分析完了,就是找到當前註冊類以及父類中訂閱事件的方法的集合,接着來看看register()subscribe()方法:

  private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
  		//獲取當前事件方法的事件類型
        Class<?> eventType = subscriberMethod.eventType;
        //Subscription中保存了要註冊的類對象以及當前的subscriberMethod方法體
        Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
        //subscriptionsByEventType是一個以eventType爲key,Subscription爲value的鍵值對HasMap
        //subscriptionsByEventType是否存在該事件類型的值
        CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
        //如果不存在subscriptions則創建一個subscriptions,並保存到subscriptionsByEventType中
        if (subscriptions == null) {
            subscriptions = new CopyOnWriteArrayList<>();
            subscriptionsByEventType.put(eventType, subscriptions);
        } 

        int size = subscriptions.size();
        //將上面的newSubscription添加到subscriptions中保存
        for (int i = 0; i <= size; i++) {
            if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
                subscriptions.add(i, newSubscription);
                break;
            }
        }
		//typesBySubscriber是一個註冊類對象爲key,該註冊類的事件類型集合爲value的HasMap
        List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
        //如果不存在subscribedEvents則創建一個,並保存到typesBySubscriber中
        if (subscribedEvents == null) {
            subscribedEvents = new ArrayList<>();
            typesBySubscriber.put(subscriber, subscribedEvents);
        }
        //保存當前訂閱事件的事件類型
        subscribedEvents.add(eventType);
		//粘性事件--後面另外分析
        if (subscriberMethod.sticky) {
        ······
        }
    }

這就是註冊的核心流程,所以subscribe()主要是獲得以事件類型爲key,Subscription爲value的subscriptionsByEventType,獲得以註冊類對象爲key,該註冊類的事件類型集合爲value的typesBySubscriber,在發送事件的時候使用subscriptionsByEventType完成事件的處理,在註銷EventBus的時候對subscriptionsByEventTypetypesBySubscriber相關資源釋放。

註冊訂閱者流程:

  • 通過registere()註冊一個訂閱者;
  • 獲取當前訂閱者所有訂閱方法(方法緩存METHOD_CACHE如果有則直接返回,沒有則通過反射和註解@Subscribe註解找到方法名集合並保存到METHOD_CACHE中);
  • 根據訂閱者的訂閱事件類型,將訂閱者存儲到以訂閱者事件類型爲key,所有訂閱者信息爲value的HasMap集合中;
  • 然後將訂閱事件保存到以訂閱者爲key,訂閱者所有訂閱事件類型爲value的HasMap集合中。

2.2 反註冊事件

EventBus通過下面的方法註銷事件:

EventBus.getDefault().unregister(this);

進入unregister()看看:

  public synchronized void unregister(Object subscriber) {
  		//根據當前註冊類對象獲得對應事件類型集合
        List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
        if (subscribedTypes != null) {
        	//遍歷,釋放當前subscriber的Subscription
            for (Class<?> eventType : subscribedTypes) {
                unsubscribeByEventType(subscriber, eventType);
            }
            typesBySubscriber.remove(subscriber);//移除typesBySubscriber中的某個註冊類對象的事件類型集合
        }
    }

註銷給定事件類型的訂閱者,就是釋放不需要用到的訂閱者數據,來到unsubscribeByEventType()

   private void unsubscribeByEventType(Object subscriber, Class<?> eventType) {
   		//得到事件類型的Subscription集合
        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);
                //如果subscription的訂閱者對象類和當前要取消註冊的註冊類對象一致,則移除當前Subscription
                if (subscription.subscriber == subscriber) {
                    subscription.active = false;
                    subscriptions.remove(i);
                    i--;
                    size--;
                }
            }
        }
    }

所以在unregister()方法中就是釋放事件方法集合和事件類型集合的資源。

註銷訂閱者的流程:

  • 通過unregister()得到要註銷的訂閱者;
  • 獲取該訂閱者的所有訂閱事件類型集合;
  • 遍歷事件類型集合,根據事件類型獲取所有訂閱者集合,從集合中刪除該訂閱者;
  • 移除該註冊者對應的事件類型集合的事件類型。

2.3 發送事件

EventBus通過下面的代碼發送普通事件:

EventBus.getDefault().post(Object event);

發送事件就是通過post()方法完成的:

  public void post(Object event) {
  		//currentPostingThreadState是一個PostingThreadState類型的本地線程ThreadLocal<PostingThreadState>
  		//PostingThreadState保存了線程隊列、線程模式、事件方法等信息
        PostingThreadState postingState = currentPostingThreadState.get();
        List<Object> eventQueue = postingState.eventQueue;
        //將當前事件添加到本地線程的事件隊列中
        eventQueue.add(event);
		//isPosting默認爲false
        if (!postingState.isPosting) {
            postingState.isMainThread = isMainThread();//是否爲主線程
            postingState.isPosting = true;
            try {
            	//遍歷事件隊列
                while (!eventQueue.isEmpty()) {
                	//發送單個事件,並且從事件隊列中移除這個事件
                    postSingleEvent(eventQueue.remove(0), postingState);
                }
            } finally {
                postingState.isPosting = false;
                postingState.isMainThread = false;
            }
        }
    }

所以post()方法是先將發送的事件保存到本地線程的事件隊列中,然後循環出隊列,將事件交給postSingleEvent()處理:

  private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
        Class<?> eventClass = event.getClass();
        boolean subscriptionFound = false;//是否找到subscription(subscription包含事件方法的信息)
        if (eventInheritance) {//是否有繼承事件,默認爲true,表示是否向上查找事件的父類
        	//根據當前事件,查找所有事件中當前事件類型的Class集合
            List<Class<?>> eventTypes = lookupAllEventTypes(eventClass);
            int countTypes = eventTypes.size();
            //遍歷Class集合,繼續處理事件
            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 (sendNoSubscriberEvent && eventClass != NoSubscriberEvent.class &&
                    eventClass != SubscriberExceptionEvent.class) {
                post(new NoSubscriberEvent(this, event));
            }
        }
    }

這根據eventInheritance事件繼承性來是否向上根據事件Class來查找所有該Class類型的事件,然後postSingleEventForEventType()進一步處理,如果找不到該事件的處理方法則發送找不到事件的處理:

   private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
        CopyOnWriteArrayList<Subscription> subscriptions;
        synchronized (this) {
        	//獲取該事件類型對應的subscription集合
            subscriptions = subscriptionsByEventType.get(eventClass);
        }
        //如果該事件類型有對應的訂閱事件
        if (subscriptions != null && !subscriptions.isEmpty()) {
        	//遍歷事件方法體集合
            for (Subscription subscription : subscriptions) {
                postingState.event = event;//記錄事件
                postingState.subscription = subscription;//記錄subscription
                boolean aborted;
                try {
                	//最終事件處理
                    postToSubscription(subscription, event, postingState.isMainThread);
                    aborted = postingState.canceled;
                } finally {
                	//最後將相關狀態設置爲初始狀態
                    postingState.event = null;
                    postingState.subscription = null;
                    postingState.canceled = false;
                }
            }
            return true;
        }
        return false;
    }

遍歷發送該事件類型的所有subscription(subscription記錄訂閱事件的相關信息),也就說明發布者發佈事件,能發送給對一個或多個該事件的訂閱者。最後來到了postToSubscription()最終事件處理方法。

發送事件流程:

  • 獲取當前線程事件隊列,將要發送的事件添加到隊列中;
  • 根據訂閱者eventClass向上查找所有的事件類型集合eventTypes
  • 分別獲取事件類型集合中的訂閱者信息集合subscriptions,再在訂閱者信息集合分別根據事件類型eventClass獲取對應的訂閱者信息subscription,最後去處理事件。

2.4 處理事件

處理事件是根據訂閱事件的方法的線程模式,直接或間接通過反射的原理來執行訂閱者的事件處理方法:

   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 {
                	//如果發送事件的線程是子線程,則將事件加入主線程隊列,通過Handler切換到主線程處理事件
                    mainThreadPoster.enqueue(subscription, event);
                }
                break;
            //無論在哪一個線程發佈事件,都先將事件加入主線程隊列,通過Handler切換到主線程依次處理事件
            case MAIN_ORDERED:
                if (mainThreadPoster != null) {
                    mainThreadPoster.enqueue(subscription, event);
                } else {
                    //注意:訂閱者與發佈者沒有關聯在技術上是不正確的
                    invokeSubscriber(subscription, event);
                }
                break;
            case BACKGROUND:
            	//如果發佈事件的線程是主線程,則先將事件加入後臺線程隊列,然後通過線程池依次處理事件
                if (isMainThread) {
                    backgroundPoster.enqueue(subscription, event);
                } else {
                //如果發佈事件的線程是子線程,則直接通過反射處理事件
                    invokeSubscriber(subscription, event);
                }
                break;
            case ASYNC:
            	//無論發佈事件是在哪一個線程,都將事件加入後臺線程隊列,通過線程池依次處理事件
                asyncPoster.enqueue(subscription, event);
                break;
        }
    }

可以看到postToSubscription()訂閱者處理事件的線程模式以及發送事件的線程來判斷如何處理事件,主要有兩種:

  • 直接調用invokeSubscriber(),通過反射處理訂閱事件方法;
  • 將事件加入消息隊列,依次執行處理訂閱事件的方法。

(1)在相應線程直接通過invokeSubscriber()方法,用反射的原理來執行訂閱事件的處理方法:

   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)先將事件和訂閱者封裝成PendingPost加入enqueue()消息隊列,隊列在排隊等待執行,這裏對mainThreadPoster.enqueue(subscription, event)做進一步的分析,其他線程模式的enqueue()原理都是一樣的:

interface Poster {//事件發送
	//爲一個訂閱發佈事件排隊執行
    void enqueue(Subscription subscription, Object event);
}

Poster是一個接口,實現了enqueue()方法,我們來看看mainThreadPoster的內部構造:

mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;

public interface MainThreadSupport {
    Poster createPoster(EventBus eventBus);
    class AndroidHandlerMainThreadSupport implements MainThreadSupport {
        private final Looper looper;
        public AndroidHandlerMainThreadSupport(Looper looper) {
            this.looper = looper;//主線程looper
        }
        @Override
        public boolean isMainThread() {
            return looper == Looper.myLooper();//是否爲主線程
        }
        @Override
        public Poster createPoster(EventBus eventBus) {
            return new HandlerPoster(eventBus, looper, 10);//返回Poster實例
        }
    }
}

mainThreadPoster 由mainThreadSupport.createPoster()創建,所以mainThreadPoster是HandlerPoster的一個實現類,來看看HandlerPoster的內部實現:

public class HandlerPoster extends Handler implements Poster {
	······
    protected HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) {
        super(looper);//主線程looper
        ······
    }

    public void enqueue(Subscription subscription, Object event) {
    	//用subscription和event封裝成一個PendingPost對象
        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
        synchronized (this) {
        	//加入消息隊列
            queue.enqueue(pendingPost);
            if (!handlerActive) {
                handlerActive = true;
                //發送處理事件的消息,handleMessage()被執行,完成從其他線程切換到主線程中
                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();
               
                //eventBus處理事件方法
                eventBus.invokeSubscriber(pendingPost);
            }
        } finally {
            handlerActive = rescheduled;
        }
    }
}

HandlerPoster中enqueue()將PendingPost對象(包含事件方法等信息)保存到隊列中,在handleMessage()中循環遍歷將消息逐個移除並交給eventBusinvokeSubscriber()處理事件:

    void invokeSubscriber(PendingPost pendingPost) {
        Object event = pendingPost.event;
        Subscription subscription = pendingPost.subscription;
        //釋放PendingPost引用的資源
        PendingPost.releasePendingPost(pendingPost);
        if (subscription.active) {//訂閱事件存在
        	//反射來執行訂閱事件處理方法
            invokeSubscriber(subscription, event);
        }
    }

這裏主要就是將PendingPost資源釋放,並且如果事件存活,則通過反射來處理訂閱事件方法。

● mainThreadPoster.enqueue(subscription, event):     核心就是將事件加入線程隊列中,然後通過Handler切換線程處理事件;
● backgroundPoster.enqueue(subscription, event):     原理同上,先將事件加入隊列,然後再移除隊列處理事件,但是會通過線程池做進一步的處理;
● asyncPoster.enqueue(subscription, event):         原理同上;

那麼處理事件到這裏完畢!

2.5 粘性事件

一般情況,我們使用EventBus都是先註冊事件,實現事件方法處理函數,再發送事件,即先註冊,後發佈;但是粘性事件恰恰相反,我們可以先發送事件,然後再註冊、處理事件函數;即先發布,後註冊。

我們先從發佈粘性事件開始:

EventBus.getDefault().postSticky(Object event);

來看看發送粘性事件的內部方法postSticky()

   public void postSticky(Object event) {
        synchronized (stickyEvents) {
            stickyEvents.put(event.getClass(), event);
        }
        post(event);//發送事件
    }

這裏就做了兩件事,stickyEvents保存事件類型已經對應粘性事件,然後通過post()發送事件,這裏的發送事件與發送普通事件一致。所以如果在發送粘性事件之前,如果有相同類型的事件訂閱者,並且是非粘性的,那麼訂閱者依然可以接受到發送出來的粘性事件。

發送完粘性事件之後,再註冊事件、實現事件處理方法。核心的註冊流程還是上面的register()中的subscribe()方法,有一段粘性事件的代碼沒有分析:

  private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
  		······
		//如果訂閱者事件處理方法sticky = true,表示該方法接收粘性事件
        if (subscriberMethod.sticky) {
        	//默認爲true,表示是否向上查找該事件的所有子類
        	//需要考慮該事件類型的所有子類現有的粘性事件
            if (eventInheritance) {
                //stickyEvents發送粘性事件時,保存事件類型和對應事件
                Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
                for (Map.Entry<Class<?>, Object> entry : entries) {
                    Class<?> candidateEventType = entry.getKey();
                    //當前事件類型是否與candidateEventType一致或者是其子類
                    if (eventType.isAssignableFrom(candidateEventType)) {
                        Object stickyEvent = entry.getValue();//獲得對應事件
                        //處理粘性事件
                        checkPostStickyEventToSubscription(newSubscription, stickyEvent);
                    }
                }
            } else {
                Object stickyEvent = stickyEvents.get(eventType);
                checkPostStickyEventToSubscription(newSubscription, stickyEvent);
            }
        }
    }

粘性事件的處理就是在註冊EventBus的時候,遍歷粘性事件stickyEvents集合,如果當前要註冊的訂閱事件方法是粘性的,並且該方法接收事件類型與粘性事件stickyEvents集合中的某個事件類型相同或者同類,則取出stickyEvents對應事件類型的事件,通過checkPostStickyEventToSubscription()做進一步處理:

   private void checkPostStickyEventToSubscription(Subscription newSubscription, Object stickyEvent) {
        if (stickyEvent != null) {
        	//事件處理
            postToSubscription(newSubscription, stickyEvent, isMainThread());
        }
    }

最終通過postToSubscription()方法對粘性事件完成處理。這就是粘性事件的整個處理過程。

粘性事件的流程:

  • 發送粘性事件的時候先根據事件類型保存粘性事件,然後再和普通事件一樣post()發送事件;
  • 在註冊事件時根據事件處理方法判斷sticky = true是粘性事件,如果stickyEventseventType一致則處理粘性事件,直接postToSubscription()響應處理事件。

三、總結

3.1註冊事件原理

通過registere()註冊一個訂閱者,先獲取當前訂閱者所有訂閱方法subscriberMethods,如果方法緩存METHOD_CACHE如果有則直接返回,沒有則通過反射和註解@Subscribe註解找到方法名集合並保存到METHOD_CACHE中;然後根據訂閱者的eventType將所有訂閱者信息subscriptions緩存到subscriptionsByEventType(HashMap)中,根據訂閱者將訂閱者的所有事件類型subscribedEvents(eventType集合)緩存到typesBySubscriber(HashMap)中。

3.2註銷事件原理

通過unregister()得到要註銷的訂閱者,從typesBySubscriber獲取該訂閱者的所有訂閱事件類型集合subscribedTypes;從該集合中根據事件類型eventType獲取所有訂閱者信息集合subscriptions,從集合中刪除remove()該訂閱者信息;再將typesBySubscriber對應的訂閱者的事件類型集合移除remove()

3.3發送並處理事件原理

獲取當前線程事件隊列ThreadLocal<PostingThreadState>,將要發送的事件event添加到隊列中;然後根據訂閱者eventClass向上查找所有的事件類型集合eventTypes,分別獲取所有的事件類型集合中的訂閱者信息集合subscriptions,再在訂閱者信息集合根據事件類型eventClass獲取對應的訂閱者信息subscription,最後去處理事件。

處理事件postToSubscription()是根據訂閱事件的方法的線程模式,如果符合線程模式則直接執行invokeSubscriber()方法,用反射的原理invoke()來執行訂閱事件的處理方法;如果不符合線程模式,則通過Poster類先將事件event和訂閱者信息subscription封裝成PendingPost加入enqueue()消息隊列,隊列在排隊等待執行(後臺線程會通過線程池pool做進一步處理),切換對應的線程後invokeSubscriber()執行訂閱事件的處理函數。

3.4粘性事件原理

發送粘性事件的時候先根據事件類型保存粘性事件stickyEvents;然後再和普通事件一樣post()發送事件;因爲粘性事件是先發布,後註冊響應事件的,所以在註冊事件時sticky = true則進行粘性事件處理,如果stickyEventseventType一致則處理粘性事件,直接postToSubscription()響應處理事件。

簡單總結:
EventBus原理簡單來說就是通過訂閱者subscriber註冊時保存訂閱者的所有訂閱事件類型和所有訂閱者信息,發送事件時根據訂閱者從緩存從得到相應的訂閱者信息數據(包含subscriber和訂閱方法的方法名,線程模式,事件類型,優先級,粘性等信息),通過線程模式,直接(對應線程)或間接(Poster類事件加入enqueue()消息隊列,隊列在排隊等待執行,做進一步處理)通過反射的原理來執行訂閱者的事件處理方法,註銷時通過訂閱者和事件類型eventType將緩存的信息移除。

至此,本文結束!


源碼地址:https://github.com/FollowExcellence/EventBusDemo

請尊重原創者版權,轉載請標明出處:https://blog.csdn.net/m0_37796683/article/details/105820728 謝謝!


相關文章:

EventBus3.2詳解和使用(一)

 ● EventBus:普通事件和粘性事件的使用

EventBus3.2詳解和使用(二)

 ● EventBus三要素、線程模式、優先級和AndroidEventBus的使用

EventBus3.2詳解和使用(三)

 ● EventBus內部原理

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章