對(duì)于一個(gè)App,組件通信必不可少,通信類型可以分為點(diǎn)對(duì)點(diǎn)和點(diǎn)對(duì)面的的通信,點(diǎn)對(duì)點(diǎn)即只有唯一的接收者可以響應(yīng)消息,點(diǎn)對(duì)面則類似于消息廣播,即所有注冊(cè)過(guò)的都可以響應(yīng)消息。在Android中,通常使用消息機(jī)制來(lái)實(shí)現(xiàn),但消息機(jī)制的耦合度比較高。目前也有一些通信框架,如EventBus、Otto等事件總線框架,這些框架可以極大地降低組件間的耦合,但無(wú)法完美地實(shí)現(xiàn)點(diǎn)對(duì)點(diǎn)通信,因此建議消息機(jī)制和事件總線機(jī)制結(jié)合使用。
組件之間的通信
舉例:fragment和fragment之間的通信,需要間接地使用宿主activity。主要有兩種方法:
- 在A fragment中通過(guò)使用getActivity獲得宿主從而獲得宿主擁有的B fragment,進(jìn)而操作B fragment。
- 在A fragment中編寫接口,讓宿主activity實(shí)現(xiàn)該接口,然后再fragment中把a(bǔ)ctivity當(dāng)成該接口使用。
然后,推薦使用第二種方式。
- 從實(shí)際使用的體驗(yàn)上來(lái)說(shuō),這種方式從一定程度上滿足了解耦,如果更改了宿主activity,fragment的代碼不需要更改。
- 從設(shè)計(jì)模式的角度來(lái)說(shuō),一個(gè)最基本原則就是開(kāi)閉原則。開(kāi)閉原則是說(shuō)模塊應(yīng)該對(duì)擴(kuò)展開(kāi)放,而對(duì)修改關(guān)閉。模塊應(yīng)該盡量不修改代碼的情況下進(jìn)行擴(kuò)展。
- 第一種方式雖然行的通,但是明顯違反了設(shè)計(jì)模式的開(kāi)閉原則。fragment設(shè)計(jì)之初就是一個(gè)獨(dú)立的開(kāi)發(fā)組件,如果fragment要與宿主activity進(jìn)行交互,那么久需要知道activity是如何工作的,者本身就破壞了fragment的獨(dú)立性。也就是說(shuō)這個(gè)fragment只能被這個(gè)activity獨(dú)占,不能再在另一個(gè)activity中使用。如果另一個(gè)B activity中也需要一個(gè)這樣的fragment,只能再重復(fù)寫一個(gè)被獨(dú)占的fragment,復(fù)用性被破壞。
- 第二種方式,在fragment中定義了接口,由宿主activity來(lái)實(shí)現(xiàn)接口處理任務(wù)。如果想換一個(gè)宿主activity,只需要在新的activity中實(shí)現(xiàn)接口就好,fragment始終不需要改變。
- 第二種方式實(shí)際上就是設(shè)計(jì)模式中的代理模式。多一個(gè)代理類出來(lái),替原對(duì)象進(jìn)行一些操作。
EventBus
出名的框架為什么出名?正是因?yàn)樗谧裱瓨?biāo)準(zhǔn)設(shè)計(jì)模式的基礎(chǔ)上,又優(yōu)雅地解決了棘手的問(wèn)題。
https://github.com/greenrobot/EventBus
EventBus是一款針對(duì)Android優(yōu)化的發(fā)布/訂閱事件總線。主要功能是替代Intent,Handler,BroadCast在Fragment,Activity,Service,線程之間傳遞消息.優(yōu)點(diǎn)是開(kāi)銷小,代碼更優(yōu)雅。以及將發(fā)送者和接收者解耦。
使用
- 新建一個(gè)消息類,
構(gòu)造時(shí)傳進(jìn)去一個(gè)字符串,然后可以通過(guò)getMsg()獲取出來(lái)。
public class FirstEvent {
private String mMsg;
public FirstEvent(String msg) {
// TODO Auto-generated constructor stub
mMsg = msg;
}
public String getMsg(){
return mMsg;
}
}
- 注冊(cè)
在接收消息的Activity的OnCreate()函數(shù)中注冊(cè)EventBus,在OnDestroy()函數(shù)中反注冊(cè)。
public class MainActivity extends Activity {
Button btn;
TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//注冊(cè)EventBus
EventBus.getDefault().register(this);
btn = (Button) findViewById(R.id.btn_try);
tv = (TextView)findViewById(R.id.tv);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
Intent intent = new Intent(getApplicationContext(), SecondActivity.class);
startActivity(intent);
}
});
}
@Override
protected void onDestroy(){
super.onDestroy();
EventBus.getDefault().unregister(this);//反注冊(cè)EventBus
}
}
- 發(fā)送消息
是使用EventBus中的Post方法來(lái)實(shí)現(xiàn)發(fā)送的,發(fā)送過(guò)去的是我們新建的消息類的實(shí)例!
public class SecondActivity extends Activity {
private Button btn_FirstEvent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
btn_FirstEvent = (Button) findViewById(R.id.btn_first_event);
btn_FirstEvent.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
EventBus.getDefault().post(new FirstEvent("FirstEvent btn clicked"));
}
});
}
}
- 接收消息
在接收消息的activity中重寫onEventMainThread(FirstEvent event),參數(shù)就是自定義的消息類
public class MainActivity extends Activity {
Button btn;
TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
EventBus.getDefault().register(this);
btn = (Button) findViewById(R.id.btn_try);
tv = (TextView)findViewById(R.id.tv);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
Intent intent = new Intent(getApplicationContext(),
SecondActivity.class);
startActivity(intent);
}
});
}
public void onEventMainThread(FirstEvent event) {
String msg = "onEventMainThread收到了消息:" + event.getMsg();
Log.d("harvic", msg);
tv.setText(msg);
Toast.makeText(this, msg, Toast.LENGTH_LONG).show();
}
@Override
protected void onDestroy(){
super.onDestroy();
EventBus.getDefault().unregister(this);
}
}
詳解
所謂發(fā)布/訂閱時(shí)間總線,肯定是有兩方,一方發(fā)布,一方觀察并接收:
- 事件的發(fā)布:告知觀察者事件發(fā)生時(shí)通過(guò)EventBus.post函數(shù)實(shí)現(xiàn),這個(gè)過(guò)程叫做事件的發(fā)布
- 事件的接收:觀察者被告知事件發(fā)生叫做事件的接收
EventBus的接收函數(shù):
EventBus總共有4個(gè)函數(shù)可以接收post過(guò)來(lái)的的消息:
- onEvent
如果使用onEvent作為訂閱函數(shù),那么該事件在哪個(gè)線程發(fā)布出來(lái)的,onEvent就會(huì)在這個(gè)線程中運(yùn)行,也就是說(shuō)發(fā)布事件和接收事件線程在同一個(gè)線程。使用這個(gè)方法時(shí),在onEvent方法中不能執(zhí)行耗時(shí)操作,如果執(zhí)行耗時(shí)操作容易導(dǎo)致事件分發(fā)延遲。 - onEventMainThread
如果使用onEventMainThread作為訂閱函數(shù),那么不論事件是在哪個(gè)線程中發(fā)布出來(lái)的,onEventMainThread都會(huì)在UI線程中執(zhí)行,接收事件就會(huì)在UI線程中運(yùn)行,這個(gè)在Android中是非常有用的,因?yàn)樵贏ndroid中只能在UI線程中跟新UI,所以在onEvnetMainThread方法中是不能執(zhí)行耗時(shí)操作的。 - onEventBackgroundThread
如果使用onEventBackgrond作為訂閱函數(shù),那么如果事件是在UI線程中發(fā)布出來(lái)的,那么onEventBackground就會(huì)新建一個(gè)子線程再運(yùn)行onEventBackground,如果事件本來(lái)就是子線程中發(fā)布出來(lái)的,那么onEventBackground函數(shù)直接在該子線程中執(zhí)行。 - onEventAsync
使用這個(gè)函數(shù)作為訂閱函數(shù),那么無(wú)論事件在哪個(gè)線程發(fā)布,都會(huì)創(chuàng)建新的子線程在執(zhí)行onEventAsync。
EventBus的消息接收機(jī)制:
EventBus是怎么接收消息的?是根據(jù)參數(shù)中類的實(shí)例來(lái)判定的!
這么說(shuō)來(lái),這個(gè)消息類必定是耦合在 發(fā)布/訂閱 雙方之間的,被兩者獨(dú)占,不能被復(fù)用到其他消息通信之中。既然有4個(gè)接收函數(shù)都可以充當(dāng)接收器,所以當(dāng)我們?cè)诮邮諘r(shí),同一個(gè)類的實(shí)例參數(shù)有兩個(gè)函數(shù)來(lái)接收會(huì)怎樣?答案是2個(gè)方法都執(zhí)行。因?yàn)镋ventBus只認(rèn)4大接收方法中的實(shí)例參數(shù)。
這個(gè)例子中,三個(gè)方法都會(huì)被執(zhí)行。
public void onEventMainThread(SecondEvent event) {
Log.d("harvic", "onEventMainThread收到了消息:" + event.getMsg());
}
public void onEventBackgroundThread(SecondEvent event){
Log.d("harvic", "onEventBackground收到了消息:" + event.getMsg());
}
public void onEventAsync(SecondEvent event){
Log.d("harvic", "onEventAsync收到了消息:" + event.getMsg());
}
總結(jié)
EventBus在發(fā)送消息的時(shí)候,簡(jiǎn)化并統(tǒng)一了intent、handler、broadcast的操作;
在接收消息的時(shí)候,又將如何進(jìn)行事件處理的線程封裝成四個(gè)方法提供使用。