EventBus源碼詳解(三):高級使用

寫在前面

EventBus是一個Android平臺上基于事件發(fā)布和訂閱的輕量級框架,可以對發(fā)布者和訂閱者解耦,并簡化Android的事件傳遞。

本文是關(guān)于EventBus系列文章的第三篇,相關(guān)文章有:

這篇是EventBus使用篇最后一篇,前面基本把EventBus的功能都介紹了,但還有一個很重要的功能:EventBus的創(chuàng)建和配置,這也是今天的主要介紹的。


正文

從前面的文章知道,EventBus不是單例的,它是可以無限創(chuàng)建實例的,也就是說我們可以自己創(chuàng)建適合自己需求的EventBus實例。EventBus的創(chuàng)建是使用了建造者模式,提供了一個EventBusBuilder給我們用鏈?zhǔn)秸{(diào)用的方式實現(xiàn)可自由搭配所要配置的功能。

好,先來看看EventBusBuilder給我們提供了哪些配置項(注意,下面的屬性為了讀者能更好理解,我調(diào)了下聲明順序和加了空行):

public class EventBusBuilder {
    boolean logSubscriberExceptions = true;        // 在訂閱方法中拋出異常時, 是否打印日志, 默認(rèn)為true
    boolean sendSubscriberExceptionEvent = true;   // 在非訂閱SubscriberExceptionEvent事件方法中拋出異常時, 是否發(fā)送SubscriberExceptionEvent事件, 默認(rèn)為true
    boolean throwSubscriberException;              // 在非訂閱SubscriberExceptionEvent事件方法中拋出異常時, 是否拋出EventBusException異常, 默認(rèn)為false

    boolean logNoSubscriberMessages = true;        // 沒找到事件訂閱方法, 是否打印日志, 默認(rèn)為true
    boolean sendNoSubscriberEvent = true;          // 沒找到事件訂閱方法, 是否發(fā)送NoSubscriberEvent事件, 默認(rèn)為true

    boolean eventInheritance = true;               // 發(fā)送子事件, 是否發(fā)送父事件, 默認(rèn)為true

    /* 下面三個參數(shù)用在查找訂閱方法 */
    boolean ignoreGeneratedIndex;                  // 是否用反射查找訂閱方法, 默認(rèn)為false
    boolean strictMethodVerification;              // 非注解生成索引時, 嚴(yán)格方法驗證: 當(dāng)方法不符合格式(public, non-abstract, non-static, non-briage, 只有一個參數(shù))時, 是否拋出EventBusException異常, 默認(rèn)為false
    List<SubscriberInfoIndex> subscriberInfoIndexes; // 注解生成的索引
    
    /* 默認(rèn)線程數(shù)量不定的緩存線程池 */
    private final static ExecutorService DEFAULT_EXECUTOR_SERVICE = Executors.newCachedThreadPool(); 
    ExecutorService executorService = DEFAULT_EXECUTOR_SERVICE;

    List<Class<?>> skipMethodVerificationForClasses; // 這貨根本沒用, 沒在源碼中看到它使用...

    EventBusBuilder() {
    }

    ...
}

上面注釋已經(jīng)講得非常詳細(xì)了,上面的配置項在EventBusBuilder里除了subscriberInfoIndexes以外,都有一個同名方法來改變配置,而subscriberInfoIndexes的配置方法是addIndex

/** Adds an index generated by EventBus' annotation preprocessor. */
public EventBusBuilder addIndex(SubscriberInfoIndex index) {
    if(subscriberInfoIndexes == null) {
        subscriberInfoIndexes = new ArrayList<>();
    }
    subscriberInfoIndexes.add(index);
    return this;
}

哈哈,如果看了我第二遍文章的應(yīng)該知道這貨了,它是在生成索引時,要手動給EventBus配置的,來回顧下:

public class MyApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        // 加載索引,添加到默認(rèn)配置的EventBus
        EventBus.builder().addIndex(new SampleBusIndex()).installDefaultEventBus();
    }
}

EventBusBuilder的配置項一般保持默認(rèn)配置就可以了,下面重點挑幾個比較重要的來舉例說明下。

發(fā)送異常事件

EventBus在所訂閱的方法拋出異常時,是默認(rèn)會打印日志并發(fā)出異常事件SubscriberExceptionEvent(當(dāng)然,你沒在訂閱者里訂閱SubscriberExceptionEvent肯定會收不到)。下面來試驗下:

