筆記概述
-
EventBus簡(jiǎn)介
-
EventBus方法介紹
-
EventBus實(shí)際運(yùn)用
EventBus簡(jiǎn)介
開源項(xiàng)目地址:https://github.com/greenrobot/EventBus
EventBus主頁:http://greenrobot.org/eventBus/
github項(xiàng)目地址中關(guān)于EventBus的簡(jiǎn)介:
Event bus for Android and Java that simplifies communication between Activities, Fragments, Threads, Services, etc. Less code, better quality.
即,
Event 簡(jiǎn)化了活動(dòng)、碎片、進(jìn)程、服務(wù)等之間的通訊方式;
使APP項(xiàng)目用更少的代碼量實(shí)現(xiàn)更好的質(zhì)量;
關(guān)于EventBus的優(yōu)勢(shì)
- 簡(jiǎn)化組件間的通訊方式
- 解耦合事件發(fā)送者和接收者
- 使活動(dòng)、碎片和后臺(tái)的線程實(shí)現(xiàn)更高的執(zhí)行效率
- 防止復(fù)雜的有錯(cuò)誤(傾向)的依賴以及生命周期的問題
- 讓你的代碼簡(jiǎn)潔
- 運(yùn)行快
- 庫小
- EventBus主頁簡(jiǎn)潔:
EventBus is an open-source library for Android and Java using the publisher/subscriber pattern for loose coupling. EventBus enables central communication to decoupled classes with just a few lines of code – simplifying the code, removing dependencies, and speeding up app development.
即,
EventBus是一個(gè)開源庫,
使用發(fā)布/訂閱機(jī)制來對(duì)代碼進(jìn)行解耦。
簡(jiǎn)化項(xiàng)目的集中通訊(僅僅通過幾行代碼就可以解耦各個(gè)類),
移除了一些不必要的依賴,加速移動(dòng)應(yīng)用的開發(fā)。
關(guān)于大項(xiàng)目,如果還是用Java/Android原生的調(diào)用,
兩個(gè)Activity之間的通訊,
還用startActivity()/startActivityForResult()這類通信方式的話,
代碼會(huì)非常的冗余,
例如Activity和Fragment之間的通訊就需要不斷地調(diào)用相關(guān)的函數(shù);
使用EventBus可以解除這些耦合;
否則如果代碼耦合性非常大的話,
會(huì)大大增加后期維護(hù)的難度!
EventBus架構(gòu)

