一 概述
BroadcastReceiver是安卓四大組件之一,它是一個(gè)全局的監(jiān)聽器,它能夠接收安卓系統(tǒng)、App內(nèi)以及其它App發(fā)出的廣播。安卓的廣播采用觀察者模式,基于消息的 訂閱 / 發(fā)布 事件模型,因此廣播的發(fā)送者和接收者完全解耦,易于使用和擴(kuò)展。
二 BroadcastReceiver使用流程
- 自定義BroadcastReceiver
我們要想使用BroadcastReceiver需要先定義個(gè)類繼承BroadcastReceiver然后實(shí)現(xiàn)其public void onReceive(Context context, Intent intent)方法。
廣播接收器接收到相應(yīng)的廣播后會(huì)自動(dòng)回調(diào)onReceive方法,該方法運(yùn)行在主線程中,因此不宜做耗時(shí)操作。
一般情況下在onReceive方法中會(huì)涉及到與其它組件的交互,比如打開Service、啟動(dòng)Activity等。
代碼示例
public class MyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
//獲得意圖(intent)中傳遞過(guò)來(lái)的action
String action = intent.getAction();
Log.e(getClass().getSimpleName(),"action:"+action);
//可以在意圖(intent)中使用bundle傳遞參數(shù)
Bundle extras = intent.getExtras();
String id = extras.getString("id");
String name = extras.getString("name");
Log.e(getClass().getSimpleName(),"id:"+id+",name:"+name);
}
}
- BroadcastReceiver注冊(cè)
我們自定義了BroadcastReceiver之后,還需要注冊(cè)一下才能收到對(duì)應(yīng)的廣播消息。廣播的注冊(cè)有兩種方式分別為:靜態(tài)注冊(cè)和動(dòng)態(tài)注冊(cè)。
- 靜態(tài)注冊(cè)
靜態(tài)注冊(cè)的方式是直接在xml文件中注冊(cè),示例如下:
<receiver
//廣播接收者的類名路徑
android:name=".MyReceiver"
//發(fā)送廣播的app應(yīng)該具有的權(quán)限
android:permission="String"
//廣播是否可用,為true時(shí)系統(tǒng)才會(huì)初始化該廣播
android:enabled="true"
//是否能接受其他app發(fā)出的廣播,當(dāng)有intent-filter時(shí)默認(rèn)為true
android:exported="true">
<intent-filter>
//接受到的廣播類型,可以使用系統(tǒng)已有的廣播類型也可以自定義
<action android:name="com.jrmf360.sendrp"/>
</intent-filter>
</receiver>
當(dāng)app啟動(dòng)時(shí)系統(tǒng)會(huì)自動(dòng)實(shí)例化該廣播類,并注冊(cè)到系統(tǒng)中
- 動(dòng)態(tài)注冊(cè)
動(dòng)態(tài)注冊(cè)就是在代碼中注冊(cè),這種方式比較靈活,當(dāng)用到廣播時(shí)才去注冊(cè)且可以反注冊(cè),廣播不會(huì)一直存在,節(jié)約內(nèi)存??磦€(gè)示例:
@Override
protected void onResume() {
super.onResume();
broadcastReceiver = new MyBroadcastReceiver();
//創(chuàng)建意圖過(guò)濾器并添加Action
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("com.jrmf360.sendrp");
//注冊(cè)BroadcastReceiver
registerReceiver(broadcastReceiver,intentFilter);
}
@Override
protected void onPause() {
super.onPause();
unregisterReceiver(broadcastReceiver);
}
class MyBroadcastReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Log.e(getClass().getSimpleName(),"action:"+action);
Bundle extras = intent.getExtras();
String id = extras.getString("id");
String name = extras.getString("name");
Log.e(getClass().getSimpleName(),"id:"+id+",name:"+name);
}
}
在onResume方法中注冊(cè)BroadcastReceiver在onPause方法中取消注冊(cè)BroadcastReceiver。注冊(cè)和反注冊(cè)必須成對(duì)出現(xiàn)以免造成內(nèi)存泄漏。
- 廣播種類
廣播分為普通廣播、有序廣播、系統(tǒng)廣播和App內(nèi)廣播,具體說(shuō)明如下:
- 普通廣播
開發(fā)者自定義的廣播,發(fā)送廣播如下:
//創(chuàng)建意圖并設(shè)置Action
Intent intent = new Intent();
//只有當(dāng)廣播的接收Action于此匹配才能接收到該廣播
intent.setAction("com.jrmf360.sendrp");
//使用bundle傳遞參數(shù)
Bundle bundle = new Bundle();
bundle.putString("id","123456");
bundle.putString("name","zhangsan");
intent.putExtras(bundle);
//發(fā)送廣播
sendBroadcast(intent);
- 有序廣播
發(fā)送出去的廣播按照先后順序被廣播接收者接收,有序是針對(duì)廣播接收者而言的。
接收規(guī)則:
按照Priority屬性值從大-小排序
如果Priority屬性值相同動(dòng)態(tài)廣播優(yōu)先
高優(yōu)先級(jí)的廣播可以更改廣播中的數(shù)據(jù)然后通過(guò)public final void setResult (int code, String data, Bundle extras)方法把更改后的結(jié)果傳遞給下一級(jí)廣播;高優(yōu)先級(jí)的廣播還可以通過(guò)調(diào)用abortBroadcast()方法攔截廣播,后續(xù)所有的廣播接收者都將收不到該廣播。 - 系統(tǒng)廣播
安卓系統(tǒng)內(nèi)置了很多系統(tǒng)廣播,只要涉及到手機(jī)的基本操作基本都會(huì)發(fā)出對(duì)應(yīng)的廣播,例如:開關(guān)機(jī)、亮屏以及音量變化等。每個(gè)廣播都有Intent - Filter(包括具體的action),安卓中一些內(nèi)置的Action如下:
| 系統(tǒng)操作 | action |
|---|---|
| 網(wǎng)絡(luò)變化 | android.net.conn.CONNECTIVITY_CHANGE |
| 充電時(shí)或電量發(fā)生變化 | Intent.ACTION_BATTERY_CHANGED |
| 屏幕被關(guān)閉 | Intent.ACTION_SCREEN_OFF |
| 屏幕被打開 | Intent.ACTION_SCREEN_ON |
| 系統(tǒng)啟動(dòng)完成后(僅廣播一次) | Intent.ACTION_BOOT_COMPLETED |
- 應(yīng)用內(nèi)廣播
當(dāng)我們使用自定義廣播時(shí)可能會(huì)出現(xiàn)一些問(wèn)題:
其它App發(fā)出和我們廣播接收者相同的Action導(dǎo)致我們不斷地收到廣播并處理;
其它App定義和我們相同的廣播接收者導(dǎo)致我們的廣播信息暴露。
要解決以上問(wèn)題我們就需要使用到應(yīng)用內(nèi)廣播,所謂應(yīng)用內(nèi)廣播保證了我們App發(fā)出的廣播在其它App內(nèi)的廣播接收者收不到并且其它App發(fā)出的廣播我們App內(nèi)的廣播接收者不接收。App內(nèi)廣播的實(shí)現(xiàn)有兩種方式:
方式一:
在注冊(cè)廣播時(shí)android:exported="false"這樣就接收不到其它App發(fā)出的廣播了,如下:
<receiver
//廣播接收者的類名路徑
android:name=".MyReceiver"
//發(fā)送廣播的app應(yīng)該具有的權(quán)限
android:permission="com.jrmf360.permission.send"
//廣播是否可用,為true時(shí)系統(tǒng)才會(huì)初始化該廣播
android:enabled="true"
//是否能接受其他app發(fā)出的廣播,當(dāng)有intent-filter時(shí)默認(rèn)為true
android:exported="false">
<intent-filter>
//接受到的廣播類型,可以使用系統(tǒng)已有的廣播類型也可以自定義
<action android:name="com.jrmf360.sendrp"/>
</intent-filter>
</receiver>
單單接收不到其它App發(fā)出的廣播還不能滿足我們的要求,還需要我們自己發(fā)出的廣播其它App接收不到,這就需要在發(fā)送廣播的時(shí)候設(shè)置包名:
Intent intent = new Intent();
intent.setAction("com.jrmf360.sendrp");
Bundle bundle = new Bundle();
bundle.putString("id","123456");
bundle.putString("name","zhangsan");
intent.putExtras(bundle);
//設(shè)置包名
intent.setPackage("應(yīng)用包名");
sendBroadcast(intent);
方式二:
使用LocalBroadcastManager也可以實(shí)現(xiàn)應(yīng)用內(nèi)廣播,需要注意的是只有動(dòng)態(tài)注冊(cè)的廣播才能使用LocalBroadcastManager并且廣播的注冊(cè),發(fā)送和反注冊(cè)都要使用LocalBroadcastManager.getInstance(this)的單例對(duì)象去完成。示例如下:
@Override
protected void onResume() {
super.onResume();
broadcastReceiver = new MyBroadcastReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("com.jrmf360.sendrp");
LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver,intentFilter);
}
@Override
protected void onPause() {
super.onPause();
LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcastReceiver);
}
@Override
public void onClick(View v) {
int id = v.getId();
if (id == R.id.btn_sendBroadcast){
Intent intent = new Intent();
intent.setAction("com.jrmf360.sendrp");
Bundle bundle = new Bundle();
bundle.putString("id","123456");
bundle.putString("name","zhangsan");
intent.putExtras(bundle);
sendBroadcast(intent);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
}
}
- 廣播的權(quán)限
廣播的權(quán)限分為廣播發(fā)送者要求廣播接收者具有的權(quán)限和廣播接收者要求廣播發(fā)送者具有的權(quán)限。
- 廣播接收者要求廣播發(fā)送者具有的權(quán)限
在我們?cè)趚ml文件中注冊(cè)廣播接收者的時(shí)候有這樣一行屬性android:permission="String"我們可以在此添加對(duì)應(yīng)的權(quán)限,這就要求發(fā)出廣播的App必須擁有此權(quán)限,下面演示一下流程:
先在xml中聲明廣播接收者并添加權(quán)限
<receiver
android:name=".MyReceiver2"
//添加自定義權(quán)限
android:permission="com.jrmf360.permission.send2"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.jrmf360.sendrp"/>
</intent-filter>
</receiver>
然后我們需要在xml文件中聲明該權(quán)限
<!--權(quán)限聲明-->
<permission android:name="com.jrmf360.permission.send2"/>
最后廣播發(fā)送者所在的App需要在xml中注冊(cè)該權(quán)限
<uses-permission android:name="com.jrmf360.permission.send2"/>
- 廣播發(fā)送者要求廣播接收者具有的權(quán)限
首先調(diào)用發(fā)送廣播方法時(shí)我們需要傳入對(duì)應(yīng)的權(quán)限
sendBroadcast(intent,"com.jrmf360.rp");
接著在xml文件中聲明該權(quán)限
<permission android:name="com.jrmf360.rp"/>
最后廣播接收者所在的App需要在xml中注冊(cè)該權(quán)限
<!--權(quán)限注冊(cè)-->
<uses-permission android:name="com.jrmf360.rp"/>
廣播的權(quán)限是對(duì)廣播接收者隨意接收廣播的限制和保護(hù)。
總結(jié):廣播可以跨線程、進(jìn)程和App通信,我們?cè)陂_發(fā)過(guò)程中可以很方便的利用它來(lái)傳遞消息。但我們也不能濫用廣播可能會(huì)導(dǎo)致接收消息的地方過(guò)多不好維護(hù)而且廣播接收者相對(duì)是個(gè)比較重的組件,比如線程間通信我們可以通過(guò)Handler解決,沒(méi)有必要使用廣播。