EventBus 及其索引源碼分析

EventBus項目地址爲https://github.com/greenrobot/EventBus,clone到本地,主要看EventBus這個類就可以
關於EventBus的使用可以閱讀這篇文章https://blog.csdn.net/Icarus_/article/details/103685194

1、註解定義

EventBus使用註解@Subscribe來標識一個註冊方法,RetentionPolicy.RUNTIME表示是運行時註解,ElementType.METHOD表示可註解方法

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Subscribe {
    //線程模型
    ThreadMode threadMode() default ThreadMode.POSTING;
    //是否是粘性事件
    boolean sticky() default false;
    //訂閱事件優先級
    int priority() default 0;
}

2、EventBus初始化

EventBus發送事件,註冊,反註冊都會調用getDefault方法來完成實例的創建。
使用EventBus.getDefault方法獲取一個EventBus對象,內部使用的是單例模式,會初始化一些重要的成員變量。單例模式的構造函數一般都是private的,而這裏的構造函數是public的,這樣設計的原因是EventBus在我們的程序中不僅僅只有一條總線,還有其他的EventBus總線,訂閱者可以註冊到不同的EventBus上,通過不同的EventBus來發送數據,不同的EventBus發送數據是相互隔離開的,訂閱者只會收到註冊在該線程上的數據。

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;
}

public EventBus() {
    this(DEFAULT_BUILDER);
}

看一下EventBus類中定義的一些字段:
DEFAULT_BUILDER,是一個EventBusBuilder,EventBus是通過構造者模式build這個內部類進行對象創建的。
currentPostingThreadState,是一個ThreadLocal,可以在指定的線程中存儲數據。
subscriptionsByEventType,發送event時,通過這個map找到訂閱者。
typesBySubscriber,當註冊和反註冊事件的時候,操作這個map。
stickyEvents,維護粘性事件。
indexCount,生成的索引。
subscriberMethodFinder,對已經註解的方法的查找器。

private static final EventBusBuilder DEFAULT_BUILDER = new EventBusBuilder();

private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;
private final Map<Object, List<Class<?>>> typesBySubscriber;
private final Map<Class<?>, Object> stickyEvents;

private final ThreadLocal<PostingThreadState> currentPostingThreadState = new ThreadLocal<PostingThreadState>() {
    ......
};
private final SubscriberMethodFinder subscriberMethodFinder;
private final int indexCount;

最重要的三個成員變量poster,poster就是負責線程間調度的

private final Poster mainThreadPoster;
private final BackgroundPoster backgroundPoster;
private final AsyncPoster asyncPoster;

在構造方法中初始化

mainThreadPoster = mainThreadSupport != null ? mainThreadSupport.createPoster(this) : null;
backgroundPoster = new BackgroundPoster(this);
asyncPoster = new AsyncPoster(this);

1.mainThreadPoster,跟進去看一下他的初始化,發現返回的是HandlerPoster這個類,發現他是繼承自Handler。handleMessage開啓一個循環,從隊列中獲取數據,然後分發pendingPost,在判斷每次分發的時間是否小於最大值,來繼續下一次循環或者跳出循環。

public class HandlerPoster extends Handler implements Poster {
    //用來放即將執行的post的隊列
    private final PendingPostQueue queue;
    //post事件在HandleMessage存在的最大的時間值
    private final int maxMillisInsideHandleMessage;
    private final EventBus eventBus;
    //標識handler是否運行起來了
    private boolean handlerActive;
    ......

    @Override
    public void handleMessage(Message msg) {
        boolean rescheduled = false;
        try {
            long started = SystemClock.uptimeMillis();
            while (true) {
                //從隊列中獲取pendingPost,他維護着一個對象複用池
                PendingPost pendingPost = queue.poll();
                if (pendingPost == null) {
                    synchronized (this) {
                        // Check again, this time in synchronized
                        pendingPost = queue.poll();
                        if (pendingPost == null) {
                            handlerActive = false;
                            return;
                        }
                    }
                }
                //循環,在隊列中獲取到數據後,分發事件
                eventBus.invokeSubscriber(pendingPost);
                //每分發完事件都會對比下時間,判斷時間是否小於設定的最大值
                long timeInMethod = SystemClock.uptimeMillis() - started;
                if (timeInMethod >= maxMillisInsideHandleMessage) {
                    if (!sendMessage(obtainMessage())) {
                        throw new EventBusException("Could not send handler message");
                    }
                    rescheduled = true;
                    return;
                }
            }
        } finally {
            handlerActive = rescheduled;
        }
    }
    ......
}

