Android框架源碼分析——EventBus

1、介紹篇

關於EventBus的介紹和使用,此處只做基本介紹,很多開發者都很熟悉其使用方法,也嚐到了EventBus在開發中的便捷之處,關於EventBus的使用和源碼筆者也是早有接處,本文主要是針對其源碼進行分析,帶你一起探索EventBus的代碼細節

  • EventBus三要素
  1. Event 事件:可以是任意類型,既然爲事件一定伴隨着傳遞和操作,在使用中扮演信息和事件載體
  2. Subscriber 事件訂閱者:這裏的訂閱着和RxJava中作用一樣負責監聽某種狀態,在狀態和條件改變時回調處理,在EventBus3.0之前所有的訂閱方法必須是onEvent開頭,分別是onEvent、onEventMainThread、onEventBackgroundThread和onEventAsync,這些方法在方法名中就確定了執行的線程;而在3.0之後事件處理的方法名可以隨意取,不過需要加上註解@subscribe(),並且傳遞參數指定線程模型,默認是POSTING。
  3. Publisher 事件的發佈者:我們可以在任意線程裏發佈事件即發佈Event,攜帶信息的EvValue(ent會找到訂閱的方法執行;一般情況下,使用EventBus.getDefault()就可以得到一個EventBus對象,然後再調用post(Object)方法即可;
  • EventBus3.0有四種線程模型
  1. POSTING (默認): 表示事件處理函數的線程跟發佈事件的線程在同一個線程即誰發誰執行
  2. MAIN 表示事件處理函數的線程在主線程(UI)線程,因此在這裏不能進行耗時操作;
  3. BACKGROUND 表示事件處理函數的線程在後臺線程,因此不能進行UI操作;如果發佈事件的線程是主線程(UI線程),那麼事件處理函數將會開啓一個後臺線程,如果果發佈事件的線程是在後臺線程,那麼事件處理函數就使用該線程
  4. ASYNC 表示無論事件發佈的線程是哪一個,事件都會被單獨的線程池處理
  • 使用介紹
  1. 定義事件Event
  data class UserEvent(val id: Int, var name: String, var age: Int)
  1. 註冊解除註冊
EventBus.getDefault().register(this)
EventBus.getDefault().unregister(this)
  1. 編寫訂閱的方法的方法,並添加@Subscribe註解,註解方法必須要是Public、非靜態、非抽象的、只能有一個參數,否則會收到EventBusException
@Subscribe
public fun toast(userEvent: UserEvent) {
    Toast.makeText(this, "${userEvent.id}  ${userEvent.name}  ${userEvent.age}", Toast.LENGTH_SHORT).show()
}
  1. 發送事件
EventBus.getDefault().post(UserEvent(1,"AAAA",30)) 
EventBus.getDefault().postSticky(UserEvent(1,"AAAA",30)) 

2、EventBus源碼分析

對源碼的分析還遵循着之前的以Arouter爲例談談學習開源框架的最佳姿勢中講解的開源框架的學習姿勢進行分析,主要從一下幾個方面:

  1. 整體執行流程
  2. 如何註冊和解除註冊觀察者
  3. 如何獲取和保存觀察者和訂閱的事件信息
  4. 如何發送事件和接收事件
  5. 如何處理sticky事件

2.1整體執行流程

EventBus時序圖
上面爲EventBus源碼的時許圖,爲了體現重要的細節畫的有點亂,這裏先根據時許圖用通俗的話簡單介紹下工作過程方便後面的理解,首先Client的註冊EventBus時,EventBus會先登記你觀察什麼事件和執行什麼方法,當這個事件發送到EventBus時,EventBus查找登記的方法反射調用執行即可;

2.2如何註冊和解除註冊觀察者

  • 註冊過程
  1. EventBus.getDefault():以單利的形式初始化並對外提供EventBus實例
public static EventBus getDefault() {
    if (defaultInstance == null) {
        synchronized (EventBus.class) {
            if (defaultInstance == null) {
                defaultInstance = new EventBus();
            }
        }
    }
    return defaultInstance;
}
  1. register(Object : subscribe):註冊觀察者
public void register(Object subscriber) {
    Class<?> subscriberClass = subscriber.getClass();  //獲取觀察者Class
    //獲取觀察者類中所有的註解方法,保存在List中
    List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
    synchronized (this) {
        for (SubscriberMethod subscriberMethod : subscriberMethods) { //循環執行subscribe()方法
            subscribe(subscriber, subscriberMethod);
        }
    }
}
  1. 保存觀察者、事件類型、訂閱方法
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
    Class<?> eventType = subscriberMethod.eventType;
    Subscription newSubscription = new Subscription(subscriber, subscriberMethod); //封裝信息
    // 根據監聽的事件類型從緩存中獲取保存的CopyOnWriteArrayList<Subscription>
    CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
    if (subscriptions == null) {
        subscriptions = new CopyOnWriteArrayList<>(); //緩存中不存在則創建並保存subscriptions
        subscriptionsByEventType.put(eventType, subscriptions);
    }

    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;
        }
    }
     //從typesBySubscriber中獲取緩存的貫徹的事件類型,如果不存在則創建並保存觀察者和觀察的事件類型
    List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
    if (subscribedEvents == null) {
        subscribedEvents = new ArrayList<>();
        typesBySubscriber.put(subscriber, subscribedEvents);
    }
    subscribedEvents.add(eventType);
}

