EventBus的基本使用和源碼解析

最近在項(xiàng)目中使用了EventBus(3.0),覺得非常好用,于是就看了一些關(guān)于EventBus源碼分析的文章,現(xiàn)在記錄下它的基本使用方法和看源碼過程中分析的一些問題。EventBus源碼地址

11212.gif

EventBus是什么?

EventBus是一個(gè)Android事件發(fā)布和訂閱的框架,通過解耦發(fā)布者和訂閱者來簡(jiǎn)化Android事件傳遞。

EventBus是干什么的?

EventBus-Publish-Subscribe.png

事件傳遞既可以用于Android四大組件間通訊,也可以用于異步線程和主線程間通訊等。傳統(tǒng)的事件傳遞方式包括:HandlerBroadcastReceiver、Interface回調(diào),而EventBus就是用來傳遞事件的,相比傳統(tǒng)的方式,它代碼簡(jiǎn)潔,使用簡(jiǎn)單,并將事件發(fā)布和訂閱充分解耦。

基本使用方法

添加依賴庫(kù)

//在app下的build.gradle中添加下面的依賴
compile 'org.greenrobot:eventbus:3.0.0'

注冊(cè)訂閱事件

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    EventBus.getDefault().register(this);
}

取消注冊(cè)訂閱事件

@Override
protected void onDestroy() {
    super.onDestroy();
    EventBus.getDefault().unregister(this);
}

發(fā)布事件

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

訂閱事件

@Subscribe(threadMode = ThreadMode.MAIN)
public void onEvent(Object obj) {
}

看吧,使用起來還是挺簡(jiǎn)單的,訂閱者真正需要處理邏輯的就是在有注解@subscribe的方法onEvent(Object obj)中去按照我們自己的需求去處理,當(dāng)然這里方法名不一定是onEvent了,我是受2.4版本影響,習(xí)慣了把方法名寫成onEvent,在3.0版本中可以隨意寫,但是參數(shù)的class類型必須要跟發(fā)布者post的參數(shù)的class類型一致。

使用場(chǎng)景

比如有一個(gè)列表頁面,每個(gè)item上都有評(píng)論數(shù),點(diǎn)贊數(shù)和點(diǎn)贊狀態(tài),點(diǎn)進(jìn)去到詳情頁,用戶可以進(jìn)行一些操作,評(píng)論,點(diǎn)贊,這時(shí)候詳細(xì)頁的評(píng)論數(shù)點(diǎn)贊數(shù)和點(diǎn)贊狀態(tài)我們可以立即更新,但是當(dāng)我們回到列表頁面的時(shí)候,也需要更新UI啊,之前也說了,傳統(tǒng)的事件傳遞方式有Handler,BroadcastReceiver,以及Interface回調(diào)等,如果不使用EventBus的話,我會(huì)考慮使用廣播的方式或者用startActivityForResult來打開詳細(xì)頁,等到從詳細(xì)頁返回到列表頁時(shí),將操作后的對(duì)象傳遞回來,然后在onActivityResult方法中去處理更新UI。但是這種方法的弊端在于只有在返回到上一個(gè)界面時(shí)才會(huì)將事件傳遞回來,不能達(dá)到立即更新的目的。

線程模式