Publisher
調(diào)用post()方法,
把Event發(fā)送到EventBus;EventBus(類似于快遞中心)
分發(fā)Publisher發(fā)布的Event
給對(duì)應(yīng)的Subscriber(訂閱者);Subscriber接收Event;
EventBus概述
EventBus是一個(gè)Android端優(yōu)化的
publish/subscribe消息總線;簡(jiǎn)化了應(yīng)用程序內(nèi)各組件間、組件與后臺(tái)線程間的通訊;
舉例一個(gè)EventBus可簡(jiǎn)化代碼的場(chǎng)景:
請(qǐng)求網(wǎng)絡(luò)時(shí)候,等網(wǎng)絡(luò)返回時(shí)通過Handler或Broadcast通知UI;
兩個(gè)Fragment之間需要通過Listener通訊;
以上都可以用EventBus來代替;-
EventBus作為一個(gè)消息總線,有三個(gè)主要的元素:
Event:事件
Event可以是任意對(duì)象,
用來描述傳遞的數(shù)據(jù)或事件類型;
一般Event是由開發(fā)者按照需求自己定義的,
里面封裝要傳遞的事件類型和數(shù)據(jù);Subscriber:事件訂閱者,接收待定的事件
在Event中,使用約定來制定事件訂閱者以簡(jiǎn)化使用;
在3.0之前,EventBus還沒使用注解的方式,
消息處理的方法也僅限于:
onEvent、onEventMainThread、onEventBackgroundThread、onEventSync,
分別代表四種線程模型;
在3.0之后,消息處理的方法可以隨便取名,
但是需要
添加一個(gè)注解@Subscribe,
并且要指定線程的模型;Publisher:事件發(fā)布者,用于通知
Subscriber有事件發(fā)生
可以在任意線程、任意位置發(fā)送事件,
直接調(diào)用EventBus的post(Object)方法即可;調(diào)用
EventBus.getDefault()方法,實(shí)例化EventBus對(duì)象
EventBus線程模型
ThreadMode
ThreadMode指定了會(huì)調(diào)用的函數(shù),
只能有以下四種(因?yàn)槊總€(gè)訂閱事件都是和一個(gè)線程模型相關(guān)的):
PostThread、 BackgroundThread、 MainThread、 Async
PostThread: 在相同的進(jìn)程中做EventBus通信
事件的處理和事件的發(fā)送在相同的進(jìn)程,
所以事件的處理時(shí)間不應(yīng)太長(zhǎng),
不然會(huì)影響事件的發(fā)送線程;
而這個(gè)線程可能是UI線程;對(duì)應(yīng)的函數(shù)名是
onEvent,
一般在UI線程使用,
如果堵塞時(shí)間較長(zhǎng)則會(huì)影響其他線程的刷新,
引起界面的卡頓;
打個(gè)比方說你在UI線程中卡了兩秒等下UI就不動(dòng),不刷新了
相關(guān)地舉一個(gè)案例
- 這里有兩個(gè)Activity:
按下Activity1中的Button,
會(huì)跳轉(zhuǎn)到Activity2;
按下Activity2中的button,
會(huì)通過EventBus去通知Activity1;
Activity1會(huì)通過OnEvent接收,
如果接收到Activity2發(fā)送過來消息,
然后觸發(fā)Toast;
接下來新建一個(gè)項(xiàng)目,根據(jù)官方GitHub添加依賴,
implementation 'org.greenrobot:eventbus:3.1.1'
下面是主布局(Activity1):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/bt_toAc2"
android:text="跳轉(zhuǎn)到Activity2"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
- 然后根據(jù)需求定義Event(類似于Module類),
這里沒什么特別需求,
定義一個(gè)簡(jiǎn)單的Event就可以了:
public class MyEvent {
public String msg;
public MyEvent() {}
public MyEvent(String msg) {
this.msg = msg;
}
}
-
接下來,
要在我們這個(gè)“Activity1”里面注冊(cè) OnEvent ,
這個(gè)OnEvent 是在跟 發(fā)送事件的線程 同一個(gè)線程里面 接收事件的,
我們這里雖然是分開兩個(gè)Activity,
但是Activity本身就都是在主線程里面的;
所以這里 事件的發(fā)送(在Activity2), 事件的接收(在Activity1)
都在同一個(gè)線程中;
即,以上所說的PostThread線程類型中,
事件的發(fā)送 跟 事件的接收 是在同一個(gè)線程里面的;
下面注冊(cè)一個(gè)onEvent(),
如果接收到Activity2發(fā)送過來消息,觸發(fā)Toast;
/**
* 注冊(cè)onEvent(),
* 注意寫上注解!
*/
@Subscribe
public void onEvent(MyEvent event) {
popOutToast("接收到Event:" + event.msg);
}
/**
* 封裝彈出短時(shí)Toast提示
* @param text 企圖彈出的文本內(nèi)容
*
*/
private void popOutToast(String text) {
Toast.makeText(MainActivity.this,text,Toast.LENGTH_SHORT).show();
}
- 使用EventBus的接收方法的活動(dòng),需要在onCreate中注冊(cè)
EventBus.getDefault().register(this);
- 反注冊(cè)
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
- 整個(gè)Activity1的java代碼:
public class MainActivity extends AppCompatActivity {
private Button mButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//使用EventBus的接收方法的活動(dòng),需要注冊(cè)
EventBus.getDefault().register(this);
mButton = findViewById(R.id.bt_toAc2);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, SecondActivity.class);
startActivity(intent);
}
});
}
/**
* 注冊(cè)onEvent(),
* 注意寫上注解!
*/
@Subscribe
public void onEvent(MyEvent event) {
popOutToast("接收到Event:" + event.msg);
}
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
/**
* 封裝彈出短時(shí)Toast提示
* @param text 企圖彈出的文本內(nèi)容
*
*/
private void popOutToast(String text) {
Toast.makeText(MainActivity.this,text,Toast.LENGTH_SHORT).show();
}
}
創(chuàng)建第二個(gè)活動(dòng)SecondActivity,布局:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/bt_sendMsg"
android:text="發(fā)送消息"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
java:
public class SecondActivity extends AppCompatActivity {
private Button mButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
mButton = findViewById(R.id.bt_sendMsg);
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
EventBus.getDefault().post(new MyEvent("洞妖洞妖我是棟二?。?!"));
}
});
}
}
運(yùn)行效果圖:

跳到Activity2,點(diǎn)擊按鈕,彈出Toast:

補(bǔ)充
- 以上這種場(chǎng)景,
如果不用EventBus,
我們可能需要用到Handler + Runnable的方式,
但是如果有十個(gè)Activity向Activity1發(fā)消息,
我們就需要寫十個(gè)Handler了,
這樣子相當(dāng)繁瑣;
而使用EventBus,
這里只要稍微用代碼注冊(cè)一下就可以了,
明顯方便很多,
一個(gè)方post、一個(gè)onEvent,也很輕松地解耦了;
- 另外一個(gè)需要注意的地方就是,
EventBus.getDefault().register(this);系列的注冊(cè)與反注冊(cè)代碼,
同onEvent()系列的接收函數(shù)是緊密綁定的;
用時(shí)缺一不可,不用時(shí)存一不可,同生同滅;
也就是說一個(gè)活動(dòng)注冊(cè)onEvent()系列的接收函數(shù)了,
則必須用EventBus.getDefault().register(this);去注冊(cè),
不然會(huì)報(bào)錯(cuò);
而一個(gè)活動(dòng)它沒有寫onEvent()系列的接收函數(shù),
卻用EventBus.getDefault().register(this);去注冊(cè)了,
同樣也會(huì)報(bào)錯(cuò)!
- onEvent()處理時(shí)間比較長(zhǎng),會(huì)導(dǎo)致線程堵塞;
如以下再onEvent()中掛起線程3秒,模擬3秒處理時(shí)間:
@Subscribe
public void onEvent(MyEvent event) {
//消耗時(shí)間模擬
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
popOutToast("接收到Event:" + event.msg);
}
接著運(yùn)行項(xiàng)目的話,
會(huì)發(fā)現(xiàn)我們?cè)贏ctivity2中點(diǎn)擊發(fā)送消息的按鈕之后,
要等到3秒鐘,主線程才會(huì)刷新UI(彈出Toast),
這樣子在實(shí)際運(yùn)用中用戶體驗(yàn)很差;
MainThread
- 其機(jī)制同onEvent()其實(shí)是差不多的,
即發(fā)送和接收都是在同一個(gè)線程主線程/UI線程中進(jìn)行;
使用:基于PostThread的代碼,
加多一行(threadMode = ThreadMode.MAIN)即可:
//MainThread
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEvent(MyEvent event) {
//消耗時(shí)間模擬
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
popOutToast("接收到Event:" + event.msg);
}
BackgroundThread
事件的處理會(huì)在一個(gè)
后臺(tái)線程中執(zhí)行,
對(duì)應(yīng)的函數(shù)名是onEventBackgroundThread;雖然名字是BackgroundThread,
事件的處理是在后臺(tái)線程,
但事件的處理時(shí)間還是不宜太長(zhǎng);如果發(fā)送事件的線程是在后臺(tái)線程,
會(huì)直接執(zhí)行事件;如果當(dāng)前線程是UI線程,
事件會(huì)被加到一個(gè)隊(duì)列中,
由一個(gè)線程依次處理這些事件,
如果某個(gè)事件處理時(shí)間太長(zhǎng),
會(huì)阻塞隊(duì)列中 排在后面的事件的派發(fā)或處理;
圖解
對(duì)于PostThread和MainThread
一次執(zhí)行
-
當(dāng)只有一個(gè)線程的時(shí)候,
宏觀上來說,其代碼便是一次執(zhí)行的:
post(發(fā)送)和onEvent()是在同一個(gè)線程中去跑的,
一個(gè)線程里面的話,
對(duì)于BackgroundThread
一一對(duì)應(yīng)
- 這里有兩個(gè)線程,UI主線程和后臺(tái)線程,
這個(gè)后臺(tái)線程專門用來
處理onEventBackgroundThread方法及其對(duì)應(yīng)的事件的;
也就是說,
比如現(xiàn)在主線程里面有一個(gè)post,
它會(huì)對(duì)應(yīng)執(zhí)行到后臺(tái)的一個(gè)onEventBackgroundThread();
順序執(zhí)行,前者執(zhí)行,后者等待阻塞
一個(gè)前臺(tái)線程的post
會(huì)對(duì)應(yīng)執(zhí)行到一個(gè)后臺(tái)線程的onEventBackgroundThread();
來了第二個(gè)post,
就對(duì)應(yīng)第二個(gè)onEventBackgroundThread();-
后臺(tái)線程中的
onEventBackgroundThread();
是按照post的順序依次執(zhí)行的;
如果前面一個(gè)post對(duì)應(yīng)的onEventBackgroundThread();沒有執(zhí)行完,
這時(shí)候又post了一下,
那么對(duì)應(yīng)的后面的這個(gè)onEventBackgroundThread()會(huì)等待前面一個(gè)onEventBackgroundThread()執(zhí)行完,它才執(zhí)行;
- 來個(gè)例子,修改MainActivity代碼:
//BackgroundThread
@Subscribe(threadMode = ThreadMode.BACKGROUND)
public void onEvent(MyEvent event) {
Log.d(TAG, "onEvent Start!!!!!!!! ");
//消耗時(shí)間模擬
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.d(TAG, "onEvent End!!!!!!!! ");
// popOutToast("接收到Event:" + event.msg);
}
-
接著運(yùn)行代碼,
老規(guī)矩,Activity1跳轉(zhuǎn)到Activity2,
點(diǎn)擊“發(fā)送信息”按鈕,
連續(xù)點(diǎn)擊兩次(根據(jù)以上SecondActivity中寫的按鈕點(diǎn)擊事件,
這里可以理解成連續(xù)post兩次,一前一后),
觀察logcat: 我們可以觀察到兩對(duì)Start和End是順序執(zhí)行的;
執(zhí)行時(shí)候沒有交叉,先第一對(duì),后第二對(duì);
這里也便驗(yàn)證了以上理論——
即,
一一對(duì)應(yīng),一個(gè)post對(duì)應(yīng)一個(gè)event,
event順序執(zhí)行,
前post者對(duì)應(yīng)的event執(zhí)行中,
則后post者對(duì)應(yīng)的event等待阻塞;其實(shí)把代碼改成
MainThread的,
再運(yùn)行,連續(xù)點(diǎn)擊三次,
同樣是能體現(xiàn)一一對(duì)應(yīng),順序執(zhí)行,前者執(zhí)行,后者等待阻塞的特性:
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEvent(MyEvent event) {
Log.d(TAG, "onEvent Start!!!!!!!! ");
//消耗時(shí)間模擬
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.d(TAG, "onEvent End!!!!!!!! ");
// popOutToast("接收到Event:" + event.msg);
}