2.backgroundPoster,跟進去看一下,它實現的是runnable,同樣的在run方法中循環隊列,發送消息。

final class BackgroundPoster implements Runnable, Poster {
    .......
    @Override
    public void run() {
        ......
        while (true) {
            PendingPost pendingPost = queue.poll(1000);
            if (pendingPost == null) {
                synchronized (this) {
                    // Check again, this time in synchronized
                    pendingPost = queue.poll();
                    if (pendingPost == null) {
                        executorRunning = false;
                        return;
                    }
                }
            }
            eventBus.invokeSubscriber(pendingPost);
        }
        ......
    }
}

3.asyncPoster,跟進去看一下,也是實現runnable,他的run方法只獲取隊列中的一個pendingPost進行分發。

class AsyncPoster implements Runnable, Poster {
    .......

    @Override
    public void run() {
        PendingPost pendingPost = queue.poll();
        if(pendingPost == null) {
            throw new IllegalStateException("No pending post available");
        }
        eventBus.invokeSubscriber(pendingPost);
    }

}

3、EventBus註冊

3.1 register方法

當EventBus對象創建好之後,就可以調用註冊方法register來將某個類註冊到這個EventBus對象上。在register方法中,通過subscriberMethodFinder的findSubscriberMethods方法找到註冊到EventBus的subscriber類中所有的被@Subscribe註解的訂閱事件方法,遍歷找到的方法,調用subscribe方法來保存他們。

public void register(Object subscriber) {
    Class<?> subscriberClass = subscriber.getClass();
    List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
    synchronized (this) {
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            subscribe(subscriber, subscriberMethod);
        }
    }
}
3.2 findSubscriberMethods方法

findSubscriberMethods方法會把找到的監聽方法封裝成一個SubscriberMethod對象,一個監聽方法就對應一個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方法是如何找到一個類中所有被註解的方法的呢。EventBus會將一個類的訂閱方法的列表緩存起來,如果有緩存,則會直接返回,沒有再通過反射或是編譯時生成的代碼找到訂閱方法列表

List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {
    //獲取緩存
    List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);
    //有則返回
    if (subscriberMethods != null) {
        return subscriberMethods;
    }
    //默認爲false
    if (ignoreGeneratedIndex) {
        //通過反射獲取訂閱方法
        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 {
        //放入緩存
        METHOD_CACHE.put(subscriberClass, subscriberMethods);
        return subscriberMethods;
    }
}
3.2.1 findUsingInfo方法

看一下findUsingInfo方法,獲取到findState,得到訂閱者及其有關信息,通過循環來遍歷,每次遍歷完之後都會調用findState方法,依次找尋findState父類。最後getMethodsAndRelease方法返回和釋放資源,內部主要就是清空了FindState裏面的3個集合。

private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
    FindState findState = prepareFindState();
    findState.initForSubscriber(subscriberClass);
    while (findState.clazz != null) {
        //獲取到findState它裏面存儲訂閱者一些信息
        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中
                    findState.subscriberMethods.add(subscriberMethod);
                }
            }
        } else {
            //如果findState集合爲空
            findUsingReflectionInSingleClass(findState);
        }
        findState.moveToSuperclass();
    }
    //返回和釋放資源
    return getMethodsAndRelease(findState);
}

FindState是用來保存找到的註解過的方法以及他們的狀態,看一下他的內部

static class FindState {
    //保存所有訂閱方法
    final List<SubscriberMethod> subscriberMethods = new ArrayList<>();
    //事件類型爲key,訂閱方法爲value
    final Map<Class, Object> anyMethodByEventType = new HashMap<>();
    //訂閱方法爲key,訂閱者的class對象爲value
    final Map<String, Class> subscriberClassByMethodKey = new HashMap<>();
    ......
}