上面說過,只有使用注解@Subscribe(threadMode = ThreadMode.MAIN)標(biāo)明的方法才是訂閱者需要處理邏輯的地方。那這個(gè)threadMode是什么呢?它其實(shí)是指定訂閱者處理事件的線程,EventBus總共有四種線程模式,分別是:

  • ThreadMode.MAIN:表示無論事件是在哪個(gè)線程發(fā)布出來的,該事件訂閱方法onEvent都會(huì)在UI線程中執(zhí)行,這個(gè)在Android中是非常有用的,因?yàn)樵贏ndroid中只能在UI線程中更新UI,所有在此模式下的方法是不能執(zhí)行耗時(shí)操作的。

  • ThreadMode.POSTING:表示事件在哪個(gè)線程中發(fā)布出來的,事件訂閱函數(shù)onEvent就會(huì)在這個(gè)線程中運(yùn)行,也就是說發(fā)布事件和接收事件在同一個(gè)線程。使用這個(gè)方法時(shí),在onEvent方法中不能執(zhí)行耗時(shí)操作,如果執(zhí)行耗時(shí)操作容易導(dǎo)致事件分發(fā)延遲。

  • ThreadMode.BACKGROUND:表示如果事件在UI線程中發(fā)布出來的,那么訂閱函數(shù)onEvent就會(huì)在子線程中運(yùn)行,如果事件本來就是在子線程中發(fā)布出來的,那么訂閱函數(shù)直接在該子線程中執(zhí)行。

  • ThreadMode.ASYNC:使用這個(gè)模式的訂閱函數(shù),那么無論事件在哪個(gè)線程發(fā)布,都會(huì)創(chuàng)建新的子線程來執(zhí)行訂閱函數(shù)。

ASYNC相比前三者不同的地方是可以處理耗時(shí)的操作,其采用了線程池,且是一個(gè)異步執(zhí)行的過程,即事件的訂閱者可以立即得到執(zhí)行。雖然BACKGROUND也采用了線程池,但它每次只能執(zhí)行一個(gè)任務(wù),就是不會(huì)異步執(zhí)行。所以一般是更新UI的事件,就使用ThreadMode.MAIN,需要請(qǐng)求網(wǎng)絡(luò)的事件就使用ThreadMode.AYSNC,。

final class BackgroundPoster implements Runnable {

    private final PendingPostQueue queue;
    private final EventBus eventBus;

    private volatile boolean executorRunning;

    BackgroundPoster(EventBus eventBus) {
        this.eventBus = eventBus;
        queue = new PendingPostQueue();
    }

    public void enqueue(Subscription subscription, Object event) {
        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
        synchronized (this) {
            queue.enqueue(pendingPost);
            if (!executorRunning) {
                executorRunning = true;
                eventBus.getExecutorService().execute(this);
            }
        }
    }

    @Override
    public void run() {
        try {
            try {
                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);
                }
            } catch (InterruptedException e) {
                Log.w("Event", Thread.currentThread().getName() + " was interruppted", e);
            }
        } finally {
            executorRunning = false;
        }
    }

}

backgroundPoster通過enqueue方法,將當(dāng)前的訂閱者添加至隊(duì)列PendingPostQueue
中,是否立即執(zhí)行,則需要判斷當(dāng)前隊(duì)列是否還有正在執(zhí)行的任務(wù),若沒有的話,則立即執(zhí)行,若還有執(zhí)行任務(wù)的話,則只進(jìn)行隊(duì)列的添加。這樣,保證了后臺(tái)任務(wù)永遠(yuǎn)只會(huì)在一個(gè)線程執(zhí)行。

class AsyncPoster implements Runnable {

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

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

}

AsyncPoster是直接通過線程池調(diào)用執(zhí)行,相比BackgroundPoster執(zhí)行來說,則沒有等待的過程。

final class HandlerPoster extends Handler {

    private final PendingPostQueue queue;
    private final int maxMillisInsideHandleMessage;
    private final EventBus eventBus;
    private boolean handlerActive;

    HandlerPoster(EventBus eventBus, Looper looper, int maxMillisInsideHandleMessage) {
        super(looper);
        this.eventBus = eventBus;
        this.maxMillisInsideHandleMessage = maxMillisInsideHandleMessage;
        queue = new PendingPostQueue();
    }

    void enqueue(Subscription subscription, Object event) {
        PendingPost pendingPost = PendingPost.obtainPendingPost(subscription, event);
        synchronized (this) {
            queue.enqueue(pendingPost);
            if (!handlerActive) {
                handlerActive = true;
                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();
                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;
        }
    }
}

HandlerPoster是我們?cè)偈煜げ贿^的Handler消息機(jī)制了,它會(huì)將每個(gè)訂閱事件傳遞到UI線程中進(jìn)行處理。這里就不多說了。