簡單的總結註冊流程:

  1. 根據觀察的Event類型從緩存中獲取此Event類型的觀察方法集合,如果存在則添加此次的SubscriberMethod,如果不存在則創建集合保存SubscriberMethod,並緩存此Event類型的集合
  2. 遍歷集合中的SubscriberMethod,按照優先級降序排列
  3. 根據Subscribe實例從緩存中獲取觀察的Event類型集合,如果存在則添加,否則創建集合並添加緩存
  4. subscriber 保存對應的 eventType ,eventType 保存對應的訂閱方法
  • 取消註冊觀察者
  1. 取消註解相對簡單,主要過程在註釋中說明了,總結起來就是兩句話,線根據取消的Subscribe獲取其註冊的所有時間類型,然後根據事件類型移除所有的Sucribe對象;
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--;
            }
        }
    }
}

public synchronized void unregister(Object subscriber) {
    List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber); //從緩存中獲取Subscrober所觀察的事件類型集合
    if (subscribedTypes != null) {
        for (Class<?> eventType : subscribedTypes) { //循環遍歷集合執行unsubscribeByEventType()
            unsubscribeByEventType(subscriber, eventType);
        }
        typesBySubscriber.remove(subscriber); //移除觀察者
    } 
}

2.3獲取和保存觀察者和訂閱信息

  1. 關於保存在註冊中已經介紹了,主要使用subscriptionsByEventType和typesBySubscriber,保存每個Event類型對應的觀察信息和每個觀察者觀察的所有Event類型,subscriptionsByEventType主要用於發送事件時獲取觀察者並調用觀察方法,typesBySubscriber主要用於結出註解時確定需要移除哪些事件類型和哪些方法;
  2. 下面看一下獲取註冊的觀察者方法,方法的獲取是在register()中開始的,調用subscriberMethodFinder.findSubscriberMethods(subscriberClass)查詢註解方法;
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);
    }
    .......
        METHOD_CACHE.put(subscriberClass, subscriberMethods); //緩存獲取到的信息
        return subscriberMethods;
    }
}

這裏的操作基本框架中都會見到,首先根據Class從METHOD_CACHE中獲取,若不存在則反射遍歷Class獲取所有符合條件的方法,將結果緩存在METHOD_CACHE中,這裏使用METHOD_CACHE緩存是防止每一次註冊時都需要反射獲取,然後根據ignoreGeneratedIndex的值分別去加載觀察方法,這裏主要是是否使用索引查找,關於索引會在下一篇單獨介紹,這裏知道調用反射獲取就好了;

  1. findUsingReflectionInSingleClass(FindState findState):反射獲取並保存註解方法,方法看上去複雜其實是紙老虎,簡單來說就是遍歷所有方法然後判斷符不符合上面的方法要求,並將符合的封裝在SubscriberMethod中,返回所有方法的集合,具體細節看註釋細節
