Android開(kāi)源庫(kù)——EventBus高級(jí)用法

前言

上一篇寫(xiě)了EventBus的基本用法胚股,傳送門(mén):
Android開(kāi)源庫(kù)——EventBus使用教程
在上一篇中只是簡(jiǎn)單展示了EventBus的基本用法捻勉,其實(shí)還有很多好玩和強(qiáng)大的功能压固,那么在本篇將一步一步地去探索EventBus那些好玩又高級(jí)的用法。主要有

  • Thread Mode(線程模式)
  • Configuration(配置)
  • Sticky Events(粘性事件)
  • Priorities and Event Cancellation(優(yōu)先級(jí)和取消事件傳遞)
  • Subscriber Index(訂閱者索引)
  • AsyncExecutor(異步執(zhí)行器)

話不多說(shuō)糜值,馬上開(kāi)始

高級(jí)用法

Thread Mode

還記得上一篇中个榕,訂閱方法的@Subscribe中包含的信息

@Subscribe(threadMode = ThreadMode.MAIN)
public void onUpdateUIEvent(UpdateUIEvent updateUIEvent) {
    mTextView.setText("陳奕迅只有一個(gè)");

通過(guò)指定threadMode來(lái)確定訂閱方法在哪個(gè)線程執(zhí)行凯旭,ThreadMode.MAIN只是其中一種概耻,看一下其他的

public enum ThreadMode {
    /**
     * Subscriber will be called in the same thread, which is posting the event. This is the default. Event delivery
     * implies the least overhead because it avoids thread switching completely. Thus this is the recommended mode for
     * simple tasks that are known to complete is a very short time without requiring the main thread. Event handlers
     * using this mode must return quickly to avoid blocking the posting thread, which may be the main thread.
     */
    POSTING,

    /**
     * Subscriber will be called in Android's main thread (sometimes referred to as UI thread). If the posting thread is
     * the main thread, event handler methods will be called directly. Event handlers using this mode must return
     * quickly to avoid blocking the main thread.
     */
    MAIN,

    /**
     * Subscriber will be called in a background thread. If posting thread is not the main thread, event handler methods
     * will be called directly in the posting thread. If the posting thread is the main thread, EventBus uses a single
     * background thread, that will deliver all its events sequentially. Event handlers using this mode should try to
     * return quickly to avoid blocking the background thread.
     */
    BACKGROUND,

    /**
     * Event handler methods are called in a separate thread. This is always independent from the posting thread and the
     * main thread. Posting events never wait for event handler methods using this mode. Event handler methods should
     * use this mode if their execution might take some time, e.g. for network access. Avoid triggering a large number
     * of long running asynchronous handler methods at the same time to limit the number of concurrent threads. EventBus
     * uses a thread pool to efficiently reuse threads from completed asynchronous event handler notifications.
     */
    ASYNC
}

注釋一目了然,總結(jié)一下四種模式的應(yīng)用場(chǎng)景

  • POSTING:事件發(fā)布在什么線程罐呼,就在什么線程訂閱鞠柄。
  • MAIN:無(wú)論事件在什么線程發(fā)布,都在主線程訂閱嫉柴。
  • BACKGROUND:如果發(fā)布的線程不是主線程厌杜,則在該線程訂閱,如果是主線程计螺,則使用一個(gè)單獨(dú)的后臺(tái)線程訂閱夯尽。
  • ASYNC:用線程池線程訂閱。

比如

@Subscribe(threadMode = ThreadMode.BACKGROUND)
public void onUpdateUIEvent(UpdateUIEvent updateUIEvent) {
    mTextView.setText("陳奕迅只有一個(gè)");
}

這樣我們就是在后臺(tái)中訂閱事件登馒,但由于更新UI只能在主線程中匙握,所以會(huì)出現(xiàn)以下異常

Could not dispatch event: class com.charmingwong.androidtest.UpdateUIEvent to subscribing class class com.charmingwong.androidtest.MainActivity
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

Configuration

我們獲取EventBus實(shí)例的默認(rèn)方式是

EventBus.getDefault().register(this);

這種方式的獲取到的EventBus的屬性都是默認(rèn)的,有時(shí)候并不能滿足我們的要求陈轿,這時(shí)候我們可以通過(guò)EventBusBuilder來(lái)個(gè)性化配置EventBus的屬性

EventBus eventBus = EventBus.builder().eventInheritance(true)
    .ignoreGeneratedIndex(false)
    .logNoSubscriberMessages(true)
    .logSubscriberExceptions(false)
    .sendNoSubscriberEvent(true)
    .sendSubscriberExceptionEvent(true)
    .throwSubscriberException(true)
    .strictMethodVerification(true)
    .build();
eventBus.register(this);

使用類(lèi)似于以上這種Builder模式圈纺,來(lái)達(dá)到個(gè)性化配置的效果秦忿,builder()返回的是EventBusBuilder,來(lái)看一下EventBusBuilder各項(xiàng)配置的含義

// 創(chuàng)建默認(rèn)的EventBus對(duì)象赠堵,相當(dāng)于EventBus.getDefault()小渊。
EventBus installDefaultEventBus():
// 添加由EventBus“注釋預(yù)處理器生成的索引
EventBuilder addIndex(SubscriberInfoIndex index):
// 默認(rèn)情況下,EventBus認(rèn)為事件類(lèi)有層次結(jié)構(gòu)(訂戶超類(lèi)將被通知)
EventBuilder eventInheritance(boolean eventInheritance):
// 定義一個(gè)線程池用于處理后臺(tái)線程和異步線程分發(fā)事件
EventBuilder executorService(java.util.concurrent.ExecutorService executorService):
// 設(shè)置忽略訂閱索引茫叭,即使事件已被設(shè)置索引,默認(rèn)為false
EventBuilder ignoreGeneratedIndex(boolean ignoreGeneratedIndex):
// 打印沒(méi)有訂閱消息半等,默認(rèn)為true
EventBuilder logNoSubscriberMessages(boolean logNoSubscriberMessages):
// 打印訂閱異常揍愁,默認(rèn)true
EventBuilder logSubscriberExceptions(boolean logSubscriberExceptions):
// 設(shè)置發(fā)送的的事件在沒(méi)有訂閱者的情況時(shí),EventBus是否保持靜默杀饵,默認(rèn)true
EventBuilder sendNoSubscriberEvent(boolean sendNoSubscriberEvent):
// 發(fā)送分發(fā)事件的異常莽囤,默認(rèn)true
EventBuilder sendSubscriberExceptionEvent(boolean sendSubscriberExceptionEvent):
// 在3.0以前,接收處理事件的方法名以onEvent開(kāi)頭切距,方法名稱(chēng)驗(yàn)證避免不是以此開(kāi)頭朽缎,啟用嚴(yán)格的方法驗(yàn)證(默認(rèn):false)
EventBuilder strictMethodVerification(java.lang.Class<?> clazz)
// 如果onEvent***方法出現(xiàn)異常,是否將此異常分發(fā)給訂閱者(默認(rèn):false)
EventBuilder throwSubscriberException(boolean throwSubscriberException) 

通過(guò)這種方式谜悟,我們就可以靈活地使用EventBus话肖。

Sticky Events

一般的事件發(fā)布之后,后來(lái)注冊(cè)的訂閱者無(wú)法再收到這些事件葡幸,而sticky事件發(fā)布之后最筒,EventBus會(huì)將它保存起來(lái),直到有新的同事件類(lèi)型的sticky事件發(fā)布蔚叨,才將舊的覆蓋床蜘,因此后續(xù)的訂閱者只要使用sticky模式,依然可以獲得這個(gè)sticky事件蔑水。使用方式如下:

在SecondActivity中訂閱sticky事件并注冊(cè)

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    // 注冊(cè)該頁(yè)面為訂閱者
    EventBus.getDefault().register(this);
}