源碼分析

EventBus的事件總線

在EventBus中,真正的訂閱對(duì)象是SubscriberMethod,包含了相應(yīng)的Method類,以及事件參數(shù)類型Class<?> eventType,其他就是線程,優(yōu)先級(jí),是否Sticky信息。

public class SubscriberMethod {
    final Method method;
    final ThreadMode threadMode;
    final Class<?> eventType;
    final int priority;
    final boolean sticky;
}

Sticky=true表示等到回到事件訂閱的界面時(shí)才開始傳遞消,而不是一post就開始傳遞,什么時(shí)候使用sticy呢?當(dāng)你希望你的事件不被馬上處理的時(shí)候。

事件總線,一般都對(duì)應(yīng)著一個(gè)集合,這個(gè)集合中的對(duì)象就是訂閱的事件,而EventBus中使用的是:

private final Map<Class<?>, CopyOnWriteArrayList<Subscription>> subscriptionsByEventType;

subscriptionsByEventType = new HashMap<>();

這里的Map集合采用的是一個(gè)HashMap集合,map的key對(duì)應(yīng)就是之前SubscriberMethod中的eventType, value則對(duì)應(yīng)著一個(gè)線程安全的List,List中存放的是包含訂閱對(duì)象Object及相應(yīng)訂閱方法SubscriberMethodSubscription類:

final class Subscription {
    final Object subscriber;
    final SubscriberMethod subscriberMethod;
}

所以,EventBus內(nèi)部是用了一個(gè)線程安全的List集合來維持所有的訂閱者,即事件總線集合。而注冊(cè)訂閱和取消注冊(cè)訂閱就是對(duì)這個(gè)List集合進(jìn)行增刪的過程。

注冊(cè)訂閱和取消注冊(cè)訂閱

/**
 * Registers the given subscriber to receive events. Subscribers must call {@link #unregister(Object)} once they
 * are no longer interested in receiving events.
 * <p/>
 * Subscribers have event handling methods that must be annotated by {@link Subscribe}.
 * The {@link Subscribe} annotation also allows configuration like {@link
 * ThreadMode} and priority.
 */
public void register(Object subscriber) {
    Class<?> subscriberClass = subscriber.getClass();
    List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);
    synchronized (this) {
        for (SubscriberMethod subscriberMethod : subscriberMethods) {
            subscribe(subscriber, subscriberMethod);
        }
    }
}

首先EventBus會(huì)根據(jù)訂閱類的Class去這個(gè)類中查找處理訂閱事件的方法,SubscriberMethodFinder這個(gè)類看名字我們就知道它是干什么用的了,等會(huì)在細(xì)說。下面我們看看subscribe方法:

// Must be called in synchronized block
private void subscribe(Object subscriber, SubscriberMethod subscriberMethod) {
    Class<?> eventType = subscriberMethod.eventType;
    Subscription newSubscription = new Subscription(subscriber, subscriberMethod);
    CopyOnWriteArrayList<Subscription> subscriptions = subscriptionsByEventType.get(eventType);
    if (subscriptions == null) {
        subscriptions = new CopyOnWriteArrayList<>();
        subscriptionsByEventType.put(eventType, subscriptions);
    } else {
        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;
        }
    }

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

從這個(gè)方法我們可以看到每一個(gè)eventType 都對(duì)應(yīng)一個(gè)CopyOnWriteArrayList<Subscription>,不過這樣讀源碼太不好理解了,結(jié)合我Demo中的例子來說,MainActivity中的subscriberMethod就是onEvent(NewsModel newsModel)方法,所以eventType就是NewsModel類,而subscriber就是MainActivity的實(shí)例,他倆組合成了一個(gè)訂閱者Subscription,然后把eventType作為key,訂閱了NewsModel這類事件的訂閱者的集合作為value存儲(chǔ)在一個(gè)HashMap中,最后根據(jù)優(yōu)先級(jí)prioritynewSubscription這個(gè)最新的訂閱者添加到訂閱列表中。也就是說在一個(gè)Activity頁面類,我們可以有多個(gè)SubscriberMethod,就像下面這樣:

@Subscribe(threadMode = ThreadMode.MAIN)
public void onEvent(NewsModel newsModel) {
    newsList.set(clickPosition, newsModel);
    mAdapter.notifyItemChanged(clickPosition);
}

@Subscribe(threadMode = ThreadMode.BACKGROUND)
public void onEvent1(User user) {
    
}

@Subscribe(threadMode = ThreadMode.ASYNC)
public void onEvent2(Person person) {
    
}

取消注冊(cè)訂閱則是將該事件類型的訂閱者從事件總線集合中移除:

/** Unregisters the given subscriber from all event classes. */
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 {
        Log.w(TAG, "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--;
            }
        }
    }
}

事件消費(fèi)和發(fā)布事件

這里通過EventBus的post(Object event)方法,進(jìn)行事件的發(fā)出。緊接著EventBus的總線List中找出訂閱了這個(gè)event的方法Subscription,然后根據(jù)method指定的不同線程信息,將這個(gè)方法的調(diào)用,放置在相應(yīng)線程中調(diào)用,看看EventBus中的post方法:

/** Posts the given event to the event bus. */
public void post(Object event) {
    PostingThreadState postingState = currentPostingThreadState.get();
    List<Object> eventQueue = postingState.eventQueue;
    eventQueue.add(event);

    if (!postingState.isPosting) {
        postingState.isMainThread = Looper.getMainLooper() == Looper.myLooper();
        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;
        }
    }
}

順著postSingleEvent方法看下來,我們會(huì)發(fā)現(xiàn)最終將會(huì)調(diào)用方法postToSubscription將發(fā)布事件傳遞到訂閱者這里來。

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

然后通過反射調(diào)用訂閱者的消費(fèi)該事件的方法。也許當(dāng)你看上面postToSubscription方法時(shí)會(huì)發(fā)現(xiàn)threadModeMAIN、BACKGROUND或者ASYNC時(shí),都有用各自縣城的poster調(diào)用enqueue方法,其實(shí)仔細(xì)一層層看下去你會(huì)發(fā)現(xiàn)到最后還是調(diào)用了invokeSubscriber方法。所以,最終的消費(fèi)方法調(diào)用就是這行代碼:

subscription.subscriberMethod.method.invoke(subscription.subscriber, event);

訂閱者已經(jīng)被封裝地非常完美,這樣,我們?cè)谑褂貌煌木€程調(diào)度策略就很簡(jiǎn)單了,隨意指定一個(gè)ThreadMode,即可在指定線程中調(diào)用。

看到這里,也許你會(huì)發(fā)現(xiàn)有個(gè)很大的疑問:SubscriberMethod信息是怎么生成的?其實(shí)就是加了注解@Subscribe的方法。我們可以通過在運(yùn)行時(shí)采用反射的方法,獲取相應(yīng)添加了注解的方法,再封裝成為SubscriberMethod。也就是前面我提到的SubscriberMethodFinder類來獲取消費(fèi)事件方法??丛创a,真正在運(yùn)行時(shí)利用反射去查找SubscriberMethod的代碼:

private void findUsingReflectionInSingleClass(FindState findState) {
    Method[] methods;
    try {
        // This is faster than getMethods, especially when subscribers are fat classes like Activities
        methods = findState.clazz.getDeclaredMethods();
    } catch (Throwable th) {
        // Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149
        methods = findState.clazz.getMethods();
        findState.skipSuperClasses = true;
    }
    for (Method method : methods) {
        int modifiers = method.getModifiers();
        if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {
            Class<?>[] parameterTypes = method.getParameterTypes();
            if (parameterTypes.length == 1) {
                Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);
                if (subscribeAnnotation != null) {
                    Class<?> eventType = parameterTypes[0];
                    if (findState.checkAdd(method, eventType)) {
                        ThreadMode threadMode = subscribeAnnotation.threadMode();
                        findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,
                                subscribeAnnotation.priority(), subscribeAnnotation.sticky()));
                    }
                }
            } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
                String methodName = method.getDeclaringClass().getName() + "." + method.getName();
                throw new EventBusException("@Subscribe method " + methodName +
                        "must have exactly 1 parameter but has " + parameterTypes.length);
            }
        } else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {
            String methodName = method.getDeclaringClass().getName() + "." + method.getName();
            throw new EventBusException(methodName +
                    " is a illegal @Subscribe method: must be public, non-static, and non-abstract");
        }
    }
}

對(duì)于我們開發(fā)者來說,使用反射帶來的性能消耗,是必須要考慮到的。在3.0的版本,EventBus加入了apt處理的邏輯,有個(gè)Subscriber Index的介紹,主要是通過Apt在編譯期根據(jù)注解直接生成相應(yīng)的信息,來避免在運(yùn)行時(shí)通過反射來獲取。使用方法如下:

buildscript {
    dependencies {
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
    }
}
 
apply plugin: 'com.neenbedankt.android-apt'
 
dependencies {
    compile 'org.greenrobot:eventbus:3.0.0'
    apt 'org.greenrobot:eventbus-annotation-processor:3.0.1'
}
 
apt {
    arguments {
        eventBusIndex "com.example.myapp.MyEventBusIndex"
    }
}
11211.png

上面的配置除了第一條是在project的build.gradle中添加,其余的都是在moudle中的build.gradle中添加,具體的大家可以看我的Demo中配置方法。當(dāng)我們?cè)俅尉幾g之后,系統(tǒng)會(huì)自動(dòng)為我們生成MyEventBusIndex這個(gè)類,然后我們將它設(shè)置給EventBus:

EventBus eventBus = EventBus.builder().addIndex(new MyEventBusIndex()).build();

或者是這樣:

EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();
// Now the default instance uses the given index. Use it like this:
EventBus eventBus = EventBus.getDefault();

MyEventBusIndex這個(gè)生成的索引類實(shí)際上會(huì)實(shí)現(xiàn)SubscriberInfoIndex這個(gè)接口:

public interface SubscriberInfoIndex {
    SubscriberInfo getSubscriberInfo(Class<?> subscriberClass);
}

從接口中可以看出,這個(gè)Index類只提供了一個(gè)方法getSubsriberInfo,這個(gè)方法需要我們傳入訂閱者所在的class類,然后獲得一個(gè)SubscriberInfo的類,其類結(jié)構(gòu)如下:

public interface SubscriberInfo {
    Class<?> getSubscriberClass();

    SubscriberMethod[] getSubscriberMethods();

    SubscriberInfo getSuperSubscriberInfo();

    boolean shouldCheckSuperclass();
}

從接口中的方法即可獲取所需的所有SubscriberMethod信息。而這只是一個(gè)獲取單個(gè)類的方法,而apt生成的MyEventBusIndex中,會(huì)將所有的這些class及SubscriberInfo保存在靜態(tài)變量hashMap結(jié)構(gòu),這樣就達(dá)到了避免運(yùn)行期反射獲取生成訂閱方法的性能問題。而變成另外一個(gè)流程:訂閱類在注冊(cè)的時(shí)候,直接通過HashMap中的訂閱類,獲取到SubscriberInfo,進(jìn)而獲取到所有的SubscerberMethod,并封裝為Subscription,被添加到事件總線中。這樣,在引入了apt之后,EventBus的性能問題就得以解決了。