private void findUsingReflectionInSingleClass(FindState findState) {
    Method[] methods;
    try {
        methods = findState.clazz.getDeclaredMethods(); //反射獲取所有方法
    } 
    for (Method method : methods) { //遍歷方法
        int modifiers = method.getModifiers();
        if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) { //判斷修飾符Public
            Class<?>[] parameterTypes = method.getParameterTypes(); //獲取參數集合
            if (parameterTypes.length == 1) { 判斷參數長度是否爲1
                Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class); //獲取並判斷Subscribe註解
                if (subscribeAnnotation != null) {
                    Class<?> eventType = parameterTypes[0]; //取出參數類型,即觀察的事件類型
                    if (findState.checkAdd(method, eventType)) {
                        ThreadMode threadMode = subscribeAnnotation.threadMode(); //獲取直接的執行線程
                        //封裝所有信息在SubscriberMethod中,並保存在List<SubscriberMethod>中
                        findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
                                subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
                    }
                }
            } 
    }
}

2.4 執行發送事件和接收事件

  1. PostingThreadState:保存事件對列、線程信息及訂閱的Subscription實例,PostingThreadState保存在ThreadLocal中,所以每個線程有單獨的實例,即每個線程中有單獨的事件對列,彼此不受影響
final static class PostingThreadState {
    final List<Object> eventQueue = new ArrayList<>(); //當前線程的事件對列
    boolean isPosting; //是否正在發送事件
    boolean isMainThread; //是否爲主線程
    Subscription subscription; //觀察者信息
    Object event;  //事件
    boolean canceled;
}
  1. post(Object event):發送Event事件,發送事件後首先獲取當前線程的PostingThreadState,並將事件Event添加到內部對列中,並根據Looper信息判斷是否爲主線程,然後根據PostingThreadState的發送事件
public void post(Object event) {
   //獲取當前線程的事件對列,保存發送的事件
    PostingThreadState postingState = currentPostingThreadState.get();
    List<Object> eventQueue = postingState.eventQueue;
    eventQueue.add(event);

    if (!postingState.isPosting) { //判斷當前線程是否正在發送
        postingState.isMainThread = isMainThread();  // 賦值是否爲主線程
        postingState.isPosting = true;
        try {
            while (!eventQueue.isEmpty()) {
                postSingleEvent(eventQueue.remove(0), postingState); //循環便利對列,取出頭部事件執行
            }
        } 
    }
}
  1. postSingleEvent(Object event, PostingThreadState postingState)
private void postSingleEvent(Object event, PostingThreadState postingState) throws Error {
    Class<?> eventClass = event.getClass(); //獲取發送事件的Class
    boolean subscriptionFound = false;
    if (eventInheritance) {
        List<Class<?>> eventTypes = lookupAllEventTypes(eventClass); //獲取當前類和其父類的集合
        int countTypes = eventTypes.size();
        for (int h = 0; h < countTypes; h++) { //遍歷所喲Class集合執行postSingleEventForEventType()
            Class<?> clazz = eventTypes.get(h);
            subscriptionFound |= postSingleEventForEventType(event, postingState, clazz); 
        }
    } else {  //對於單獨的Event直接執行postSingleEventForEventType
        subscriptionFound = postSingleEventForEventType(event, postingState, eventClass);
    }
}

上述代碼根據發送的Event是否有父類進行分別處理,對於存在父類或接口的調用lookupAllEventTypes()獲取所有Class的集合,然後遍歷集合中的Class分別執行發送事件,也就是不僅發送自己類型的Event頁發送所有父類的event;對於單一的Event直接發送事件;

  1. postSingleEventForEventType():根據發送的Event類型,從subscriptionsByEventType中獲取所有註冊觀察此Event類型的Subscription實例,從而獲取其保存的觀察者和觀察方法,並調用postToSubscription()執行處理事件
private boolean postSingleEventForEventType(Object event, PostingThreadState postingState, Class<?> eventClass) {
    CopyOnWriteArrayList<Subscription> subscriptions;
    synchronized (this) {
        subscriptions = subscriptionsByEventType.get(eventClass); //從緩存中獲取Subscription集合
    }
    if (subscriptions != null && !subscriptions.isEmpty()) {
        for (Subscription subscription : subscriptions) {
            postingState.event = event;
            postingState.subscription = subscription;
            boolean aborted = false;
            try {
                postToSubscription(subscription, event, postingState.isMainThread); //執行發送事件
                aborted = postingState.canceled;
            } 
        }
        return true;
    }
    return false;
}
  1. postToSubscription():根據發送事件的線程和訂閱方法設定的線程分別處理事件,具體細節見註釋