接着看一下prepareFindState()方法,他是從FIND_STATE_POOL對象池中找到FindState對象的,找到之後,將該位置清空,爲了之後的複用。

private FindState prepareFindState() {
    synchronized (FIND_STATE_POOL) {
        for (int i = 0; i < POOL_SIZE; i++) {
            FindState state = FIND_STATE_POOL[i];
            if (state != null) {
                FIND_STATE_POOL[i] = null;
                return state;
            }
        }
    }
    return new FindState();
}
3.2.2 findUsingReflectionInSingleClass方法

接下來看findUsingReflectionInSingleClass方法,它通過反射獲取到註冊到EventBus類中的所有方法,然後遍歷,過濾掉不符合的方法,保留被@Subscribe註解的方法,封裝成SubscriberMethod對象保存起來。

private void findUsingReflectionInSingleClass(FindState findState) {
    Method[] methods;
    try {
        //獲取到註冊到EventBus類中的所有方法
        // This is faster than getMethods, especially when subscribers are fat classes like Activities
        methods = findState.clazz.getDeclaredMethods();
    } catch (Throwable th) {
        ......
    }
    //遍歷所有方法
    for (Method method : methods) {
        int modifiers = method.getModifiers();
        //如果是public,且是非靜態非抽象方法
        if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
            Class<?>[] parameterTypes = method.getParameterTypes();
            //是否只有1個參數,因爲eventbus只允許訂閱方法中的訂閱事件是1個
            if (parameterTypes.length == 1) {
                //獲取Subscribe註解
                Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                if (subscribeAnnotation != null) {
                    //創建SubscriberMethod對象保存起來
                    Class<?> eventType = parameterTypes[0];
                    if (findState.checkAdd(method, eventType)) {
                        //獲取到線程模式
                        ThreadMode threadMode = subscribeAnnotation.threadMode();
                        //將訂閱方法添加到findState中
                        findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
                                subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
                    }
                }
            } 
            ......
        } 
        ......
    }
}

看一下里面的checkAdd方法,他是用來判斷訂閱的方法是否可以添加到訂閱方法集合當中。在EventBus中有一個特點,一個訂閱者包括他所有的父類或子類,不會有多個方法相同的全部去接受同一個事件,但是可能會出現這樣一種情況:子類去訂閱該事件,同時父類也會去訂閱該事件,這時通過方法簽名進行檢查

boolean checkAdd(Method method, Class<?> eventType) {
    //2級檢查:1級,僅具有事件類型的(快速)。2級具有完整簽名的。
    //通常訂閱者沒有監聽相同事件類型的方法。
    //使用在FindState中定義的map,返回之前的value也就是之前的方法
    Object existing = anyMethodByEventType.put(eventType, method);
    //根據之前的方法是否爲空進行下面的操作
    if (existing == null) {
        return true;
    } else {
        if (existing instanceof Method) {
            //根據方法簽名進行檢查
            if (!checkAddWithMethodSignature((Method) existing, eventType)) {
                // Paranoia check
                throw new IllegalStateException();
            }
            // Put any non-Method object to "consume" the existing Method
            anyMethodByEventType.put(eventType, this);
        }
        return checkAddWithMethodSignature(method, eventType);
    }
}
3.3 Subscribe方法

findSubscriberMethods方法返回列表List後,在register方法中會遍歷這個列表,調用subscribe方法保存起來,在subscribe方法中,首先會創建一個訂閱方法SubscriberMethod對應的Subscription對象,然後獲取訂閱事件對應的Subscription集合,CopyOnWriteArrayList,如果沒有則創建,最後將新建的Subscription對象存入集合,這個集合又保存在EventBus最重要的數據結構Map<Class<?>, CopyOnWriteArrayList>對象subscriptionsByEventType中,EventBus的註冊主要是完成subscriptionsByEventType對象數據的初始化,這個Map的key爲事件類型的Class對象,value爲Subscription集合。我們在EventBus中所訂閱的事件都會有與之對應的訂閱列表,而列表中每個Subscription都會對應一個訂閱方法。

