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