@Subscribe(sticky = true)
public void onMyStickyEvent(MyStickyEvent myStickyEvent) {
    mTextView.setText("有人問(wèn)你他是不是陳奕迅");
}

在MainActivity中發(fā)布事件

@Override
public void onClick(View v) {
    // 發(fā)布黏性事件
    EventBus.getDefault().postSticky(new MyStickyEvent());
    Intent intent = new Intent(MainActivity.this, SecondActivity.class);
    startActivity(intent);
}

看一下運(yùn)行效果:


運(yùn)行效果

MainActivity啟動(dòng)在前邢锯,發(fā)布sticky事件,SecondActivity再啟動(dòng)搀别,注冊(cè)成為訂閱者之后依然能夠收到sticky事件丹擎,成功說(shuō)明sticky的作用,也驗(yàn)證了以上說(shuō)法领曼。發(fā)布sticky事件之后鸥鹉,也可以將它移除

MessageEvent stickyEvent = EventBus.getDefault().removeStickyEvent(MessageEvent.class);
// Better check that an event was actually posted before
if(stickyEvent != null) {
    // Now do something with it
}

Priorities and Event Cancellation

這里用一個(gè)例子驗(yàn)證優(yōu)先級(jí)關(guān)系

// MainActivity
@Subscribe(priority = 2)
public void onUpdateUIEvent(UpdateUIEvent updateUIEvent) {
    Log.d(TAG, "onUpdateUIEvent: priority = 2");
}

// SecondActivity
@Subscribe(priority = 1)
public void onUpdateUIEvent(UpdateUIEvent updateUIEvent) {
    Log.d(TAG, "onUpdateUIEvent: priority = 1");
}

