寫在前面
對于Android程序員來說喳张,相信大家都聽過EventBus的大名砚尽。EventBus是一個(gè)Android平臺(tái)上基于事件發(fā)布和訂閱的輕量級框架坯沪,可以對發(fā)布者和訂閱者解耦,并簡化Android的事件傳遞扛芽。正如官方介紹其優(yōu)勢:
- 簡化了組件之間的通信
- 解耦事件的發(fā)送者和接收者
- 在Activity骂蓖、Fragment和后臺(tái)線程表現(xiàn)良好
- 避免復(fù)雜和易出錯(cuò)的依賴性和生命周期問題
- 使你的代碼更加簡潔
- 快速和輕量
接下來,讓我們一起從EventBus的使用到源碼解讀川尖,全方位地解讀這個(gè)輕量但功能強(qiáng)大的開源框架登下。
EventBus項(xiàng)目:EventBus GitHub
正文
本文先從EventBus的簡單使用開始,先介紹EventBus的基本配置和簡單使用叮喳,然后用一個(gè)Demo來舉例EventBus的使用及其優(yōu)勢被芳。
配置和簡單使用
在app下的build.gradle配置:
compile 'org.greenrobot:eventbus:3.0.0'
是的,就添加這句就可以了馍悟,非常簡單畔濒。當(dāng)然還可以配置生成索引,這部分在進(jìn)階使用文章里介紹锣咒。
定義一個(gè)事件類:
public static class MessageEvent { /* Additional fields if needed */ }
在訂閱者類中定義一個(gè)用注解“@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)訂閱者不再訂閱事件時(shí)趣兄,必須注銷,否則可能發(fā)生內(nèi)存泄漏
}
最后悼嫉,發(fā)送事件艇潭,每個(gè)訂閱者注冊的事件都將收到消息(會(huì)調(diào)用“@Subscribe”標(biāo)記的方法):
EventBus.getDefault().post(new MessageEvent());
舉例
現(xiàn)在來舉個(gè)栗子來展示EventBus的魅力!想想一個(gè)閱讀App看書的場景:左邊是書籍選擇列表戏蔑,右邊是書籍的內(nèi)容蹋凝。這時(shí)我們大多數(shù)情況都是用Fragment來處理,也即左右兩邊都是Fragment总棵,這就涉及兩個(gè)Fragment的通信問題鳍寂。我們通常的處理方式是在Fragment添加接口回調(diào),然后Activitty作為兩個(gè)Fragment的通信橋梁彻舰。
或許有人說直接在Activity獲取Fragment里的控件伐割,交互更加簡單。這樣雖然簡單刃唤,但Activity的職責(zé)變得很重隔心,而Fragment的作用之一就是分擔(dān)Activity的職責(zé),所以這是不可取的尚胞。
好硬霍,我們先來看不用EventBus時(shí)用回調(diào)的方式來實(shí)現(xiàn)Fragment的通信的例子。
首先先定義兩個(gè)Fragment笼裳,左邊的Fragment1唯卖,布局只有一個(gè)ListView(布局代碼不貼粱玲,后面有源碼),在Fragment1里定義一個(gè)接口拜轨,當(dāng)點(diǎn)擊ListView的item時(shí)抽减,就把所點(diǎn)擊的書籍回調(diào)給外部:
public class Fragment1 extends Fragment {
private final static String[] BOOKS = new String[] {
"第一行代碼",
"Java編程思想",
"Android開發(fā)藝術(shù)探索",
"Android源碼設(shè)計(jì)模式",
"算法",
"研磨設(shè)計(jì)模式"
};
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就更加簡單了,布局只有一個(gè)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作為兩個(gè)Fragment的通信橋梁卵沉,來實(shí)現(xiàn)兩個(gè)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);
}
}
從上面代碼可以看到,兩個(gè)Fragment的通信也非常簡單法牲,而Activity的職責(zé)也明顯降低史汗,只需作為橋梁,那么我們還有必要用EventBus嗎拒垃?答案是有必要的停撞!這只是一個(gè)例子,而實(shí)際開發(fā)中兩個(gè)Fragment通信交互幾乎不可能這么簡單悼瓮,往往是縱橫交錯(cuò)的戈毒,這時(shí)難道在Fragment上定義多個(gè)監(jiān)聽接口,然后在Activity上處理這些邏輯横堡?這就又導(dǎo)致了Activity職責(zé)過大副硅,而耦合度也顯著增加了。所以翅萤,使用回調(diào)的通信方式似乎適合交互較少的兩個(gè)Fragment,但一旦交互過多腊满,就無能為力了套么。下面再來看看EventBus怎么實(shí)現(xiàn)Fragment的通信。
首先碳蛋,先定義一個(gè)閱讀書籍的事件類(注意胚泌,最好用public修飾,否則有可能在生成索引時(shí)失敗肃弟,這些內(nèi)容在后續(xù)文章會(huì)講解玷室,現(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)擊書籍時(shí)發(fā)送事件:
public class Fragment1 extends Fragment {
private final static String[] BOOKS = new String[] {
"第一行代碼",
"Java編程思想",
"Android開發(fā)藝術(shù)探索",
"Android源碼設(shè)計(jì)模式",
"算法",
"研磨設(shè)計(jì)模式"
};
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很好地使兩個(gè)Fragment解耦,并優(yōu)雅地實(shí)現(xiàn)了兩者的通信部蛇,同時(shí)也減少了Fragment和Activity的耦合摊唇,真正的一舉多得!再想想兩個(gè)Activity之間的通信涯鲁,我們通常使用Intent作為載體巷查,來傳遞數(shù)據(jù),使用EventBus就可以拋開Intent了撮竿,直接實(shí)現(xiàn)Activity間的通信吮便!這里就不再舉例了,相信聰明的你能舉一反三幢踏。
至此髓需,相信大家對EventBus有了基礎(chǔ)的認(rèn)知了。
寫在最后
EventBus所帶來的便利和簡潔有沒有使你震驚房蝉?僚匆!別滿足的太早,EventBus如此受人待見搭幻,當(dāng)然有她的厲害之處咧擂。后續(xù)文章將會(huì)介紹使用編譯期處理生成索引來提升事件發(fā)送的效率,還有EventBus的高級配置檀蹋,敬請期待松申!
下一篇文章: EventBus源碼詳解(二):進(jìn)階使用