BroadcastReceiver作為Android四大組件之一埃难,在Android日常開發(fā)中有著不可或缺的作用,主要做以下用途:
- 應(yīng)用內(nèi)消息傳遞涤久,多進(jìn)程之間消息通信
- 應(yīng)用之間消息傳遞涡尘,不同App之間消息傳遞
- Android系統(tǒng)和應(yīng)用之間消息傳遞,比如接收網(wǎng)絡(luò)狀態(tài)通知响迂,開機(jī)關(guān)機(jī)通知等
1. 廣播的分類
1.1 Normal Broadcast 普通廣播
普通廣播,也叫標(biāo)準(zhǔn)廣播考抄,由開發(fā)者自定義發(fā)出,執(zhí)行在和接受者異步線程的廣播蔗彤,在廣播發(fā)出后川梅,所有的接受者均可以接受到該廣播通知,接受者之間沒有先后順序然遏,且無法被截?cái)嗥锻尽F胀ㄗ?cè)形式有兩種,一種為動(dòng)態(tài)注冊(cè)待侵,一種為靜態(tài)注冊(cè)丢早。
1.1.1 動(dòng)態(tài)注冊(cè)廣播
動(dòng)態(tài)注冊(cè)可以在java代碼中,通過繼承自BroadcastReceiver的類實(shí)例秧倾,以及指定Action的IntentFilter去進(jìn)行注冊(cè)怨酝,這里需要注意,在Destroy的時(shí)候需要將該廣播進(jìn)行unRegister中狂,否則會(huì)導(dǎo)致內(nèi)存泄漏等問題凫碌。
/**
* 注冊(cè)廣播接收機(jī)
*/
private void registerBroadcast() {
broadcastReceiver = new TestBroadcastReceiver();
// 指定Action,這里使用系統(tǒng)廣播Action胃榕,也可以使用自定義
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(android.content.Intent.ACTION_AIRPLANE_MODE_CHANGED);
registerReceiver(broadcastReceiver, intentFilter);
}
這里使用的注冊(cè)方法盛险,還有其余構(gòu)造參數(shù)瞄摊,例如:
/**
* 廣播注冊(cè)
* @param receiver 廣播接收器
* @param filter 接受廣播的條件過濾
* @param broadcastPermission 接受廣播的權(quán)限,只能接受相應(yīng)權(quán)限的廣播發(fā)送器發(fā)送的廣播
* @param scheduler 默認(rèn)為主線程苦掘,也可使用其他線程創(chuàng)建的handler
* @return
*/
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
String broadcastPermission, Handler scheduler)
1.1.2 靜態(tài)注冊(cè)廣播
在清單文件AndroidManifest.xml中進(jìn)行動(dòng)態(tài)注冊(cè)换帜,使用<receiver>標(biāo)簽進(jìn)行聲明,并在標(biāo)簽內(nèi)鹤啡,使用<inter-filter>標(biāo)簽設(shè)置過濾器惯驼。
<receiver
android:name="com.jd.test.receiver.TestBroadcastReceiver"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.ACTION_AIRPLANE_MODE_CHANGED" />
</intent-filter>
</receiver>
其中exported設(shè)置該廣播能否接受其他應(yīng)用的廣播消息,前提是filter中name名稱一致递瑰。當(dāng)廣播中有默認(rèn)IntentFilter祟牲,該值默認(rèn)為true,否則為false抖部。同樣還可以設(shè)置:
- android:permission 廣播權(quán)限設(shè)置说贝,具有相應(yīng)廣播發(fā)送權(quán)限的廣播發(fā)送方,發(fā)送的廣播才能被該廣播接收器接收慎颗。
- android:process 設(shè)置廣播運(yùn)行所處的進(jìn)程乡恕,默認(rèn)為app進(jìn)程,可以指定獨(dú)立的進(jìn)程
這里我們?cè)O(shè)置的監(jiān)聽信息為系統(tǒng)廣播俯萎,監(jiān)聽飛行模式的變化傲宜。可以在onReceive中打印輸出變化狀態(tài)
1.2 System Broadcast 系統(tǒng)廣播
系統(tǒng)廣播夫啊,Android系統(tǒng)內(nèi)設(shè)置了許多個(gè)系統(tǒng)廣播函卒,比如開關(guān)機(jī),網(wǎng)絡(luò)狀態(tài)變化等涮母,每一個(gè)廣播谆趾,均有一個(gè)相應(yīng)的intent-filter,其中包含了具體的action叛本,由Android系統(tǒng)發(fā)出沪蓬。應(yīng)用內(nèi)注冊(cè)了相應(yīng)的BroadcastReceiver,會(huì)收到該通知,例如上述設(shè)置的監(jiān)聽系統(tǒng)飛行模式的廣播来候□尾妫可以監(jiān)測(cè)的還有其他類型的廣播:
// 關(guān)閉或打開飛行模式時(shí)的廣播
Intent.ACTION_AIRPLANE_MODE_CHANGED;
// 充電狀態(tài),或者電池的電量發(fā)生變化
Intent.ACTION_BATTERY_CHANGED;
// 表示電池電量低
Intent.ACTION_BATTERY_LOW;
// 表示電池電量充足营搅,即從電池電量低變化到飽滿時(shí)會(huì)發(fā)出廣播
Intent.ACTION_BATTERY_OKAY;
// 在系統(tǒng)啟動(dòng)完成后云挟,這個(gè)動(dòng)作被廣播一次(只有一次)
Intent.ACTION_BOOT_COMPLETED;
// 這樣的廣播還有很多
........
在Android8.0的升級(jí)后,對(duì)廣播機(jī)制進(jìn)行了限制转质,因?yàn)榇罅康膹V播通過靜態(tài)注冊(cè)园欣,會(huì)導(dǎo)致所有應(yīng)用快速地連續(xù)消耗資源,降低用戶體驗(yàn)休蟹,所以鼓勵(lì)使用動(dòng)態(tài)注冊(cè)沸枯。
兼容:
如果遇到靜態(tài)廣播無法修改日矫,或者需要兼容靜態(tài)之前靜態(tài)廣播時(shí),可以使用intent.setComponent()方法绑榴,在發(fā)送廣播之前哪轿,進(jìn)行設(shè)置,該方法可以在8.0上兼容之前版本設(shè)置隱式廣播翔怎。
1.3 Ordered broadcast 有序廣播
有序廣播是一種同步執(zhí)行的廣播窃诉,在廣播發(fā)出后,同一時(shí)間只有一個(gè)廣播接收器會(huì)接受到廣播赤套,優(yōu)先級(jí)較高的接收器飘痛,先接受到廣播。此時(shí)該廣播接收器于毙,可以選擇敦冬,截?cái)鄰V播,還是繼續(xù)傳遞廣播唯沮,如果選擇截?cái)鄰V播,則后續(xù)優(yōu)先級(jí)較低的廣播堪遂,將不會(huì)收到該廣播信息介蛉。
// 設(shè)置優(yōu)先級(jí)
intentFilter.setPriority(20);
// 判斷是否為有序廣播
broadcastReceiver.isOrderedBroadcast();
// 取消廣播
broadcastReceiver.abortBroadcast();
取消廣播實(shí)際為設(shè)置廣播內(nèi)部PendingResult的標(biāo)志位為取消
mPendingResult.mAbortBroadcast = true;
上述,簡(jiǎn)單的描述了廣播的使用方法溶褪,顯式注冊(cè)和隱式注冊(cè)币旧。而廣播是在設(shè)計(jì)之初,就是針對(duì)應(yīng)用與系統(tǒng)之間猿妈,應(yīng)用與應(yīng)用之間吹菱,應(yīng)用內(nèi)部等進(jìn)行通信,但是也正是這樣的設(shè)計(jì)模式彭则,對(duì)使用BroadcastReceiver的應(yīng)用程序鳍刷,帶來了風(fēng)險(xiǎn)。如果有人通過某些手段俯抖,得到了你應(yīng)用監(jiān)聽的廣播名稱和過濾器名稱输瓜,那么便可以通過發(fā)送指定的廣播,對(duì)你的應(yīng)用造成“破壞”芬萍。為了防止出現(xiàn)這樣的問題尤揣,我們需要做到:
- 設(shè)置exported為false,默認(rèn)至接受當(dāng)前應(yīng)用的廣播柬祠,其他應(yīng)用發(fā)出的廣播不接受北戏。
- 在自定義廣播發(fā)送和接收時(shí),添加指定的permission漫蛔,進(jìn)行校驗(yàn)
- 在應(yīng)用內(nèi)部發(fā)送廣播時(shí)嗜愈,對(duì)包名進(jìn)行指定旧蛾,調(diào)用intent.setPackage()這樣只對(duì)指定包中注冊(cè)的接收器進(jìn)行接受
- 如果只是應(yīng)用內(nèi)部廣播的話,可以直接使用LocalBroadcastManager芝硬,本地廣播蚜点,保證廣播發(fā)送者和接收者在同一個(gè)App。
1.4 Local Broadcast 應(yīng)用內(nèi)廣播
應(yīng)用內(nèi)廣播為Google向開發(fā)者提供的一個(gè)輕量型廣播拌阴,只能用于單個(gè)應(yīng)用內(nèi)消息傳遞绍绘,無法靜態(tài)注冊(cè),僅支持動(dòng)態(tài)注冊(cè)迟赃。
// 使用LocalBroadcastManager發(fā)送廣播
Intent intent = new Intent();
intent.setAction(TEST_ACTION);
LocalBroadcastManager.getInstance(MainActivity.this).sendBroadcast(intent);
只能使用LocalBroadcastManager自己的單例進(jìn)行廣播發(fā)送陪拘,也只能對(duì)LocalBroadcastManager注冊(cè)過的接收器,才能收到該廣播纤壁。
// 指定Action左刽,這里使用系統(tǒng)廣播Action,也可以使用自定義
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(TEST_ACTION);
// BroadcastReceiver
broadcastReceiver = new TestBroadcastReceiver();
// 注冊(cè)廣播
LocalBroadcastManager.getInstance(this).registerReceiver(broadcastReceiver, intentFilter);
在Activity銷毀時(shí)酌媒,千萬記得將當(dāng)前注冊(cè)廣播進(jìn)行銷毀欠痴,否則可能會(huì)造成內(nèi)存泄漏等問題。
// 注銷廣播
if (broadcastReceiver != null) {
LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcastReceiver);
}
推薦在應(yīng)用內(nèi)部使用LocalBroadcastManager秒咨,它有以下優(yōu)點(diǎn):
- 避免自定義廣播被外部應(yīng)用意外接收
- 避免應(yīng)用內(nèi)部信息泄漏
- 使用主線程Looper進(jìn)行循環(huán)發(fā)送廣播喇辽,高效簡(jiǎn)潔
最后,在老版本中雨席,這里還有一種廣播類型菩咨,Sticky Broadcast,也就是粘滯性廣播陡厘,由于該廣播在Android5.0也就是API 21之后已經(jīng)不再使用抽米,因此,這里對(duì)該廣播將不再贅述糙置。
1.5 注意事項(xiàng)
對(duì)于需要使用BroadcastReceiver的地方云茸,為了安全高效,需要注意以下幾點(diǎn):
- 設(shè)置exported為false罢低,不監(jiān)聽其他應(yīng)用廣播
- 權(quán)限設(shè)置查辩,發(fā)送廣播時(shí)区匠,使用permission進(jìn)行校驗(yàn)
- 包名設(shè)置驹针,使用intent.setPackage()指定包名
- 使用LocalBroadcastManager
- onReceive中不能執(zhí)行耗時(shí)操作枢劝,如果耗時(shí)超過10s會(huì)彈出ANR氧秘。
- onReceive中的Context括儒,如果是動(dòng)態(tài)注冊(cè)令漂,則為注冊(cè)組建的Context楼咳,例如Activity或者是Service娃弓,如果是靜態(tài)注冊(cè)辟汰,則為ReceiverRestrictedContext列敲,在這里如果需要啟動(dòng)Activity阱佛,需要添加Flag——Intent.FLAG_ACTIVITY_NEW_TASK
- 普通廣播是并列無序執(zhí)行的,有序廣播是串行有序執(zhí)行的戴而,優(yōu)先級(jí)較高的先執(zhí)行凑术。
- 使用Context.sendBroadcast發(fā)送的廣播,如果依次注冊(cè)了三個(gè)廣播接收者A/B/C,那么接受順序是C/B/A所意,即后注冊(cè)的先收到消息淮逊。
- 使用LocalBroadcastManager.getInstance(MainActivity.this).sendBroadcast(intent)發(fā)送廣播的,如果依次注冊(cè)了三個(gè)廣播接收者A/B/C,那么接受順序是A/B/C扶踊,即先注冊(cè)的先收到消息