兩個(gè)Activity中都訂閱相同的事件類(lèi)型,但優(yōu)先級(jí)不同庶骄,在SecondActivity發(fā)布事件

// 發(fā)布事件
EventBus.getDefault().post(new UpdateUIEvent());

看運(yùn)行效果毁渗,日志:

D/MainActivity: onUpdateUIEvent: priority = 2
D/SecondActivity: onUpdateUIEvent: priority = 1

再將priority調(diào)換一下

// MainActivity
@Subscribe(priority = 1)
public void onUpdateUIEvent(UpdateUIEvent updateUIEvent) {
    Log.d(TAG, "onUpdateUIEvent: priority = 1");
}

// SecondActivity
@Subscribe(priority = 2)
public void onUpdateUIEvent(UpdateUIEvent updateUIEvent) {
    Log.d(TAG, "onUpdateUIEvent: priority = 2");
}

運(yùn)行,看日志:

D/SecondActivity: onUpdateUIEvent: priority = 2
D/MainActivity: onUpdateUIEvent: priority = 1

可見(jiàn)priority決定收到事件的先后順序单刁,priority越大灸异,越先收到事件府适。先收到的訂閱者還可以攔截該事件,并取消繼續(xù)傳遞肺樟,后續(xù)優(yōu)先級(jí)低訂閱者就不會(huì)再收到該事件檐春。我們可以這樣設(shè)置取消事件繼續(xù)傳遞:

@Subscribe(priority = 2)
public void onUpdateUIEvent(UpdateUIEvent updateUIEvent) {
    Log.d(TAG, "onUpdateUIEvent: priority = 2");
    EventBus.getDefault().cancelEventDelivery(updateUIEvent);
}

運(yùn)行,看日志:

D/SecondActivity: onUpdateUIEvent: priority = 2

果然么伯,priority=1的訂閱者就沒(méi)有收到該事件疟暖,這就是EventBus的取消事件繼續(xù)傳遞功能。

Subscriber Index

看完官方文檔田柔,我們知道Subscriber Index是EventBus 3上的新技術(shù)俐巴,所以這里也建議還沒(méi)學(xué)習(xí)過(guò)EventBus的可以跳過(guò)2.X之前的版本直接學(xué)習(xí)最新版本。
關(guān)于EventBus的Subscriber Index技術(shù)的特點(diǎn)硬爆,翻譯一下官方解釋?zhuān)?/p>

It is an optional optimization to speed up initial subscriber registration.

Subscriber Index是一個(gè)可選的優(yōu)化技術(shù)欣舵,用來(lái)加速初始化訂閱者注冊(cè)。

The subscriber index can be created during build time using the EventBus annotation processor. While it is not required to use an index, it is recommended on Android for best performance.

Subscriber Index在編譯時(shí)使用EventBus注解處理器創(chuàng)建缀磕,雖然沒(méi)有規(guī)定必須使用它缘圈,但是官方推薦使用這種方式,因?yàn)樗贏ndroid上有著最佳的性能袜蚕。

使用方式:在gradle做如下配置

android {
    defaultConfig {
        javaCompileOptions {
            annotationProcessorOptions {
                arguments = [ eventBusIndex : 'com.example.myapp.MyEventBusIndex' ]
            }
        }
    }
}
 
dependencies {
    compile 'org.greenrobot:eventbus:3.1.1'
    annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.1.1'
}

如果以上方法不生效糟把,還可以使用android-apt Gradle plugin

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

配置完了之后,我們就可以在代碼中添加索引了

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

除了application廷没,還可以在library中使用索引

EventBus eventBus = EventBus.builder()
    .addIndex(new MyEventBusAppIndex())
    .addIndex(new MyEventBusLibIndex()).build();

好了糊饱,這就是索引的使用方式。

AsyncExecutor

先看個(gè)例子

AsyncExecutor.create().execute(
    new AsyncExecutor.RunnableEx() {
        @Override
        public void run() throws Exception {
            // No need to catch any Exception (here: LoginException)
            prepare();
            EventBus.getDefault().postSticky(new MyStickyEvent());
        }
    }
);

private void prepare() throws Exception {
    throw new Exception("prepare failed");
}

@Subscribe(threadMode = ThreadMode.MAIN)
public void onUpdateUIEvent(UpdateUIEvent updateUIEvent) {
    Log.d(TAG, "onUpdateUIEvent: ");
}