public class SubscriberExceptionsActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_subscriber_exceptions);
        EventBus.getDefault().register(this);
    }

    public void post(View view) {
        EventBus.getDefault().post(new MsgEvent(""));
    }

    @Subscribe
    public void onMsgEvent(MsgEvent event) {
        if (TextUtils.isEmpty(event.getMsg())) {
            throw new IllegalArgumentException("Event msg can not be empty");
        }
        Log.i("TEST", "onMsgEvent --> " + event.getMsg());
    }

    @Subscribe
    public void onSubscriberExceptionEvent(SubscriberExceptionEvent event) {
        // 訂閱方法拋出異常時, 會到這里
        Log.i("TEST", "onSubscriberExceptionEvent --> " + event.throwable.getMessage());
    }
}

上面代碼在Activity里訂閱了MsgEventSubscriberExceptionEvent,然后點擊按鈕post一個msg為空的MsgEvent事件,而在MsgEvent的訂閱方法中,當(dāng)msg為空時拋出異常。運行程序,打開logcat可以看到:

E/EventBus: Could not dispatch event: class com.leo.eventbus.sample3.MsgEvent to subscribing class class com.leo.eventbus.sample3.SubscriberExceptionsActivity
...
I/TEST: onSubscriberExceptionEvent --> Event msg can not be empty

從上面日志可以看到,在訂閱方法中拋出異常時,即會打log,同時發(fā)送SubscriberExceptionEvent事件。如果你把throwSubscriberException配置為true,就會直接閃退了。

發(fā)送找不到訂閱方法事件

“發(fā)送找不到訂閱方法事件”聽起來有點拗口... 它的意思是當(dāng)在訂閱者post個事件時,而沒訂閱該事件的訂閱方法時,就會發(fā)送NoSubscriberEvent事件(同樣的,如果沒在訂閱者里訂閱NoSubscriberEvent肯定也收不到)。驗證下:

public class NoSubscriberActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_no_subscriber);
        EventBus.getDefault().register(this);
    }

    public void post(View view) {
        EventBus.getDefault().post(new MsgEvent("Hello World"));
    }

    @Subscribe
    public void onNoSubscriberEvent(NoSubscriberEvent event) {
        // 在訂閱者中沒找到發(fā)送的訂閱事件, 會到這里
        Log.i("TEST", "onNoSubscriberEvent --> " + ((MsgEvent) event.originalEvent).getMsg());
    }
}

上面代碼在Activity里只訂閱了NoSubscriberEvent事件,然后點擊按鈕post一個沒訂閱的MsgEvent事件,運行程序,打開logcat:

D/EventBus: No subscribers registered for event class com.leo.eventbus.sample3.MsgEvent
I/TEST: onNoSubscriberEvent --> Hello World
事件繼承

所謂事件繼承即當(dāng)一個事件類繼承于另一個事件,當(dāng)發(fā)送該子事件時,如果訂閱了父事件,也將會發(fā)送父事件。事件繼承的開關(guān)是eventInheritance,一切都沒有代碼來的直觀:

public class EventInheritanceActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_event_inheritance);
        EventBus.getDefault().register(this);
        EventBusManager.getEventBus(EventBusManager.NO_EVENT_INHERITANCE_TYPE).register(this);
    }

    public void post(View view) {
        EventBus.getDefault().post(new ErrMsgEvent("I am a error msg"));
    }

    @Subscribe
    public void onMsgEvent(MsgEvent event) {
        Log.i("TEST", "onMsgEvent --> " + event.getMsg());
    }

    @Subscribe
    public void onErrMsgEvent(ErrMsgEvent event) {
        Log.i("TEST", "onErrMsgEvent --> " + event.getMsg());
    }
}

上面代碼在Activity里同時訂閱了MsgEventErrMsgEvent事件,然后點擊按鈕post一個ErrMsgEvent事件。ErrMsgEvent是繼承于MsgEvent的類:

public class ErrMsgEvent extends MsgEvent {
    ErrMsgEvent(String errMsg) {
        super(errMsg);
    }
}

再次運行程序,查看logcat:

I/TEST: onErrMsgEvent --> I am a error msg
I/TEST: onMsgEvent --> I am a error msg