private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
    switch (subscription.subscriberMethod.threadMode) {
        case POSTING:
            invokeSubscriber(subscription, event); //POSTING默認在同一線程執行,所以直接執行事件
            break;
        case MAIN:  // 對於指定Main線程方法,如果發送事件在Main線程直接執行,否則調用mainThreadPoster加入主線程對列
            if (isMainThread) {
                invokeSubscriber(subscription, event); 
            } else {
                mainThreadPoster.enqueue(subscription, event);
            }
            break;
        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;
        default:
            throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
    }
}

整個EventBus的執行流程最複雜的地方就是事件的發送,但總的來說還是很好理解的,在發送事件後從緩存的觀察方法中找到事件的監聽方法,然後根據二者的線程要求選擇合適的執行線程,然後反射執行方法即可,對於整個過程可能會有幾個疑問,比如:isMainThread在什麼時候設定的?mainThreadPoster是什麼?爲何可以切換線程?. backgroundPoster、asyncPoster如何實現功能?針對這幾個對象都是在EventBus初始化時設置好的,下面簡單分析下幾個疑問:

  1. isMainThread在什麼時候設定的?答案:在post()發送事件時直接判斷
postingState.isMainThread = isMainThread();  //Post方法中調用isMainThread()
private boolean isMainThread() {
    return mainThreadSupport != null ? mainThreadSupport.isMainThread() : true;
}
@Override
public boolean isMainThread() {
    return looper == Looper.myLooper(); // 此處的looper在創建傳入的Looper.getMainLooper()
}
  1. mainThreadPoster是什麼?爲何可以切換線程?
    答:mainThreadPoster是在初始化EventBus時創建的HandlerPoster,HandlerPoster是使用主線程對列初始化Handler實例,內部封裝了EventBus、主線程對列,在添加Event事件時發送信息到Handler中,在handleMessage()中調用eventBus.invokeSubscriber()執行觀察方法
public Poster createPoster(EventBus eventBus) {
    return new HandlerPoster(eventBus, looper, 10);
}
protected HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) {
    super(looper);
    this.eventBus = eventBus;
    this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage;
    queue = new PendingPostQueue(); //初始化主線程中的對列
}
queue.enqueue(pendingPost); //加入事件對列
if (!handlerActive) {
    handlerActive = true;
    if (!sendMessage(obtainMessage())) { //發送事件
        throw new EventBusException("Could not send handler message");
    }
}
@Override
public void handleMessage(Message msg) {
       eventBus.invokeSubscriber(pendingPost); //最終還是調用eventBus.invokeSubscriber()
}
  1. backgroundPoster、asyncPoster如何實現功能?
    答:backgroundPoster和asyncPoster原理一樣,他們都是Runnable的字類,在內部保存事件對列,添加事件後會調用線程池執行發送事件
class AsyncPoster implements Runnable, Poster {
    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); //調用線程池執行Runnable
    }
    @Override
    public void run() {
        PendingPost pendingPost = queue.poll();
        eventBus.invokeSubscriber(pendingPost); //取出事件調用 eventBus.invokeSubscriber()發送事件
    }
}

2.5、處理Sticky的Event

  1. postSticky():EventBus內部保存着每個粘性事件,發送粘性事件時首先被保存到緩存中去,然後按照正常事件發送出去,因爲粘性事件的本身還是事件
public void postSticky(Object event) {
    synchronized (stickyEvents) {
        stickyEvents.put(event.getClass(), event); //保存發送的Event事件
    }
    post(event); //按正常流程發送事件
}

2.粘性事件的接收, 在註冊觀察粘性事件時,如果Subscribe註解中標名接受粘性事件,則直接獲取緩存中匹配的粘性事件並直接發送,所以在觀察粘性事件時只要註冊成功立刻會執行方法

if (subscriberMethod.sticky) { 如果註冊的方法爲sticky
    if (eventInheritance) {
        Set<Map.Entry<Class<?>, Object>> entries = stickyEvents.entrySet();
        for (Map.Entry<Class<?>, Object> entry : entries) {
            Class<?> candidateEventType = entry.getKey();
            if (eventType.isAssignableFrom(candidateEventType)) { //遍歷stickyEvents集合,獲取符合的Event發送事件
                Object stickyEvent = entry.getValue();
                checkPostStickyEventToSubscription(newSubscription, stickyEvent);
            }
        }
    } else {
        Object stickyEvent = stickyEvents.get(eventType);
        checkPostStickyEventToSubscription(newSubscription, stickyEvent); //最終調用postToSubscription()處理事件
    }
}