private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
    //獲取訂閱方法對應的事件
    Class<?> eventType = subscriberMethod.eventType;
    //一個訂閱方法subscriberMethod對應一個Subscription
    Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
    //獲取事件對應的Subscription集合
    CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
    //如果沒有集合則創建,並加入subscriptionsByEventType中
    if (subscriptions == null) {
        subscriptions = new CopyOnWriteArrayList<>();
        subscriptionsByEventType.put(eventType, subscriptions);
    } else {
        if (subscriptions.contains(newSubscription)) {
            //如果newSubscription保存過則拋異常
        }
    }
    //獲取CopyOnWriteArrayList<Subscription>集合的大小
    int size = subscriptions.size();
    //遍歷subscriptions
    for (int i = 0; i <= size; i++) {
        //根據優先級將新建的newSubscription加入集合
        if (i == size || subscriberMethod.priority > subscriptions.get(i).subscriberMethod.priority) {
            subscriptions.add(i, newSubscription);
            break;
        }
    }

    List<Class<?>> subscribedEvents = typesBySubscriber.get(subscriber);
    if (subscribedEvents == null) {
        subscribedEvents = new ArrayList<>();
        typesBySubscriber.put(subscriber, subscribedEvents);
    }
    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);
        }
    }
}

總結一下subscribe
1.首先判斷是否註冊過該事件
2.然後按照優先級插入到subscriptionsByEventType的value的list中
3.然後在添加到typesBySubscriber的value的list中
4.發佈事件,checkPostStickyEventToSubscription

4、EventBus發佈事件

由於註冊時構建了一個事件類型對應的Subscription集合,發佈時獲取到該集合然後遍歷,調用裏面的訂閱方法。
看一下post方法,先通過threadLocal獲取到PostingThreadState,他就是發送事件的線程狀態的封裝類,內部有事件隊列集合,是否正在發送事件的標誌位等

public void post(Object event) {
    //通過threadLocal獲取到PostingThreadState
    PostingThreadState postingState = currentPostingThreadState.get();
    //獲取到事件的隊列
    List<Object> eventQueue = postingState.eventQueue;
    eventQueue.add(event);
    //是否正在發送事件
    if (!postingState.isPosting) {
        //通過looper判斷當前是否在主線程
        postingState.isMainThread = isMainThread();
        postingState.isPosting = true;
        if (postingState.canceled) {
            throw new EventBusException("Internal error. Abort state was not reset");
        }
        try {
            //循環直到整個隊列都爲空
            while (!eventQueue.isEmpty()) {
                //不爲空,調用這個方法發送事件
                postSingleEvent(eventQueue.remove(0), postingState);
            }
        } finally {
            //發送完
            postingState.isPosting = false;
            postingState.isMainThread = false;
        }
    }
}

post內部調用了postSingleEvent,postSingleEvent內部又調用了postSingleEventForEventType方法,他會獲取到發佈事件對應的Subscription集合,遍歷,調用postToSubscription方法來完成訂閱方法的調用。

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;
            boolean aborted = false;
            try {
                //發佈事件,調用Subscription對象對應的訂閱方法
                postToSubscription(subscription, event, postingState.isMainThread);
                aborted = postingState.canceled;
            } 
            ......
        }
        return true;
    }
    return false;
}

postToSubscription裏面會根據訂閱方法的線程模型來完成在哪個線程執行

private void postToSubscription(Subscription subscription, Object event, boolean isMainThread) {
    switch (subscription.subscriberMethod.threadMode) {
        case POSTING:
            //posting,直接在當前線程執行
            invokeSubscriber(subscription, event);
            break;
        case MAIN:
            //mian,在主線程執行
            if (isMainThread) {
                //如果在主線程,直接執行
                invokeSubscriber(subscription, event);
            } else {
                //否則切換到主線程,內部是handler完成的
                mainThreadPoster.enqueue(subscription, event);
            }
            break;
        case MAIN_ORDERED:
            //將在主線程中調用。該事件總是排隊等待以後交付給訂閱者,因此對post的調用將立即返回。這爲事件處理提供了更嚴格且更一致的順序
            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) {
                //如果當前是主線程,則在EventBus內部線程池中執行
                backgroundPoster.enqueue(subscription, event);
            } else {
                //不在主線程直接調用
                invokeSubscriber(subscription, event);
            }
            break;
        case ASYNC:
            //在EventBus內部線程池中執行
            asyncPoster.enqueue(subscription, event);
            break;
        default:
            throw new IllegalStateException("Unknown thread mode: " + subscription.subscriberMethod.threadMode);
    }
}