- 但區(qū)別在于,
Main是執(zhí)行在主線程,
而Background是執(zhí)行在后臺(tái)線程,
而且我們前面說過,
在主線程中執(zhí)行占用資源多、占用時(shí)間長(zhǎng)的任務(wù)是不合適的,
既不規(guī)范,也影響體驗(yàn);
PostThread/MainThread缺點(diǎn):
執(zhí)行在主線程,
事件的個(gè)數(shù),事件的耗時(shí),
都需要做比較嚴(yán)格的限制;
BackgroundThread缺點(diǎn):
運(yùn)行在后臺(tái)線程,不占用主線程資源,
比PostThread/MainThread好那么一點(diǎn),
但是還是沒有解決——
多個(gè)(>= 2個(gè))事件時(shí),
一次處理一個(gè),依次處理,
前者執(zhí)行,后者等待阻塞的問題,
不適合事件中有耗時(shí)較長(zhǎng)的任務(wù);
Async adj.異步的;
sync n.同時(shí),同步;
-
事件處理會(huì)在單獨(dú)的線程中執(zhí)行,
主要用于在后臺(tái)線程中執(zhí)行耗時(shí)操作,
每個(gè)事件會(huì)開啟一個(gè)線程
(程序初始化時(shí),已經(jīng)幫我們創(chuàng)建好一個(gè)線程池,
每次POST一下框架都會(huì)去取一個(gè)線程來執(zhí)行),
但最好限制線程的數(shù)目
(線程過多,CPU使用大,設(shè)備耗電快);
每次POST一下框架都會(huì)去取一個(gè)線程來執(zhí)行,
線程池中線程之間互相不干擾,可以同時(shí)運(yùn)行;
更改代碼:
//AsyncThread
@Subscribe(threadMode = ThreadMode.ASYNC)
public void onEvent(MyEvent event) {
Log.d(TAG, "onEvent Start!!!!!!!! ");
//消耗時(shí)間模擬
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.d(TAG, "onEvent End!!!!!!!! ");
}
執(zhí)行效果:
這次我們可以看到,
事件的處理就沒有——
一次處理一個(gè),依次處理,前者執(zhí)行中,后者等待阻塞的特性了,
因?yàn)楦鱾€(gè)post的事件,
都有各自獨(dú)立的線程去處理,
所以事件的處理運(yùn)行是同時(shí)的、異步的;
小結(jié)
- PostThread:發(fā)送和接收在同一個(gè)線程;
- MainThread:發(fā)送和接收同在主線程;
(應(yīng)用范圍上被PostThread包括) - BackgroundThread
PostThread/MainThread缺點(diǎn):
一般執(zhí)行在主線程,
事件的個(gè)數(shù),事件的耗時(shí),
都需要做比較嚴(yán)格的限制;
BackgroundThread缺點(diǎn):
運(yùn)行在后臺(tái)線程,不占用主線程資源,
比PostThread/MainThread好那么一點(diǎn),
但是還是沒有解決——
多個(gè)(>= 2個(gè))事件時(shí),
一次處理一個(gè),依次處理,
前者執(zhí)行,后者等待阻塞的問題,
不適合事件中有耗時(shí)較長(zhǎng)的任務(wù);
以上三種線程都是不適合跑耗時(shí)操作的;
Async
adj.異步的;
核心:異步,同時(shí),高效;
- 事件處理會(huì)在單獨(dú)的線程中執(zhí)行,
主要用于在后臺(tái)線程中執(zhí)行耗時(shí)操作,
每個(gè)事件會(huì)開啟一個(gè)線程
(程序初始化時(shí),已經(jīng)幫我們創(chuàng)建好一個(gè)線程池,
每次POST一下框架都會(huì)去取一個(gè)線程來執(zhí)行),
但最好限制線程的數(shù)目
(線程過多,CPU使用大,設(shè)備耗電快);
每次POST一下框架都會(huì)去取一個(gè)線程來執(zhí)行,
線程池中線程之間互相不干擾,可以同時(shí)運(yùn)行
補(bǔ)充:


