一、Android 廣播機(jī)制包括 3 個(gè)基本要素:
發(fā)送廣播的 Broadcast;
接受廣播的 BroadcastReceiver;
用于傳遞信息的 Intent;
二、發(fā)送廣播
Android 為應(yīng)用程序發(fā)送廣播提供了三種方式:
2.1 sendOrderedBroadcast(Intent, String) — 有序廣播
sendOrderedBroadcast(Intent, String)
方法一次向一個(gè)接收器發(fā)送廣播。當(dāng)每個(gè)接收器依次執(zhí)行時(shí),它可以將結(jié)果傳播到下一個(gè)接收器,它也可以完全中止廣播,使其不會(huì)傳遞給其他接收器。receiver 的運(yùn)行順序可以通過匹配的 intent-filter 的 android:priority 屬性進(jìn)行控制;具有相同優(yōu)先級的接收器將以任意順序運(yùn)行。
2.2 sendBroadcast(Intent) — 普通廣播
sendBroadcast(Intent)
方法以隨機(jī)的順序向所有接收者發(fā)送廣播。這被稱為普通廣播。這樣更有效率,但意味著 receiver 無法讀取其他 receiver 的結(jié)果、傳播從其他廣播接收的數(shù)據(jù)以及中止廣播。
2.3 LocalBroadcastManager.sendBroadcast — 本地廣播
LocalBroadcastManager.sendBroadcast()
方法將廣播發(fā)送到與發(fā)送者位于同一應(yīng)用程序中的接收者。如果你不需要跨應(yīng)用發(fā)送廣播,請使用本地廣播。實(shí)現(xiàn)效率更高(不需要進(jìn)程間通信),而且無需擔(dān)心其他應(yīng)用程序能夠接收或發(fā)送你的廣播。
以下代碼片段演示了如何通過創(chuàng)建一個(gè) Intent 并調(diào)用 sendBroadcast(Intent) 來發(fā)送廣播。
Intent intent = new Intent();
intent.setAction("com.example.broadcast.MY_NOTIFICATION");
intent.putExtra("data","Notice me senpai!");
sendBroadcast(intent);
廣播消息被包裹在一個(gè) Intent 對象中。Intent 的 action 字符串必須提供應(yīng)用程序的 Java 包名稱并唯一標(biāo)識廣播事件。你可以將附加信息通過 putExtra(String, Bundle)
添加到 intent 中。你也可以通過調(diào)用 setPackage(String)
限制發(fā)送的應(yīng)用程序。
注意:盡管 intent 用于發(fā)送廣播和啟動(dòng)活動(dòng)
(startActivity(Intent))
,但這些操作完全不相關(guān)。
三、使用權(quán)限限制廣播
權(quán)限允許你將廣播限制為擁有特定權(quán)限的應(yīng)用程序集。你可以對廣播的發(fā)送者或接收者實(shí)施限制。
3.1 帶權(quán)限發(fā)送
當(dāng)你調(diào)用 sendBroadcast(Intent, String)
或者 sendOrderedBroadcast(Intent, String, BroadcastReceiver, Handler, int, String, Bundle)
,你可以指定一個(gè)權(quán)限參數(shù)。只有接收方已經(jīng)在 manifest 中標(biāo)記了權(quán)限(并且授予許可,如果該權(quán)限存在危險(xiǎn))才可以接收廣播。例如,下面的代碼發(fā)送一個(gè)廣播:
sendBroadcast(new Intent("com.example.NOTIFY"),
Manifest.permission.SEND_SMS);
要接收廣播,應(yīng)用程序必須請求權(quán)限,如下所示:
<uses-permission android:name="android.permission.SEND_SMS"/>
注意:自定義權(quán)限是在安裝應(yīng)用程序時(shí)注冊的。定義自定義權(quán)限的應(yīng)用程序必須在使用之前安裝。
3.2 帶權(quán)限接收
如果你在注冊 receiver 時(shí)指定一個(gè)權(quán)限參數(shù)(無論是在 manifest 的 <receiver> 標(biāo)簽中,還是通過 registerReceiver(BroadcastReceiver, IntentFilter, String, Handler))
,那么只有在 manifest 中通過 <uses-permission> 標(biāo)簽請求該權(quán)限的應(yīng)用程序 (并且授予許可,如果該權(quán)限存在危險(xiǎn))可以發(fā)送 intent 給該 receiver。
例如,假設(shè)你的接收應(yīng)用程序具有如下所示的接收器聲明:
<receiver android:name=".MyBroadcastReceiver"
android:permission="android.permission.SEND_SMS">
<intent-filter>
<action android:name="android.intent.action.AIRPLANE_MODE"/>
</intent-filter>
</receiver>
或者你的接收應(yīng)用程序有一個(gè)上下文注冊的接收器,如下所示:
IntentFilter filter = new IntentFilter(Intent.ACTION_AIRPLANE_MODE_CHANGED);
registerReceiver(receiver, filter, Manifest.permission.SEND_SMS, null );
那么,為了能夠向這些接收方發(fā)送廣播,發(fā)送方必須請求權(quán)限,如下所示:
<uses-permission android:name="android.permission.SEND_SMS"/>
四、安全考慮和最佳做法
以下是發(fā)送和接收廣播的一些安全考慮事項(xiàng)和最佳做法:
如果你不需要將廣播發(fā)送到你的應(yīng)用以外的組件,則可以使用
LocalBroadcastManager
發(fā)送和接收本地廣播。LocalBroadcastManager
效率要高得多(不需要進(jìn)程間通信),并可以讓你避免其他應(yīng)用程序能夠接收或發(fā)送你的廣播。本地廣播可以在你的應(yīng)用程序中作為通用的 pub/sub event bus,而無需系統(tǒng)廣播的任何開銷。如果許多應(yīng)用程序已經(jīng)在 manifest 中注冊相同的廣播,則可能會(huì)導(dǎo)致系統(tǒng)啟動(dòng)大量應(yīng)用程序,從而對設(shè)備性能和用戶體驗(yàn)產(chǎn)生重大影響。為了避免這種情況,優(yōu)先使用 context 方式注冊廣播。有時(shí),Android 系統(tǒng)本身會(huì)強(qiáng)制使用上下文注冊的接收器。例如,
CONNECTIVITY_ACTION
廣播僅傳送給上下文注冊的接收器。-
不要使用隱式 intent 傳播敏感信息。任何注冊了接收該廣播的應(yīng)用程序都可以讀取信息。有三種方法可以限制哪些應(yīng)用能夠接收你的廣播:
可以在發(fā)送廣播時(shí)指定權(quán)限。
在 Android 4.0 及更高版本,可以通過
setPackage(String)
指定包名。可以使用
LocalBroadcastManager
發(fā)送廣播。
-
當(dāng)你注冊 receiver 時(shí),任何應(yīng)用程序都可能將潛在的惡意廣播發(fā)送到你應(yīng)用的 receiver。有三種方法可以限制你的應(yīng)用可以收到哪些廣播:**
可以在注冊廣播接收器時(shí)指定權(quán)限。
對于 manifest 聲明的 receiver,可以在清單中將
android:exported
屬性設(shè)置為 “false”。這樣 receiver 就不接收來自應(yīng)用程序外部的廣播。可以通過
LocalBroadcastManager
限制只有本地廣播。
廣播動(dòng)作 (action) 的命名空間是全局的。確保動(dòng)作名稱和其他字符串被寫入您擁有的名稱空間中,否則可能會(huì)無意中與其他應(yīng)用程序發(fā)生沖突。
-
因?yàn)?receiver 的
onReceive(Context, Intent)
方法在主線程上運(yùn)行,所以它應(yīng)該快速執(zhí)行并返回。如果你需要執(zhí)行長時(shí)間運(yùn)行的工作,請注意開啟子線程或啟動(dòng)后臺服務(wù),因?yàn)橄到y(tǒng)可能會(huì)在onReceive()
返回后終止整個(gè)進(jìn)程 。建議:在 receiver 的
onReceive()
方法中調(diào)用goAsync()
方法并傳遞BroadcastReceiver.PendingResult
給后臺線程。這使廣播從onReceive()
返回后依舊保持活動(dòng)狀態(tài)。但是,即使采用這種方法,系統(tǒng)也希望你能夠很快結(jié)束廣播(不到 10 秒)。不過,它確實(shí)允許你將工作移至另一個(gè)線程以避免妨礙主線程。使用
JobScheduler
安排工作。
不要從廣播接收器啟動(dòng)活動(dòng),因?yàn)楹苡绊懹脩趔w驗(yàn);特別是如果有多個(gè)接收器的話。相反,請考慮執(zhí)行顯示通知等簡單操作。