說完了性能問題,還得接著說SubscriberMethodFinder這個(gè)類,它會(huì)根據(jù)訂閱類class信息,來獲取SubscriberMethod,EventBus提供了兩種方式進(jìn)行獲?。?/p>

if (ignoreGeneratedIndex) {
    subscriberMethods = findUsingReflection(subscriberClass);
} else {
    subscriberMethods = findUsingInfo(subscriberClass);
}
  • 如果不使用生成的索引來查找,就采用findUsingReflection(subscriberClass)方法進(jìn)行反射來獲取。
  • 使用生成的索引來查找,就采用findUsingInfo(subscriberClass)方法在apt中進(jìn)行查找獲取。

而這個(gè)ignoreGeneratedIndex默認(rèn)是false的,如果沒有在項(xiàng)目中引入apt則將強(qiáng)制使用反射方式查找。

/** Forces the use of reflection even if there's a generated index (default: false). */
public EventBusBuilder ignoreGeneratedIndex(boolean ignoreGeneratedIndex) {
    this.ignoreGeneratedIndex = ignoreGeneratedIndex;
    return this;
}

在查找SubscriberMethod時(shí),EventBus封裝了一個(gè)類FindState對(duì)查找的狀態(tài)值處理,結(jié)構(gòu)如下:

static class FindState {
    final List<SubscriberMethod> subscriberMethods = new ArrayList<>();
    final Map<Class, Object> anyMethodByEventType = new HashMap<>();
    final Map<String, Class> subscriberClassByMethodKey = new HashMap<>();
    final StringBuilder methodKeyBuilder = new StringBuilder(128);

    Class<?> subscriberClass;
    Class<?> clazz;
    boolean skipSuperClasses;
    SubscriberInfo subscriberInfo;
}

其中有訂閱類subscriberClass,事件對(duì)象clazz,以及查找的結(jié)果subscriberMethods、subscriberInfo等,另外,還有一個(gè)判斷的標(biāo)志量skipSuperClasses,用來標(biāo)記是否需要進(jìn)行父類的查找。

無論是哪種查找方法,步驟都是一樣的,于是FindState將這些通用的步驟封裝起來,大致是這四步:

  • initForSubscriber(Class<?> subscriberClass) 初始化訂閱類
  • checkAdd(Method method, Class<?> eventType) 檢查方法合法性
  • checkAddWithMethodSignature(Method method, Class<?> eventType) 檢查方法合法性
  • moveToSuperclass() 是否需要查看父類中的訂閱方法

除了對(duì)查找結(jié)果的封裝,F(xiàn)indState還使用了緩存池:

private static final int POOL_SIZE = 4;
private static final FindState[] FIND_STATE_POOL = new FindState[POOL_SIZE];

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

EventBus指定了FindState的緩存池的大小為4,并使用一維的靜態(tài)數(shù)組,所以這里需要注意線程同步的問題。使用同步代碼塊從緩存池中取FindState,同樣,通過上面兩種方式獲取SubscriberMethod時(shí),也使用同步代碼塊將FindState放入了緩存池中。

private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {
    List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods);
    findState.recycle();
    synchronized (FIND_STATE_POOL) {
        for (int i = 0; i < POOL_SIZE; i++) {
            if (FIND_STATE_POOL[i] == null) {
                FIND_STATE_POOL[i] = findState;
                break;
            }
        }
    }
    return subscriberMethods;
}

總結(jié)

分析了EventBus的源碼后,相信你會(huì)對(duì)它的工作方式更加清晰,簡(jiǎn)而言之,就是“發(fā)布者發(fā)布事件,訂閱者通過反射的方式根據(jù)發(fā)布事件的class類型查找SubscriberMethod,然后通過這個(gè)類來invoke訂閱類中處理對(duì)應(yīng)事件的方法”。它的引入大大簡(jiǎn)化了開發(fā)者需要做的工作,而且將訂閱者和發(fā)布者完全解耦,so趕緊用起來吧。

演示的Demo下載地址

參考文檔

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容