3、EventBus飆車神技——索引

在上面的分析中我們知道,EventBus使用findUsingReflectionInSingleClass()通過反射獲取每個觀察者的註解方法,用官方的話說早期是因爲效率所以選擇反射放棄註解,EventBus 3.0後使用註解生成索引文件,在加載時不需要使用反射更好的提高了EventBus的效率;

3.1、索引的使用

  • 引入EventBus的註解
    因爲使用Kotlin開發,所以這裏使用Kapt引入,其他的使用方式可去官方查看點擊查看
implementation 'org.greenrobot:eventbus:3.1.1'
kapt 'org.greenrobot:eventbus-annotation-processor:3.1.1'
  • 在build.gradle中配置索引文件,這裏配置的信息會作爲最後生成的Index文件的包名和類名
kapt {
    arguments {
        arg('eventBusIndex', 'com.example.myapp.MyEventBusIndex')
    }
}
  • 實例化EventBus
EventBus.builder().addIndex(new MyEventBusIndex()).ignoreGeneratedIndex(false).installDefaultEventBus();
  • 創建事件的觀察方法
    @Subscribe
	public void action(TypeEvent event) {
	}
	
	@Subscribe(threadMode = ThreadMode.MAIN,sticky = true)
	public void actionTwo(TypeEvent event) {
	}
  • 查看生成的MyEventBusIndex
public class MyEventBusIndex implements SubscriberInfoIndex {
    private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX; // Key = Class Value= SubscriberInfo

    static { . //初始化HashMap保存信息
        SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>(); 
        putIndex(new SimpleSubscriberInfo(MainActivity.class, true, new SubscriberMethodInfo[] {
            new SubscriberMethodInfo("action", TypeEvent.class), //封裝信息到SimpleSubscriberInfo中並保存
            new SubscriberMethodInfo("actionTwo", TypeEvent.class, ThreadMode.MAIN, 0, true)
        }));
    }
    private static void putIndex(SubscriberInfo info) {
        SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);  //保存到HashMap中
    }

    @Override
    public SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {
        SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);
        if (info != null) {
            return info;
        } else {
            return null;
        }
    }
}

註解生成類中,首先創建Map集合用於保存所有的註解方法,在static代碼塊中將註解方法名、事件類型、線程信息、優先級、是否爲粘性事件都封裝在SubscriberMethodInfo中,然後將一個類中的創建的所有SubscriberMethodInfo保存在集合中,最後集合封裝在SimpleSubscriberInfo中,並以class爲Key保存在在Map中;

3.2、根據索引查找註解方法

EventBus.builder().addIndex(new MyEventBusIndex())方法中,將MyEventBusIndex實例保存在subscriberInfoIndexes中,然後在創建EventBus時使用subscriberInfoIndexes索引創建SubscriberMethodFinder的實例,通過之前的學習知道SubscriberMethodFinder負責查找所有的註冊方法,在SubscriberMethodFinder.findSubscriberMethods()中如果設置ignoreGeneratedIndex爲false,則執行findUsingInfo(),下面看看findUsingInfo()是如何獲取註解類中保存的方法的

  • findUsingInfo()
while (findState.clazz != null) {
    findState.subscriberInfo = getSubscriberInfo(findState); //獲取SubscriberInfo信息
    if (findState.subscriberInfo != null) {
        SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods(); //獲取Method集合
        for (SubscriberMethod subscriberMethod : array) { //遍歷Method集合
            if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {
                findState.subscriberMethods.add(subscriberMethod); //直接保存Method
            }
        }
    } else {
        findUsingReflectionInSingleClass(findState);//沒找到的繼續使用反射查找
    }
}

從上面的代碼中看到首先從getSubscriberInfo獲取subscriberInfo,可以推測到這裏獲取的subscriberInfo就是前面註解類中創建並實例化的SimpleSubscriberInfo,然後獲取其中保存的SubscriberMethod集合,遍歷集合中的SubscriberMethod保存在findState.subscriberMethods並返回,這樣就獲取到所有的註解方法集合了,下面看一下getSubscriberInfo如何獲取的。

  • getSubscriberInfo(findState)
