EventBus使用詳解

前言:EventBus出來已經(jīng)有一段時間了,github上面也有很多開源項目中使用了EventBus。所以抽空學(xué)習(xí)順便整理了一下。目前EventBus最新版本是3.0,所以本文是基于EventBus3.0的。

相關(guān)文章

EventBus使用詳解

EventBus源碼解析

概述

EventBus是針一款對Android的發(fā)布/訂閱事件總線。它可以讓我們很輕松的實現(xiàn)在Android各個組件之間傳遞消息,并且代碼的可讀性更好,耦合度更低。

如何使用

(1)首先需要定義一個消息類,該類可以不繼承任何基類也不需要實現(xiàn)任何接口。如:

publicclassMessageEvent {? ? ......}

(2)在需要訂閱事件的地方注冊事件

EventBus.getDefault().register(this);

(3)產(chǎn)生事件,即發(fā)送消息

EventBus.getDefault().post(messageEvent);

(4)處理消息

@Subscribe(threadMode = ThreadMode.PostThread)publicvoidXXX(MessageEvent messageEvent){? ? ...}

在3.0之前,EventBus還沒有使用注解方式。消息處理的方法也只能限定于onEvent、onEventMainThread、onEventBackgroundThread和onEventAsync,分別代表四種線程模型。而在3.0之后,消息處理的方法可以隨便取名,但是需要添加一個注解@Subscribe,并且要指定線程模型(默認為PostThread),四種線程模型,下面會講到。

注意,事件處理函數(shù)的訪問權(quán)限必須為public,否則會報異常。

(5)取消消息訂閱

EventBus.getDefault().unregister(this);

有何優(yōu)點

采用消息發(fā)布/訂閱的一個很大的優(yōu)點就是代碼的簡潔性,并且能夠有效地降低消息發(fā)布者和訂閱者之間的耦合度。

舉個例子,比如有兩個界面,ActivityA和ActivityB,從ActivityA界面跳轉(zhuǎn)到ActivityB界面后,ActivityB要給ActivityA發(fā)送一個消息,ActivityA收到消息后在界面上顯示出來。我們最先想到的方法就是使用廣播,使用廣播實現(xiàn)此需求的代碼如下:

首先需要在ActivityA中定義一個廣播接收器:

publicclassMessageBroadcastReceiverextendsBroadcastReceiver{@Overridepublic void onReceive(Contextcontext,Intentintent) {? ? ? ? mMessageView.setText("Message from SecondActivity:"+ intent.getStringExtra("message"));? ? }}

還需要在onCreate()方法中注冊廣播接收器:

@OverrideprotectedvoidonCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);? ? setContentView(R.layout.activity_main);//注冊事件EventBus.getDefault().register(this);//注冊廣播IntentFilter intentFilter =newIntentFilter("message_broadcast");? ? mBroadcastReceiver =newMessageBroadcastReceiver();? ? registerReceiver(mBroadcastReceiver, intentFilter);? ? ......}

然后在onDestory()方法中取消注冊廣播接收器:

@OverrideprotectedvoidonDestroy(){super.onDestroy();? ? ......//取消廣播注冊unregisterReceiver(mBroadcastReceiver);}

最后我們需要在ActivityB界面中發(fā)送廣播消息:

findViewById(R.id.send_broadcast).setOnClickListener(newView.OnClickListener() {@OverridepublicvoidonClick(View v){? ? ? ? String message = mMessageET.getText().toString();if(TextUtils.isEmpty(message)) {? ? ? ? ? ? message ="defaule message";? ? ? ? }? ? ? ? Intent intent =newIntent();? ? ? ? intent.setAction("message_broadcast");? ? ? ? intent.putExtra("message", message);? ? ? ? sendBroadcast(intent);? ? }});

看著上面的實現(xiàn)代碼,感覺也沒什么不妥,挺好的!下面對比看下使用EventBus如何實現(xiàn)。

根據(jù)文章最前面所講的EventBus使用步驟,首先我們需要定義一個消息事件類:

publicclassMessageEvent {privateStringmessage;publicMessageEvent(Stringmessage) {this.message = message;? ? }publicStringgetMessage() {returnmessage;? ? }publicvoidsetMessage(Stringmessage) {this.message = message;? ? }}

在ActivityA界面中我們首先需要注冊訂閱事件:

@OverrideprotectedvoidonCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);? ? setContentView(R.layout.activity_main);//注冊事件EventBus.getDefault().register(this);? ? ......}

然后在onDestory()方法中取消訂閱:

@OverrideprotectedvoidonDestroy(){super.onDestroy();//取消事件注冊EventBus.getDefault().unregister(this);}

當(dāng)然還要定義一個消息處理的方法:

@Subscribe(threadMode= ThreadMode.MainThread)public void onShowMessageEvent(MessageEvent messageEvent) {mMessageView.setText("MessagefromSecondActivity:"+messageEvent.getMessage());}

至此,消息訂閱者我們已經(jīng)定義好了,我們還需要在ActivityB中發(fā)布消息:

findViewById(R.id.send).setOnClickListener(newView.OnClickListener() {@OverridepublicvoidonClick(View v){? ? ? ? String message = mMessageET.getText().toString();if(TextUtils.isEmpty(message)) {? ? ? ? ? ? message ="defaule message";? ? ? ? }? ? ? ? EventBus.getDefault().post(newMessageEvent(message));? ? }});

對比代碼一看,有人會說了,這尼瑪有什么區(qū)別嘛!說好的簡潔呢?哥們,別著急嘛!我這里只是舉了個簡單的例子,僅僅從該例子來看,EventBus的優(yōu)勢沒有體現(xiàn)出來。現(xiàn)在我將需求稍微改一下,ActivityA收到消息后,需要從網(wǎng)絡(luò)服務(wù)器獲取數(shù)據(jù)并將數(shù)據(jù)展示出來。如果使用廣播,ActivityA中廣播接收器代碼應(yīng)該這么寫:

publicclassMessageBroadcastReceiverextendsBroadcastReceiver{@OverridepublicvoidonReceive(Context context, Intent intent){newThread(newRunnable() {@Overridepublicvoidrun(){//從服務(wù)器上獲取數(shù)據(jù)......? ? ? ? ? ? ? ? runOnUiThread(newRunnable() {@Overridepublicvoidrun(){//將獲取的數(shù)據(jù)展示在界面上......? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? });? ? ? ? ? ? }? ? ? ? }).start();? ? }}

看到這段代碼,不知道你何感想,反正我是看著很不爽,嵌套層次太多,完全違反了Clean Code的原則。那使用EventBus來實現(xiàn)又是什么樣呢?我們看一下。

@Subscribe(threadMode = ThreadMode.BackgroundThread)public void onGetDataEvent(MessageEvent messageEvent) {//從服務(wù)器上獲取數(shù)據(jù)......EventBus.getDefault().post(new ShowMessageEvent());}@Subscribe(threadMode = ThreadMode.MainThread)public void onShowDataEvent(ShowMessageEvent showMessageEvent) {//將獲取的數(shù)據(jù)展示在界面上......}

對比一下以上兩段代碼就能很明顯的感覺到EventBus的優(yōu)勢,代碼簡潔、層次清晰,大大提高了代碼的可讀性和可維護性。我這只是簡單的加了一個小需求而已,隨著業(yè)務(wù)越來越復(fù)雜,使用EventBus的優(yōu)勢愈加明顯。

常用API介紹

線程模型

在EventBus的事件處理函數(shù)中需要指定線程模型,即指定事件處理函數(shù)運行所在的想線程。在上面我們已經(jīng)接觸到了EventBus的四種線程模型。那他們有什么區(qū)別呢?

在EventBus中的觀察者通常有四種線程模型,分別是PostThread(默認)、MainThread、BackgroundThread與Async。

PostThread:如果使用事件處理函數(shù)指定了線程模型為PostThread,那么該事件在哪個線程發(fā)布出來的,事件處理函數(shù)就會在這個線程中運行,也就是說發(fā)布事件和接收事件在同一個線程。在線程模型為PostThread的事件處理函數(shù)中盡量避免執(zhí)行耗時操作,因為它會阻塞事件的傳遞,甚至有可能會引起ANR。

MainThread:如果使用事件處理函數(shù)指定了線程模型為MainThread,那么不論事件是在哪個線程中發(fā)布出來的,該事件處理函數(shù)都會在UI線程中執(zhí)行。該方法可以用來更新UI,但是不能處理耗時操作。

BackgroundThread:如果使用事件處理函數(shù)指定了線程模型為BackgroundThread,那么如果事件是在UI線程中發(fā)布出來的,那么該事件處理函數(shù)就會在新的線程中運行,如果事件本來就是子線程中發(fā)布出來的,那么該事件處理函數(shù)直接在發(fā)布事件的線程中執(zhí)行。在此事件處理函數(shù)中禁止進行UI更新操作。

Async:如果使用事件處理函數(shù)指定了線程模型為Async,那么無論事件在哪個線程發(fā)布,該事件處理函數(shù)都會在新建的子線程中執(zhí)行。同樣,此事件處理函數(shù)中禁止進行UI更新操作。

為了驗證以上四個方法,我寫了個小例子。

@Subscribe(threadMode= ThreadMode.PostThread)public void onMessageEventPostThread(MessageEvent messageEvent) {Log.e("PostThread",Thread.currentThread().getName());}@Subscribe(threadMode= ThreadMode.MainThread)public void onMessageEventMainThread(MessageEvent messageEvent) {Log.e("MainThread",Thread.currentThread().getName());}@Subscribe(threadMode= ThreadMode.BackgroundThread)public void onMessageEventBackgroundThread(MessageEvent messageEvent) {Log.e("BackgroundThread",Thread.currentThread().getName());}@Subscribe(threadMode= ThreadMode.Async)public void onMessageEventAsync(MessageEvent messageEvent) {Log.e("Async",Thread.currentThread().getName());}

分別使用上面四個方法訂閱同一事件,打印他們運行所在的線程。首先我們在UI線程中發(fā)布一條MessageEvent的消息,看下日志打印結(jié)果是什么。

findViewById(R.id.send).setOnClickListener(newView.OnClickListener() {@OverridepublicvoidonClick(View v){? ? ? ? ? ? Log.e("postEvent", Thread.currentThread().getName());? ? ? ? ? ? EventBus.getDefault().post(newMessageEvent());? ? ? ? }? ? });

打印結(jié)果如下:

2689-2689/com.lling.eventbusdemoE/postEvent﹕ main2689-2689/com.lling.eventbusdemoE/PostThread﹕ main2689-3064/com.lling.eventbusdemoE/Async﹕ pool-1-thread-12689-2689/com.lling.eventbusdemoE/MainThread﹕ main2689-3065/com.lling.eventbusdemoE/BackgroundThread﹕ pool-1-thread-2

從日志打印結(jié)果可以看出,如果在UI線程中發(fā)布事件,則線程模型為PostThread的事件處理函數(shù)也執(zhí)行在UI線程,與發(fā)布事件的線程一致。線程模型為Async的事件處理函數(shù)執(zhí)行在名字叫做pool-1-thread-1的新的線程中。而MainThread的事件處理函數(shù)執(zhí)行在UI線程,BackgroundThread的時間處理函數(shù)執(zhí)行在名字叫做pool-1-thread-2的新的線程中。

我們再看看在子線程中發(fā)布一條MessageEvent的消息時,會有什么樣的結(jié)果。

findViewById(R.id.send).setOnClickListener(newView.OnClickListener() {@OverridepublicvoidonClick(View v){newThread(newRunnable() {@Overridepublicvoidrun(){? ? ? ? ? ? ? ? ? ? Log.e("postEvent", Thread.currentThread().getName());? ? ? ? ? ? ? ? ? ? EventBus.getDefault().post(newMessageEvent());? ? ? ? ? ? ? ? }? ? ? ? ? ? }).start();? ? ? ? }? ? });

打印結(jié)果如下:

3468-3945/com.lling.eventbusdemo E/postEvent﹕ Thread-1253468-3945/com.lling.eventbusdemo E/PostThread﹕ Thread-1253468-3945/com.lling.eventbusdemo E/BackgroundThread﹕ Thread-1253468-3946/com.lling.eventbusdemo E/Async﹕ pool-1-thread-13468-3468/com.lling.eventbusdemo E/MainThread﹕ main

從日志打印結(jié)果可以看出,如果在子線程中發(fā)布事件,則線程模型為PostThread的事件處理函數(shù)也執(zhí)行在子線程,與發(fā)布事件的線程一致(都是Thread-125)。BackgroundThread事件模型也與發(fā)布事件在同一線程執(zhí)行。Async則在一個名叫pool-1-thread-1的新線程中執(zhí)行。MainThread還是在UI線程中執(zhí)行。

上面一個例子充分驗證了指定不同線程模型的事件處理方法執(zhí)行所在的線程。

黏性事件

除了上面講的普通事件外,EventBus還支持發(fā)送黏性事件。何為黏性事件呢?簡單講,就是在發(fā)送事件之后再訂閱該事件也能收到該事件,跟黏性廣播類似。具體用法如下:

訂閱黏性事件:

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

黏性事件處理函數(shù):

@Subscribe(sticky =true)publicvoidXXX(MessageEvent messageEvent){? ? ......}

發(fā)送黏性事件:

EventBus.getDefault().postSticky(newMessageEvent("test"));

處理消息事件以及取消訂閱和上面方式相同。

看個簡單的黏性事件的例子,為了簡單起見我這里就在一個Activity里演示了。

Activity代碼:

publicclassStickyModeActivityextendsAppCompatActivity{intindex =0;@OverrideprotectedvoidonCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);? ? ? ? setContentView(R.layout.activity_sticky_mode);? ? ? ? findViewById(R.id.post).setOnClickListener(newView.OnClickListener() {@OverridepublicvoidonClick(View v){? ? ? ? ? ? ? ? EventBus.getDefault().postSticky(newMessageEvent("test"+ index++));? ? ? ? ? ? }? ? ? ? });? ? ? ? findViewById(R.id.regist).setOnClickListener(newView.OnClickListener() {@OverridepublicvoidonClick(View v){? ? ? ? ? ? ? ? EventBus.getDefault().registerSticky(StickyModeActivity.this);? ? ? ? ? ? }? ? ? ? });? ? ? ? findViewById(R.id.unregist).setOnClickListener(newView.OnClickListener() {@OverridepublicvoidonClick(View v){? ? ? ? ? ? ? ? EventBus.getDefault().unregister(StickyModeActivity.this);? ? ? ? ? ? }? ? ? ? });? ? }@Subscribe(threadMode = ThreadMode.PostThread, sticky =true)publicvoidonMessageEventPostThread(MessageEvent messageEvent){? ? ? ? Log.e("PostThread", messageEvent.getMessage());? ? }@Subscribe(threadMode = ThreadMode.MainThread, sticky =true)publicvoidonMessageEventMainThread(MessageEvent messageEvent){? ? ? ? Log.e("MainThread", messageEvent.getMessage());? ? }@Subscribe(threadMode = ThreadMode.BackgroundThread, sticky =true)publicvoidonMessageEventBackgroundThread(MessageEvent messageEvent){? ? ? ? Log.e("BackgroundThread", messageEvent.getMessage());? ? }@Subscribe(threadMode = ThreadMode.Async, sticky =true)publicvoidonMessageEventAsync(MessageEvent messageEvent){? ? ? ? Log.e("Async", messageEvent.getMessage());? ? }}

布局代碼activity_sticky_mode.xml:

? ? ? ? ? ?

代碼很簡單,界面上三個按鈕,一個用來發(fā)送黏性事件,一個用來訂閱事件,還有一個用來取消訂閱的。首先在未訂閱的情況下點擊發(fā)送按鈕發(fā)送一個黏性事件,然后點擊訂閱,會看到日志打印結(jié)果如下:

15246-15246/com.lling.eventbusdemoE/PostThread﹕ test015246-15391/com.lling.eventbusdemoE/Async﹕ test015246-15246/com.lling.eventbusdemoE/MainThread﹕ test015246-15393/com.lling.eventbusdemoE/BackgroundThread﹕ test0

這就是粘性事件,能夠收到訂閱之前發(fā)送的消息。但是它只能收到最新的一次消息,比如說在未訂閱之前已經(jīng)發(fā)送了多條黏性消息了,然后再訂閱只能收到最近的一條消息。這個我們可以驗證一下,我們連續(xù)點擊5次POST按鈕發(fā)送5條黏性事件,然后再點擊REGIST按鈕訂閱,打印結(jié)果如下:

6980-6980/com.lling.eventbusdemoE/PostThread﹕ test46980-6980/com.lling.eventbusdemoE/MainThread﹕ test46980-7049/com.lling.eventbusdemoE/Async﹕ test46980-7048/com.lling.eventbusdemoE/BackgroundThread﹕ test4

由打印結(jié)果可以看出,確實是只收到最近的一條黏性事件。

好了,EventBus的使用暫時分析到這里,例子代碼從這里獲取。下一講將講解EventBus的源碼。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

  • EventBus這個開源框架出來已經(jīng)很久了,深的很多開發(fā)者青睞,由greenrobot組織貢獻(該組織還貢獻了gr...
    Scus閱讀 2,257評論 0 0
  • 本文的EventBus,是指greenrobot的 EventBus, 主要以EventBus3.0 講解; 什么...
    Simon_z閱讀 5,190評論 2 2
  • 前言:EventBus出來已經(jīng)有一段時間了,github上面也有很多開源項目中使用了EventBus。所以抽空學(xué)習(xí)...
    Lauren_Liuling閱讀 48,547評論 23 155
  • 目錄 1.概述 2.實戰(zhàn) 1.基本框架搭建 2.新建一個類FirstEvent 3.在要接收消息的頁面注冊Even...
    慕涵盛華閱讀 10,532評論 2 16
  • 前言 最近在公司做一個類似于手機工廠模式的一個項目,用來檢測其他各個App是否正常工作,所以要求是盡可能的輕量級,...
    Luckily_Liu閱讀 1,204評論 2 8