5、EventBus的反註冊

註冊是給Map<Class<?>, CopyOnWriteArrayList>對象subscriptionsByEventType中添加Subscription集合,那麼反註冊就是清除掉訂閱者subscriber的Subscription

public synchronized void unregister(Object subscriber) {
    //獲取訂閱者訂閱事件類型的集合
    List<Class<?>> subscribedTypes = typesBySubscriber.get(subscriber);
    if (subscribedTypes != null) {
        //遍歷
        for (Class<?> eventType : subscribedTypes) {
            //通過事件類型和訂閱者找到對應的Subscription並清除
            unsubscribeByEventType(subscriber, eventType);
        }
        //移除訂閱者
        typesBySubscriber.remove(subscriber);
    } else {
        logger.log(Level.WARNING, "Subscriber to unregister was not registered before: " + subscriber.getClass());
    }
}

typesBySubscriber是Map<Object, List<Class<?>>>類型的集合,key是訂閱者,value是訂閱者訂閱事件類型集合。他的數據添加是在註冊時完成的,反註冊時通過他獲取訂閱者訂閱事件類型集合,然後遍歷集合,調用unsubscribeByEventType。

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--;
            }
        }
    }
}

6、EventBus3.0的索引

6.1、索引的創建

EventBus有一個註解處理器模塊EventBusAnnotationProcessor,此模塊下只有一個類EventBusAnnotationProcessor

@SupportedAnnotationTypes("org.greenrobot.eventbus.Subscribe")
@SupportedOptions(value = {"eventBusIndex", "verbose"})
public class EventBusAnnotationProcessor extends AbstractProcessor {
    ......
}

@SupportedAnnotationType指定處理的註解爲@Subscribe,@SupportedOptions指定處理註解的參數,eventBusIndex和verbose,這兩個參數是在build.gradle配置的(配置見eventBus的使用)。

eventBusIndex指定將要生成的EventBus的索引類,verbose是布爾值,編譯時是否打印log。註解處理器的配置文件在模塊下的META-INF/services/javax.annotation.processing.Processor裏,內容爲

org.greenrobot.eventbus.annotationprocessor.EventBusAnnotationProcessor

接下來看EventBusAnnotationProcessor的process方法是如何處理註解來生成索引文件的

@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {
    //信使,用來編譯時打印log
    Messager messager = processingEnv.getMessager();
    try {
        //獲取參數eventBusIndex
        String index = processingEnv.getOptions().get(OPTION_EVENT_BUS_INDEX);
        ......
        //獲取參數verbose
        verbose = Boolean.parseBoolean(processingEnv.getOptions().get(OPTION_VERBOSE));
        int lastPeriod = index.lastIndexOf('.');
        //獲取生成索引文件的包名
        String indexPackage = lastPeriod != -1 ? index.substring(0, lastPeriod) : null;
        ......
        //收集所有訂閱者信息
        collectSubscribers(annotations, env, messager);
        //檢查訂閱者,如果不是public的類裏面或者裏面的事件也不是public則忽略
        checkForSubscribersToSkip(messager, indexPackage);
        if (!methodsByClass.isEmpty()) {
            //創建索引文件
            createInfoIndexFile(index);
        } else {
            ......
        }
        writerRoundDone = true;
    } 
    ......
    return true;
}

在process方法中,先獲取eventBusIndex和verbose參數,然後調用collectSubscribers方法收集所有訂閱者信息,檢查訂閱者,如果不是public的類裏面或者裏面的事件也不是public則忽略,最後調用createInfoIndexFile生成索引文件。
collectSubscribers收集收集所有訂閱者和他內部所有的訂閱方法,然後保存在一個ListMap集合methodsByClass中,由於EventBusAnnotationProcessor只處理@Subscribe註解,所以collectSubscribers第一個參數annotations集合中只有一個@Subscribe對應的TypeElement