if (subscriberInfoIndexes != null) {
    for (SubscriberInfoIndex index : subscriberInfoIndexes) {  //遍歷傳入的subscroberInfoIndexes集合
 SubscriberInfo info = index.getSubscriberInfo(findState.clazz); //從MyEventBudIndex中保存的集合中獲取SubscriberInfo實例
        if (info != null) {
            return info;
        }
    }
}

代碼邏輯很簡單,上面是使用class爲Key緩存的這裏遍歷所有的subscroberInfoIndexes然後以class爲鍵取出SubscriberInfo實例,到此註解生成類和解析方法的過程就分析完畢了,在編譯時就創建並保存好所有方法在執行時只需要直接獲取即可。

3.3、查看EventBusAnnotationProcessor

@SupportedAnnotationTypes("org.greenrobot.eventbus.Subscribe”) //聲明處理的註解類型
@SupportedOptions(value = {"eventBusIndex", "verbose”}) //配置的索引文件的參數
public class EventBusAnnotationProcessor extends AbstractProcessor {
    public static final String OPTION_EVENT_BUS_INDEX = "eventBusIndex";
    public static final String OPTION_VERBOSE = "verbose";

    private final ListMap<TypeElement, ExecutableElement> methodsByClass = new ListMap<>(); //保存類中的方法
    private final Set<TypeElement> classesToSkip = new HashSet<>(); //保存跳過的不符合要求的方法
    String index = processingEnv.getOptions().get(OPTION_EVENT_BUS_INDEX);  // 獲取build.gradle中配置的文件信息
    collectSubscribers(annotations, env, messager);  //獲取類中的所有註解方法,保存在methodsByClass中
    checkForSubscribersToSkip(messager, indexPackage);  //跳過不符合條件的方法保存在classesToSkip中
    createInfoIndexFile(index); //創建類文件
}

EventBusAnnotationProcessor中使用APT根據註解生成代碼,整個類中的方法不多,出去判斷和寫入的邏輯後上面爲主要的流程代碼,真個過程分四步,其餘細節可自行查看源碼:

  1. 獲取build.gradle中配置的文件信息並解析包名和類名
int period = index.lastIndexOf('.');
String myPackage = period > 0 ? index.substring(0, period) : null; //解析包名
String clazz = index.substring(period + 1); //解析類名
  1. 獲取類中的所有註解方法,保存在methodsByClass中
  2. 找出不符合條件的方法保存在classesToSkip中,主要過濾修飾符和參數
  3. 創建類文件並寫入數據

4、EventBus框架學習

4.1、設計模式

  • Builder設計模式:像很多框架一樣EventBus採用Builder模式配置和初始化EventBus
  • 單例模式:單例模式對外提供,保證程序中只有一個實例
  • 觀察者模式:整個EventBus的設計基礎就是觀察者模式,通過註冊觀察方法訂閱事件,在事件發生時響應訂閱方法

4.2、細節實現

  • 觀察方法和觀察類的緩存
    在EventBus中使用subscriptionsByEventType以EventType爲Key緩存每種事件類型對應的所有觀察者和觀察方法;再使用typesBySubscriber以Subscriber爲Key保存每個觀察者觀察的事件類型,簡單的說就是Subscriber --> EventType --> fun() 這樣將所有的方法、觀察者、事件類型聯繫起來,在發送事件時和註銷時查找都很便捷

  • ThreadLocal區分每個線程

在處理事件的每個線程上都有各自的ThreadLocal 實例,在ThreadLocal中創建PostingState實例用於保存每個線程的信息,這樣在事件發送時線程等信息就會伴隨事件,並根據線程確定處理方式,線程彼此互布影響。

  • 執行線程的切換

在EventBus的內部保存着主線程handler、backgroundPoster、asyncPoster實例,並且每個實例中保存着各自的對列,在事件發生時根據發送事件的線程和註解方法的信息,將事件加入到各自的對列中然後各自執行事件。

對EventBus的學習就到此結束了,本篇文章也是對之前筆記的整理,希望對學習EventBus和學習框架的同學有所幫助!

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