一、基本概念
1.1 實(shí)現(xiàn)廣播接收者
首先,我們需要創(chuàng)建一個(gè)廣播接收者,繼承于BroadcastReceiver并重寫它的onReceive方法。
public class MyBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {}
}
在創(chuàng)建完接收者之后,還需要進(jìn)行注冊,告訴系統(tǒng)有這個(gè)監(jiān)聽者。廣播注冊的方式分為:靜態(tài)注冊和動態(tài)注冊。
靜態(tài)注冊
靜態(tài)注冊在AndroidManifest.xml中進(jìn)行指定。
<receiver android:enabled=["true" | "false"]
android:exported=["true" | "false"]
android:icon="drawable resource"
android:label="string resource"
android:name="string"
android:permission="string"
android:process="string" >
. . .
</receiver>
exported:exported決定了 廣播接收者所能收到廣播的范圍,假如為false,那么只有同一個(gè)App,或者userId相同的App發(fā)出的廣播它才能夠收到,并不是指同一個(gè)進(jìn)程。exported一般情況下默認(rèn)為false,唯一例外的是假如設(shè)置了intent-filter,那么默認(rèn)值為true。對于靜態(tài)注冊的廣播,在Android 3.1之后,應(yīng)用如果沒有啟動并且Intent中包含了FLAG_INCLUDE_STOPPED_PACKAGES屬性,那么會先調(diào)起應(yīng)用,否則在應(yīng)用沒有啟動的情況下將無法收到廣播。permission:如果設(shè)置了permission,那么只有 具有相應(yīng)權(quán)限的廣播發(fā)送方 發(fā)送的廣播才能被此BroadcastReceiver接收。Android廣播的權(quán)限機(jī)制是雙向的,即我們既可以 要求發(fā)送者具有權(quán)限,也可以 要求接收者具有權(quán)限,才能完成端到端的通信過程,這里就是 要求發(fā)送者具有權(quán)限。process:運(yùn)行所處的進(jìn)程,默認(rèn)為App的進(jìn)程。
動態(tài)注冊
動態(tài)注冊的廣播無需在AndroidManifest.xml進(jìn)行聲明,在代碼中進(jìn)行注冊和注銷即可。
//注冊廣播。
registerReceiver(BroadcastReceiver receiver, IntentFilter filter)
//注銷廣播。
unregisterReceiver(BroadcastReceiver receiver);
1.2 發(fā)送廣播
廣播的發(fā)送者通過Intent將其意圖發(fā)送出去,系統(tǒng)找到匹配的接收者,發(fā)送廣播的一般方式如下:
Intent intent = new Intent();
intent.setAction(INTENT_ACTION);
sendBroadcast(intent);
1.3 廣播類型
廣播可以分為以下幾類:無序廣播、有序廣播和粘性廣播。
1.3.1 無序廣播
無序廣播指的是所有廣播接收者收到廣播的順序是沒有規(guī)律的。
1.3.2 有序廣播
有序廣播指的是發(fā)送出的廣播被BroadcastReceiver按照priority從大到小的順序接收,當(dāng)priority相同時(shí),動態(tài)廣播優(yōu)先靜態(tài)廣播,發(fā)送有序廣播的方式為:
sendOrderedBroadcast(intent, receiverPermission, ...)
對于有序廣播有一個(gè)特點(diǎn),先接收的BroadcastReceiver具有攔截廣播的權(quán)利,攔截的方法為onReceive方法中調(diào)用abortBroadcast()方法。
1.3.3 粘性廣播
已經(jīng)廢棄,它是用來處理先收到廣播然后才注冊的情況。
1.4 應(yīng)用內(nèi)廣播
假如exported屬性為true,那么是允許兩個(gè)不同應(yīng)用通過廣播進(jìn)行通信的。就可能出現(xiàn) 安全隱患:
- 其他
App可能會針對性地發(fā)出與當(dāng)前App的intent-filter相匹配的廣播,導(dǎo)致當(dāng)前App不斷接收到廣播并處理。 - 其他
App可以注冊與當(dāng)前App相匹配的intent-filter,從而獲取廣播具體信息。
為了避免出現(xiàn)以上的安全問題,有以下的解決方法:
- 設(shè)置
exported屬性為false。 - 設(shè)置權(quán)限。
- 發(fā)送廣播時(shí),指定具體的包名。
假如我們的廣播只需要在應(yīng)用內(nèi)部通信,那么可以采用封裝好的LocalBroadcastManager類,用于解決安全問題。
傳統(tǒng)的廣播是通過Binder來實(shí)現(xiàn)的,而LocalBroadcastManager則是采用Handler的方式。當(dāng)注冊廣播的時(shí)候,其實(shí)將Receiver添加到單例對象LocalBroadcastManager維護(hù)的列表當(dāng)中,發(fā)送消息的時(shí)候,通過Receiver所關(guān)聯(lián)的action找到它,最后回調(diào)它的onReceive方法。
因此,只有通過LocalBroadcastManager注冊的BroadcastReceiver才能收到通過LocalBroadcastManager發(fā)出的廣播。
具體的代碼實(shí)現(xiàn)可以參考這篇文章 LocalBroadcastManager 的實(shí)現(xiàn)原理,還是 Binder?。
二、一些需要注意的點(diǎn)
2.1 權(quán)限問題
通過權(quán)限可以也可以解決我們之前談到的安全問題,Broadcast的權(quán)限是雙向的。
2.1.1 要求接收者具有權(quán)限
這種方式 用于防止廣播信息泄露。
在 發(fā)送者 的AndroidManifest.xml中定義權(quán)限。
<permission android:name = "com.android.permission.RECV_XXX"/>
發(fā)送者在發(fā)送廣播的時(shí)候,采用帶有權(quán)限的接口進(jìn)行發(fā)送。
sendBroadcast("com.android.XXX_ACTION", "com.android.permission.RECV_XXX");
接收者 如果希望能收到廣播,那么需要在它的AndroidManifest.xml進(jìn)行聲明使用該權(quán)限。
<uses-permission android:name="com.android.permission.RECV_XXX"></uses-permission>
2.1.2 要求發(fā)送者具有權(quán)限
這種方式 用于防止外部應(yīng)用惡意地發(fā)送廣播,導(dǎo)致接收者一直在處理。
在 接收者 的AndroidManifest.xml中定義權(quán)限。
<permission android:name="com.android.SEND_XXX"/>
在接收者的AndroidManifest.xml聲明BroadcastReceiver的時(shí)候,通過permission字段指定權(quán)限。
<receiver android:name=".XXXReceiver"
android:permission="com.android.permission.SEND_XXX">
<intent-filter>
<action android:name="com.android.XXX_ACTION" />
</intent-filter>
</receiver>
發(fā)送者 如果希望廣播能被該接收者收到,那么需要在AndroidManifest.xml中聲明使用該權(quán)限。
<uses-permission android:name="com.android.permission.SEND_XXX“></users-permission>
2.2 ANR
在BroadcastReceiver方法中,不要進(jìn)行耗時(shí)的操作,超過10s會發(fā)生BroadcastQueue Timeout的ANR異常。
2.3 onReceive 傳入的 Context
靜態(tài)注冊的
BroadcastReceiver,其Context是android.app.ReceiverRestrictedContext。動態(tài)注冊的普通
BroadcastReceiver,與調(diào)用registerReceiver方法的Context有關(guān),如果是通過Application Context注冊的,那么Context是Application Context,如果是Activity Context,那么其Context是Activity Context。LocalBroadcastManager動態(tài)注冊的BroadcastReceiver,其Context是Application Context。
三、參考文章
Android Broadcast 和 BroadcastReceiver 的權(quán)限限制
Android 總結(jié)篇系列:Android 廣播機(jī)制
LocalBroadcastManager 的實(shí)現(xiàn)原理,還是 Binder?