可以看到,發(fā)送了子事件ErrMsgEvent,同時也會發(fā)送父事件MsgEvent。我覺得這個功能會對代碼的可讀性和理解在一定程度上造成影響。例如我訂閱了一個父事件來更新UI,使我的賬號的狀態(tài)改為“在線”狀態(tài),而有個子事件是把賬號改成“離線”狀態(tài),當(dāng)你post子事件想把狀態(tài)改成“離線”時,會發(fā)現(xiàn)無法成功。當(dāng)然,也沒人這么傻,如此使用,這里只是舉例。我的意思是,當(dāng)你還不很理解“事件繼承”的用途時,最好先把它關(guān)掉,即把eventInheritance設(shè)置為false。

其他配置

其他配置沒啥好說了,保持默認(rèn)就可以。需要提醒注意的是:

1. 訂閱方法格式
訂閱方法一定滿足以下條件:

  • public修飾
  • 只有一個事件類參數(shù)
  • 非靜態(tài)方法
  • 非抽象方法
  • 非橋接方法

如果你使用了注解,在不滿足上面條件時,會在編譯期就會報錯,無法編譯,因為在索引生成時,apt會檢測上面前3個條件是否滿足。如果沒使用注解,是不會去檢測的,也就相當(dāng)于你沒訂閱該方法,那么就會收不到所訂閱的事件了。當(dāng)然,前面文章也說明了使用索引的優(yōu)勢,效率高出幾倍,我們沒理由不用注解,也就不怕訂閱不滿足條件的方法了。

2. 線程池
EventBus的默認(rèn)線程池是數(shù)量不定的緩存線程池,也就是說,如果你稍不注意,過多地新開線程來發(fā)送事件的話,就有可能造成OOM。幸好的是,EventBus可以讓我們自己實現(xiàn)線程池。

EventBus實例配置

上面關(guān)于EventBusBuilder的介紹已經(jīng)講解得非常詳細(xì)了,相信大家能根據(jù)自己的需求來創(chuàng)建EventBus的實例。創(chuàng)建EventBus實例的入口是EventBus#builder().build()

/* EventBus */
public static EventBusBuilder builder() {
    return new EventBusBuilder();
}

可以看到,它是直接new個EventBusBuilder出來,然后就可以配置我們所需要的功能了。EventBus實例可以無限創(chuàng)建,但我們不可能這么做,我們只需創(chuàng)建我們需要的就可以了。我們可以使用簡單工廠方法來選擇創(chuàng)建我們需要的EventBus實例:

public class EventBusManager {

    public static final int NO_EVENT_INHERITANCE_TYPE = 1;
    public static final int FIXED_THREAD_TYPE = 2;

    /**
     * 事件非繼承訂閱EventBus
     */
    private static EventBus noEventInheritanceEventBus;

    /**
     * 固定線程池EventBus
     */
    private static EventBus fixedThreadEventBus;

    public static EventBus getEventBus(int type) {
        EventBus eventBus = EventBus.getDefault();
        switch (type) {
            case NO_EVENT_INHERITANCE_TYPE:
                if (noEventInheritanceEventBus == null) {
                    // lazy
                    synchronized (EventBusManager.class) {
                        if (noEventInheritanceEventBus == null) {
                            noEventInheritanceEventBus = EventBus.builder()
                                    .eventInheritance(false)
                                    .build();
                        }
                    }
                }
                eventBus = noEventInheritanceEventBus;
                break;

            case FIXED_THREAD_TYPE:
                if (fixedThreadEventBus == null) {
                    // lazy
                    synchronized (EventBusManager.class) {
                        if (fixedThreadEventBus == null) {
                            fixedThreadEventBus = EventBus.builder()
                                    .executorService(Executors.newFixedThreadPool(20))
                                    .build();
                        }
                    }
                    eventBus = fixedThreadEventBus;
                }
                break;
        }

        return eventBus;
    }
}

最后提醒注意:每個EventBus的實例都是獨立的,也就是說,每個EventBus post的事件,只有使用該EventBus注冊的訂閱者才能接受到,其他EventBus注冊的是無法接收的。


寫在最后

終于寫完EventBus的使用了,想不到如此輕量級的開源框架需要用三篇文章來講解,看來要講清楚各個細(xì)節(jié)真的不容易。三篇文章基本涵蓋了EventBus的全部內(nèi)容了,后續(xù)文章將會從源碼來分析EventBus。


demo

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

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

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