優(yōu)先級(jí)(
priority)就是字面上的意義,
值越高,優(yōu)先級(jí)越高;-
sticy即粘性發(fā)送,
發(fā)送方法即EventBus.getDefault().postSticky(new MyEvent());
注銷粘性event的方法EventBus.getDefault().removeStickyEvent(new MyEvent());
發(fā)送(postSticky)的時(shí)候,
項(xiàng)目中有多少Fragment、Activity等載體,
事件MyEvent就發(fā)送多少份;
粘性其意義在于,
無論項(xiàng)目中載體類中
是否使用EventBus.getDefault().register(this);對(duì)EventBus注冊(cè)過,
都會(huì)對(duì)其發(fā)送事件,
若載體注冊(cè)了,則接收處理該粘性事件;
若載體未注冊(cè),則該粘性事件會(huì)緩存起來,
一旦載體注冊(cè),馬上接收處理事件;
但由于這種粘性發(fā)送在項(xiàng)目比較大的時(shí)候
需要占用一定量的緩存資源,
所以一般使用較少;
- 另外一個(gè)需要注意的地方就是,
EventBus.getDefault().register(this);系列的注冊(cè)與反注冊(cè)代碼,
同onEvent()系列的接收函數(shù)是緊密綁定的;
用時(shí)缺一不可,不用時(shí)存一不可,同生同滅;
也就是說一個(gè)活動(dòng)注冊(cè)onEvent()系列的接收函數(shù)了,
則必須用EventBus.getDefault().register(this);去注冊(cè),
不然會(huì)報(bào)錯(cuò);
而一個(gè)活動(dòng)它沒有寫onEvent()系列的接收函數(shù),
卻用EventBus.getDefault().register(this);去注冊(cè)了,
同樣也會(huì)報(bào)錯(cuò)!
使用技巧
-
事件只需要傳遞一個(gè)
狀態(tài)/指令,無需傳遞數(shù)據(jù)時(shí),
event自定義類內(nèi)容可以為空;
比如一個(gè)只需要傳遞“清空位置信息列表”這個(gè)指令的事件,
可以這么定義:就是定義一個(gè)Event類,但是內(nèi)容為空;
即
無需傳遞數(shù)據(jù),
僅僅event類的類名已經(jīng)具備傳遞的事件、指令意義; -
一個(gè)
Fragment或者Activity需要接收處理多個(gè)Event時(shí)候,
通過建立多個(gè)注解方法,并以不同的event 形參,
來區(qū)分處理,接收到不同的event時(shí),該做的對(duì)應(yīng)的邏輯;








