寫在前面
對于Android程序員來說,相信大家都聽過EventBus的大名。EventBus是一個Android平臺上基于事件發(fā)布和訂閱的輕量級框架,可以對發(fā)布者和訂閱者解耦,并簡化Android的事件傳遞。正如官方介紹其優(yōu)勢:
- 簡化了組件之間的通信
- 解耦事件的發(fā)送者和接收者
- 在Activity、Fragment和后臺線程表現(xiàn)良好
- 避免復(fù)雜和易出錯的依賴性和生命周期問題
- 使你的代碼更加簡潔
- 快速和輕量

接下來,讓我們一起從EventBus的使用到源碼解讀,全方位地解讀這個輕量但功能強(qiáng)大的開源框架。
EventBus項目:EventBus GitHub
正文
本文先從EventBus的簡單使用開始,先介紹EventBus的基本配置和簡單使用,然后用一個Demo來舉例EventBus的使用及其優(yōu)勢。
配置和簡單使用
在app下的build.gradle配置:
compile 'org.greenrobot:eventbus:3.0.0'
是的,就添加這句就可以了,非常簡單。當(dāng)然還可以配置生成索引,這部分在進(jìn)階使用文章里介紹。
定義一個事件類:
public static class MessageEvent { /* Additional fields if needed */ }
在訂閱者類中定義一個用注解“@Subscribe”標(biāo)記并參數(shù)為事件類的方法:
@Subscribe
public void onMessageEvent(MessageEvent event) {/* Do something */};
然后在訂閱者類中注冊和注銷,在Android上,通常在Activity或Fragment的生命周期里處理:
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this); // 注冊
}
@Override
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this); // 注銷,當(dāng)訂閱者不再訂閱事件時,必須注銷,否則可能發(fā)生內(nèi)存泄漏
}
最后,發(fā)送事件,每個訂閱者注冊的事件都將收到消息(會調(diào)用“@Subscribe”標(biāo)記的方法):
EventBus.getDefault().post(new MessageEvent());
舉例
現(xiàn)在來舉個栗子來展示EventBus的魅力!想想一個閱讀App看書的場景:左邊是書籍選擇列表,右邊是書籍的內(nèi)容。這時我們大多數(shù)情況都是用Fragment來處理,也即左右兩邊都是Fragment,這就涉及兩個Fragment的通信問題。我們通常的處理方式是在Fragment添加接口回調(diào),然后Activitty作為兩個Fragment的通信橋梁。
或許有人說直接在Activity獲取Fragment里的控件,交互更加簡單。這樣雖然簡單,但Activity的職責(zé)變得很重,而Fragment的作用之一就是分擔(dān)Activity的職責(zé),所以這是不可取的。
好,我們先來看不用EventBus時用回調(diào)的方式來實(shí)現(xiàn)Fragment的通信的例子。
首先先定義兩個Fragment,左邊的Fragment1,布局只有一個ListView(布局代碼不貼,后面有源碼),在Fragment1里定義一個接口,當(dāng)點(diǎn)擊ListView的item時,就把所點(diǎn)擊的書籍回調(diào)給外部:
public class Fragment1 extends Fragment {
private final static String[] BOOKS = new String[] {
"第一行代碼",
"Java編程思想",
"Android開發(fā)藝術(shù)探索",
"Android源碼設(shè)計模式",
"算法",
"研磨設(shè)計模式"
};
ListView listView;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment1, container, false);
listView = (ListView) view.findViewById(R.id.lv_catalogue);
ArrayAdapter arrayAdapter = new ArrayAdapter<String>(getActivity(),
android.R.layout.simple_list_item_1, BOOKS);
listView.setAdapter(arrayAdapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// 回調(diào)
if (listener != null) {
listener.onCLickBook(BOOKS[position]);
}
}
});
return view;
}
private OnClickBookListener listener;
public void setOnClickBookListener(OnClickBookListener listener) {
this.listener = listener;
}
/**
* 點(diǎn)擊書籍監(jiān)聽接口
*/
public interface OnClickBookListener {
void onCLickBook(String book);
}
}
右邊的Fragment2就更加簡單了,布局只有一個TextView來展示書籍的內(nèi)容:
public class Fragment2 extends Fragment {
TextView textView;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment2, container, false);
textView = (TextView) view.findViewById(R.id.tv_content);
return view;
}
/**
* 需要暴露接口給外部來交互
*/
public void readBook(String book) {
textView.setText(book);
}
}
最后,MainActivity作為兩個Fragment的通信橋梁,來實(shí)現(xiàn)兩個Fragment的通信:
public class MainActivity extends AppCompatActivity {
Fragment1 fragment1;
Fragment2 fragment2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
fragment1 = (Fragment1) getSupportFragmentManager().findFragmentById(R.id.fragment1);
// 設(shè)置監(jiān)聽
fragment1.setOnClickBookListener(new Fragment1.OnClickBookListener() {
@Override
public void onCLickBook(String book) {
// 更新fragment2的閱讀書籍
fragment2.readBook(String.format("我正在閱讀%s", book));
}
});
fragment2 = (Fragment2) getSupportFragmentManager().findFragmentById(R.id.fragment2);
}
}
從上面代碼可以看到,兩個Fragment的通信也非常簡單,而Activity的職責(zé)也明顯降低,只需作為橋梁,那么我們還有必要用EventBus嗎?答案是有必要的!這只是一個例子,而實(shí)際開發(fā)中兩個Fragment通信交互幾乎不可能這么簡單,往往是縱橫交錯的,這時難道在Fragment上定義多個監(jiān)聽接口,然后在Activity上處理這些邏輯?這就又導(dǎo)致了Activity職責(zé)過大,而耦合度也顯著增加了。所以,使用回調(diào)的通信方式似乎適合交互較少的兩個Fragment,但一旦交互過多,就無能為力了。下面再來看看EventBus怎么實(shí)現(xiàn)Fragment的通信。
首先,先定義一個閱讀書籍的事件類(注意,最好用public修飾,否則有可能在生成索引時失敗,這些內(nèi)容在后續(xù)文章會講解,現(xiàn)在先記著):
public class ReadBookEvent {
private String book;
ReadBookEvent(String book) {
this.book = book;
}
public String getBook() {
return book;
}
}
顯然,因?yàn)镕ragment2是監(jiān)聽Fragment1的點(diǎn)擊事件作出更新響應(yīng),所以,這里Fragment2是訂閱者(注意:與事件類一樣,訂閱者最好聲明為public,否則有可能在生成索引失敗):
public class Fragment2 extends Fragment {
TextView textView;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment2, container, false);
textView = (TextView) view.findViewById(R.id.tv_content);
return view;
}
@Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this); // 注冊訂閱者
}
@Override
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this); 注銷訂閱者
}
/**
* 監(jiān)聽閱讀事件
*/
@Subscribe
public void onReadBook(ReadBookEvent event) {
textView.setText(String.format("我正在閱讀%s", event.getBook()));
}
}
而Fragment1不用定義回調(diào)接口了,只需在點(diǎn)擊書籍時發(fā)送事件:
public class Fragment1 extends Fragment {
private final static String[] BOOKS = new String[] {
"第一行代碼",
"Java編程思想",
"Android開發(fā)藝術(shù)探索",
"Android源碼設(shè)計模式",
"算法",
"研磨設(shè)計模式"
};
ListView listView;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment1, container, false);
listView = (ListView) view.findViewById(R.id.lv_catalogue);
ArrayAdapter arrayAdapter = new ArrayAdapter<String>(getActivity(),
android.R.layout.simple_list_item_1, BOOKS);
listView.setAdapter(arrayAdapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
EventBus.getDefault().post(new ReadBookEvent(BOOKS[position])); // 發(fā)送事件
}
});
return view;
}
}
另外在MainActivity里,不用再處理Fragment的交互了,甚至不用創(chuàng)建Fragment的實(shí)例,直接解耦:
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
從上面代碼可以看到,EventBus很好地使兩個Fragment解耦,并優(yōu)雅地實(shí)現(xiàn)了兩者的通信,同時也減少了Fragment和Activity的耦合,真正的一舉多得!再想想兩個Activity之間的通信,我們通常使用Intent作為載體,來傳遞數(shù)據(jù),使用EventBus就可以拋開Intent了,直接實(shí)現(xiàn)Activity間的通信!這里就不再舉例了,相信聰明的你能舉一反三。
至此,相信大家對EventBus有了基礎(chǔ)的認(rèn)知了。
寫在最后
EventBus所帶來的便利和簡潔有沒有使你震驚?!別滿足的太早,EventBus如此受人待見,當(dāng)然有她的厲害之處。后續(xù)文章將會介紹使用編譯期處理生成索引來提升事件發(fā)送的效率,還有EventBus的高級配置,敬請期待!
下一篇文章: EventBus源碼詳解(二):進(jìn)階使用