Android--四大元件之BroadCastReceiver(生命週期、實現原理及使用等)
####1. BroadCastReceiver是什麼?
####2. 廣播型別
######1). 有序廣播
######2). 無序廣播
####3. 生命週期
####4. 實現原理
####5. 使用方法
####6. 許可權問題(安全性)
####7. LocalBroadcast
####8. 注意事項
原文:簡書ThinkinLiu 部落格: IT老五
#BroadCastReceiver
BroadCastReceiver即廣播接收器,用於監聽/接收Android應用分發的各類廣播並做出相應的響應。
應用場景:
- 監聽系統事件:如開機廣播,網路連線與斷開,螢幕開啟與關閉等
- 不同元件間通訊(多個Activity/service,包括不同應用間)
- 多執行緒通訊
#廣播型別
廣播可分為無序廣播和有序廣播:
#####無序廣播
無序廣播即廣播被髮送後,BroadCastReceiver之間是無順序,完全非同步的,各個Receiver之間無關聯。無序廣播無法通過abortBroadcast終止,也無法使用setResult和getResult來傳遞處理結果。
無序廣播直接通過Context.sendBroadcast()來發送。
#####有序廣播
有序廣播即廣播發送後會按照優先順序順序被不同的廣播接收器接收,優先順序可以通過intent-filter的android:priority屬性來設定,定義範圍為-1000~1000,數值越大,優先順序越高(如果優先順序相同,1. 傳送廣播的程序會優先接收 2. 先註冊的Receiver先接收)。
有序廣播發送後,會被優先順序最高的BroadCastReceiver接收,然後在處理完畢後依次被優先順序較低的BroadCastReceiver接收,期間,BroadCastReceiver可以設定setResult將該廣播的處理結果傳遞給下一個BroadCastReceiver,下一個Receiver通過getResult獲取。有序廣播被接收時,還可以通過abortBroadcast來終止,終止後,廣播不會繼續傳遞給其他Receiver。如:開發一個攔截簡訊的功能,我們將priority設定為1000,來最大限度的保證簡訊可以被我們的Receiver接收,在接收後,如果不想簡訊顯示在系統簡訊列表中,我們可以abortBroadcast來終止傳遞,此時也就不會有簡訊提示音,也不會再簡訊收件箱中看到該簡訊。
有序廣播需要通過Context.sendOrderedBroadcast來發送
#####粘性廣播
其實將粘性廣播與有序廣播/無序廣播放在一起講並不是非常合適,因為對它們的定義並不是在同一個維度上。
粘性廣播通過Context.sendStickyBroadcast()來發送,它與非粘性廣播的區別是,在onReceiver中粘性廣播不受10s限制(普通廣播onReceiver在10s未處理完畢,會丟擲ANR),粘性廣播在10s後仍然存在,直至廣播處理完畢。但需要注意的是,10s後,系統會將這個廣播置為candidate狀態,即可以被幹掉的,當系統資源不足時,廣播仍然可能會被丟棄。
使用粘性廣播需要android.Manifest.permission.BROADCAST_STICKY許可權
#生命週期
廣播的生命週期從呼叫開始到onReceiver執行完畢結束,需要注意的是,一般廣播的生命週期都極短,需要在10s內處理完onReceiver中的所有工作,所以,一般不進行耗時長的工作,如果有耗時長的工作,應當通過Intent傳遞給Service進行處理。(注意,不要在onReceiver中開啟執行緒進行耗時任務處理,否則,在10s後,該執行緒會變成空執行緒,從而導致任務的丟失。同樣的,也不要使用bindService來繫結服務。)
值得注意的是,如果是在程式碼中動態註冊的廣播,如:在Activity註冊,那麼在Activity的onDestory中需要使用unregisterReceiver登出廣播。
#實現原理
在Android中,廣播的出現是為了元件間的通訊。其實在Android中,程序間通訊有Binder,而同進程的通訊方式就更多了,之所以使用廣播,傳送者與接受者都不需要知道對方的存在,這樣帶來的好處便是,系統的各個元件可以鬆耦合地組織在一起,這樣系統就具有高度的可擴充套件性,容易與其它系統進行整合。
Android中的廣播使用了觀察者模式:基於訊息釋出/訂閱模式的事件驅動模型。
廣播模型中包含三個角色:- 訊息釋出者(廣播發布者)
- 訊息中心(AMS,即Activity Manager Service)
- 訊息訂閱者(廣播接收者)
- 廣播接受者通過Binder機制在AMS中註冊監聽
- 廣播發布者通過Binder機制向AMS傳送廣播
- AMS根據傳送者的需求,在已登錄檔中獲取到合適的廣播接受者(根據Intent-filter,Permission)
- AMS將廣播發送給合適的廣播接受者的訊息迴圈佇列中
- 廣播接受者通過訊息迴圈獲取到廣播並回調onReceive()
整個廣播發送與接收過程中,傳送者與接收者是非同步的,傳送者不需要知道是否有接受者,也不需要知道接受者何時收到廣播。
#使用方法
廣播的使用包括以下幾個步驟:
- 自定義廣播接收者:重寫onReceiver()
- 註冊廣播接收者到訊息中心(AMS):動態(程式碼中registerReceiver)或者靜態註冊(AndroidManifest.xml中申明)
- 定義及傳送廣播到訊息中心(AMS):Context.sendBroadcast()、Context.sendOrderedBroadcast、Context.sendStickyBroadcast()
- AMS選擇併發送廣播給合適的廣播接受者(根據Intent-filter,Permission)
- 廣播接受者通過訊息迴圈獲取廣播,並呼叫onReceive()進行處理
以上1~3步驟需要使用者進行處理,4、5步驟由Android系統完成。
#####自定義廣播接受者
廣播接受者的自定義很簡單,繼承BroadcastReceiver基類並重寫onReceive()即可:
// 繼承BroadcastReceiver基類
public class MyBroadcastReceiver extends BroadcastReceiver {
// 複寫onReceive()方法
// 接收到廣播後,則自動呼叫該方法
@Override
public void onReceive(Context context, Intent intent) {
// 接收廣播後的操作
}
}
#####註冊廣播接收者
廣播接受者的註冊包含靜態註冊和動態註冊兩種方式。
靜態註冊 即在AndroidManifest.xml中通過標籤進行申明。該方式註冊的廣播為常駐廣播,註冊後,即使應用處於非執行狀態,也可以接收到廣播。
屬性及例項:
<receiver
android:enabled=["true" | "false"]
android:exported=["true" | "false"] // 此broadcastReceiver能否接收其他App的發出的廣播,預設值是由receiver中有無intent-filter決定:如果有intent-filter,預設值為true,否則為false
android:icon="drawable resource"
android:label="string resource"
android:name=".MyBroadcastReceiver " // 繼承BroadcastReceiver子類的類名
android:permission="string" // 具有相應許可權的廣播發送者傳送的廣播才能被此BroadcastReceiver所接收;
android:process="string"> // BroadcastReceiver執行所處的程序 預設為與app同進程,可以指定獨立的程序。注:Android四大基本元件都可以通過此屬性指定自己的獨立程序
<intent-filter>
<action android:name="android.net.conn.BOOT_COMPLETED" /> //用於指定此廣播接收器將接收的廣播型別,可以配置多條,這裡給出的示例是android系統開機廣播
</intent-filter>
</receiver>
動態註冊 即在程式碼中通過registerReceiver(BroadcastReceiver receiver, IntentFilter filter)來動態註冊廣播,該方法包含兩個引數,receiver即我們自己定義的MyBroadcastReceiver,IntentFilter即需要過濾的條件。動態註冊的廣播在應用停止執行後無法接收廣播,比如在ActivityA中註冊,則應當在ActivityA銷燬前,使用unregisterReceiver(BroadcastReceiver receiver)來登出註冊
MyBroadcastReceiver receiver=new MyBroadcastReceiver(); // 這裡使用我們上面自定義的廣播接受者
IntentFilter filter=new IntentFilter();
filter.addAction("android.net.conn.CONNECTIVITY_CHANGE"); // 這裡以網路連線狀態變化為例
registerReceiver(receiver, filter);
#####定義及傳送廣播
廣播的傳送有三種方法sendBroadcast(),sendOrderedBroadcast()和sendStickyBroadcast()
示例如下:
Intent intent = new Intent();
intent.setAction("com.itlao5.broadcast");
intent.putExtra("data", "data");
sendBroadcast(intent);
#許可權問題(安全性)
看到這裡,應該都很清楚如何傳送及接受廣播。但是在此過程中是否會存在一些問題,比如我傳送的廣播只想讓指定的廣播接收者接收,或者我只想接收指定傳送者傳送的廣播,這些該如何實現呢?
在上面廣播註冊一節中,我們可以看到有一個permission屬性,沒錯,我們可以通過指定許可權來實現上面的需求。
- 如何傳送給指定的廣播接受者?
在AndroidManifest.xml中,我們可以配置
<permission android:name = "com.android.permission.XXX"/>
然後在傳送廣播時
sendBroadcast("com.android.XXX_ACTION", "com.android.permission.XXX");
此時,就只有在廣播接收的應用中在AndroidManifest.xml中新增對應的XXX許可權,才能正常接收到該廣播。
<uses-permission android:name="com.android.permission.XXX">></uses-permission>
- 如何接收指定傳送者的廣播?
在這種情況下,需要在接受者app的 tag中宣告一下發送者app應該具有的許可權。
首先同上,在AndroidManifest.xml中定義新的許可權SEND_XXX,例如:<permission android:name="com.android.SEND_XXX"/>
然後,在接受者app的Androidmanifest.xml中的 tag裡新增許可權SEND_XXX的宣告
<receiver android:name=".MyBroadcastReceiver" android:permission="com.android.permission.SEND_XXX"> <intent-filter> <action android:name="com.android.XXX_ACTION" /> </intent-filter> </receiver>
如此,該接受者便只能接收來自具有該SEND_XXX許可權的應用發出的廣播。要傳送該廣播,只需在傳送者app的AndroidManifest.xml中也宣告使用該許可權即可,如下:
<uses-permission android:name="com.android.permission.SEND_XXX"></uses-permission>
#LocalBroadcast
既然是說的廣播,那麼這裡僅順便提及一下LocalBroadcast。LocalBroadcast是本地廣播,它的出現是為了應用內的廣播傳遞。準確的說,LocalBroadcast與Broadcast不同,BroadCast基於Binder機制,而LocalBroadcast則是基於Handler機制。其機制的不同,導致他們的應用場景也不同,LocalBroadcast消耗資源少,安全性也相對來說更有保障,所以在應用內,更推薦使用LocalBroadcast。
這裡簡單提一下,有興趣的可以看這篇博文:Android開發之區域性廣播的使用——LocalBroadcast
#注意事項
- 動態註冊的廣播,在不需要使用時或者載體即將銷燬時進行登出,即每一個registerReceiver需要有一個對應的unregisterReceiver
- 不要在廣播接收器onReceive()中進行耗時操作,否則會引起ANR(10s)
- 不要在廣播接收器onReceive()中開啟非同步任務,否則因為其生命週期的結束會出現空執行緒,導致任務丟失或者出現ANR等情況
- 耗時任務請開啟service進行處理,且應當使用startService,而不應該使用bindService
- 應用內的廣播儘量使用localBroadcast,因為其使用Handler,較Broadcast的Binder機制開銷更小,且安全性更高
- 動態註冊的廣播優先度比靜態註冊高(當配置的優先順序一致時),且可以控制其註冊與登出,開銷更小,所以能滿足功能的情況下優先使用動態註冊
- 如果接受不到自己傳送的廣播,請注意是否是因為許可權問題