@Subscribe(threadMode = ThreadMode.MAIN)
public void handleFailureEvent(ThrowableFailureEvent event) {
    Log.d(TAG, "handleFailureEvent: " + event.getThrowable().getMessage());

通過(guò)這樣的方式颠黎,如果相關(guān)代碼出現(xiàn)了異常另锋,則會(huì)將異常封裝成ThrowableFailureEvent,自動(dòng)發(fā)布出去狭归,只要訂閱者定義了接收ThrowableFailureEvent的方法夭坪,就可以拿到異常信息,后續(xù)的UpdateUIEvent也不會(huì)再發(fā)布过椎,如果沒(méi)有出現(xiàn)異常室梅,則正常發(fā)布事件。
運(yùn)行疚宇,看日志:

D/SecondActivity: handleFailureEvent: prepare failed

果然亡鼠,只有handleFailureEvent(ThrowableFailureEvent event)收到了異常事件。這種寫(xiě)法的好處是能把異常信息交給訂閱者敷待,讓訂閱者根據(jù)情況處理间涵。

總結(jié)

好了,以上就是EventBus相對(duì)高級(jí)一點(diǎn)的用法榜揖,除了以上提到的勾哩,還有一些別的特性抗蠢,具體請(qǐng)看官方文檔,如果有不明白的地方思劳,也一定要看官方文檔迅矛,這個(gè)非常重要,附上傳送門(mén):

戳我潜叛,看EventBus官方文檔秽褒!

下一篇著重于分析EventBus的源碼,了解其實(shí)現(xiàn)原理威兜。

感謝閱讀震嫉!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市牡属,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌扼睬,老刑警劉巖逮栅,帶你破解...
    沈念sama閱讀 216,496評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異窗宇,居然都是意外死亡措伐,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)军俊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)侥加,“玉大人,你說(shuō)我怎么就攤上這事粪躬〉0埽” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,632評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵镰官,是天一觀的道長(zhǎng)提前。 經(jīng)常有香客問(wèn)我,道長(zhǎng)泳唠,這世上最難降的妖魔是什么狈网? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,180評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮笨腥,結(jié)果婚禮上拓哺,老公的妹妹穿的比我還像新娘。我一直安慰自己脖母,他們只是感情好士鸥,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著镶奉,像睡著了一般础淤。 火紅的嫁衣襯著肌膚如雪崭放。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,165評(píng)論 1 299
  • 那天鸽凶,我揣著相機(jī)與錄音币砂,去河邊找鬼。 笑死玻侥,一個(gè)胖子當(dāng)著我的面吹牛决摧,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播凑兰,決...
    沈念sama閱讀 40,052評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼掌桩,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了姑食?” 一聲冷哼從身側(cè)響起波岛,我...
    開(kāi)封第一講書(shū)人閱讀 38,910評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎音半,沒(méi)想到半個(gè)月后则拷,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,324評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡曹鸠,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評(píng)論 2 332
  • 正文 我和宋清朗相戀三年煌茬,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片彻桃。...
    茶點(diǎn)故事閱讀 39,711評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡坛善,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出邻眷,到底是詐尸還是另有隱情眠屎,我是刑警寧澤,帶...
    沈念sama閱讀 35,424評(píng)論 5 343
  • 正文 年R本政府宣布耗溜,位于F島的核電站组力,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏抖拴。R本人自食惡果不足惜燎字,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望阿宅。 院中可真熱鬧候衍,春花似錦、人聲如沸洒放。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,668評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)往湿。三九已至妖异,卻和暖如春惋戏,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背他膳。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,823評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工响逢, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人棕孙。 一個(gè)月前我還...
    沈念sama閱讀 47,722評(píng)論 2 368
  • 正文 我出身青樓舔亭,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親蟀俊。 傳聞我的和親對(duì)象是個(gè)殘疾皇子钦铺,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • title: android 初識(shí)EventBusdate: 2016-04-17tags: eventbus E...
    梵依然閱讀 4,400評(píng)論 7 107
  • EventBus 簡(jiǎn)介 EventBus 直譯過(guò)來(lái)就是事件總線,熟悉計(jì)算機(jī)原理的人一定很熟悉總線的概念肢预,所有設(shè)備都...
    DanieX閱讀 1,043評(píng)論 0 1
  • 項(xiàng)目到了一定階段會(huì)出現(xiàn)一種甜蜜的負(fù)擔(dān):業(yè)務(wù)的不斷發(fā)展與人員的流動(dòng)性越來(lái)越大矛洞,代碼維護(hù)與測(cè)試回歸流程越來(lái)越繁瑣。這個(gè)...
    fdacc6a1e764閱讀 3,181評(píng)論 0 6
  • EventBus源碼分析(一) EventBus官方介紹為一個(gè)為Android系統(tǒng)優(yōu)化的事件訂閱總線烫映,它不僅可以很...
    蕉下孤客閱讀 3,998評(píng)論 4 42
  • 那年冬天窑邦,天氣很冷,教室里面只有寥寥可數(shù)的幾個(gè)人壕探,因?yàn)檫B續(xù)的補(bǔ)課和低溫冈钦,很多同學(xué)對(duì)于寒冷都無(wú)力抵抗,我也是一樣李请。 ...
    草瓣閱讀 513評(píng)論 1 1