private void collectSubscribers(Set<? extends TypeElement> annotations, RoundEnvironment env, Messager messager) {
    //遍歷所有註解類型元素,這裏只有註解@Subscribe對應的類型元素
    for (TypeElement annotation : annotations) {
        //獲取所有被@Subscribe註解的元素,也就是訂閱方法對應的元素
        Set<? extends Element> elements = env.getElementsAnnotatedWith(annotation);
        //遍歷所有方法對應的elements
        for (Element element : elements) {
            if (element instanceof ExecutableElement) {
                ExecutableElement method = (ExecutableElement) element;
                if (checkHasNoErrors(method, messager)) {
                    //獲取訂閱方法外部元素,就是所在類的元素類型,如MainActivity
                    TypeElement classElement = (TypeElement) method.getEnclosingElement();
                    methodsByClass.putElement(classElement, method);
                }
            } else {
                messager.printMessage(Diagnostic.Kind.ERROR, "@Subscribe is only valid for methods", element);
            }
        }
    }
}

通過collectSubscribers收集到所有的訂閱者信息後,就調用createInfoIndexFile創建索引類,使用的是JDK的JavaFileObject來生成Java文件。

6.2、索引的使用

生成索引文件後,就可以使用這個索引文件初始化EventBus實例,因爲所有的訂閱方法都在索引文件保存起來了,就不需要通過遍歷和反射註解來解析訂閱方法了。

EventBusBuilder方法創建一個EventBusBuilder對象,然後通過addIndex方法將生成的的索引類加入集合,最後調用InstallDefaultEventBus方法創建EventBus對象。在構造方法中使用subscriberInfoIndexes初始化SubscriberMethodFinder對象。
接下來在註冊過程中使用SubscriberMethodFinder查找訂閱方法時,會直接使用索引類獲取訂閱方法。

EventBus(EventBusBuilder builder) {
    ......
    //使用subscriberInfoIndexes初始化subscriberMethodFinder
    subscriberMethodFinder = new SubscriberMethodFinder(builder.subscriberInfoIndexes,
            builder.strictMethodVerification, builder.ignoreGeneratedIndex);
    ......
}

默認使用findUsingInfo方法查找所有的訂閱方法,會調用getSubscriberInfo()通過查找索引文件的方式來獲取訂閱者所對應的訂閱者信息SubscriberInfo對象,如果沒有開啓索引加上,則會退化爲反射查找。

private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {
    //準備一個查找狀態的對象,用來跟蹤訂閱方法查找的過程
    FindState findState = prepareFindState();
    //設置findState的訂閱者類
    findState.initForSubscriber(subscriberClass);
    while (findState.clazz != null) {
        //通過索引獲取subscriberInfo,該對象中包含所有的訂閱方法
        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();
    }
    返回找到的方法列表,釋放資源
    return getMethodsAndRelease(findState);
}

看getSubscriberInfo()方法

private SubscriberInfo getSubscriberInfo(FindState findState) {
    ......
    if (subscriberInfoIndexes != null) {
        //遍歷所有索引文件
        for (SubscriberInfoIndex index : subscriberInfoIndexes) {
            //獲取訂閱者信息
            SubscriberInfo info = index.getSubscriberInfo(findState.clazz);
            if (info != null) {
                return info;
            }
        }
    }
    return null;
}

然後就可以通過這個訂閱者信息對象獲取其中的SubscriberMethod數組,完成訂閱方法的查找

@Override
public synchronized SubscriberMethod[] getSubscriberMethods() {
    int length = methodInfos.length;
    SubscriberMethod[] methods = new SubscriberMethod[length];
    for (int i = 0; i < length; i++) {
        SubscriberMethodInfo info = methodInfos[i];
        methods[i] = createSubscriberMethod(info.methodName, info.eventType, info.threadMode,
                info.priority, info.sticky);
    }
    return methods;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章