本文主要介紹BroadcastReceiver的概念、使用、生命周期、安全性、分類、特殊的BroadcastReceiver(本地、粘性、有序、粘性有序廣播)。
示例代碼見BroadcastReceiverDemo,示例APK見:TrineaAndroidDemo.apk。
1、概念介紹及兩種注冊方式的區別
BroadcastReceiver作為Android四大組件之一,不像Activity,沒有可顯示的界面。BroadcastReceiver包括兩個概念,廣播發送者和廣播接收者(Receiver),這里的廣播實際就是指Intent,程序可以自己發送廣播自己接收,也可以接受系統或其他應用的廣播或是發送廣播給其他應用程序。
發送者可以通過類似Context.sendBroadcast接口發送廣播,接收者通過Context.registerReceiver()動態注冊或在AndroidManifest.xml文件中通過<receiver>標簽靜態注冊,注冊完成后,當發送者發送某個廣播時系統會將發送的廣播(Intent)與系統中所有注冊的符合條件的接收者(Receiver) 的IntentFilter進行匹配,若匹配成功則執行相應接收者的onReceive函數,匹配規則見Intent和IntentFilter的匹配規則。
關於registerReceiver動態注冊和通過<receiver>標簽靜態注冊廣播的區別如下:
a.對bindService的調用,<receiver>注冊的廣播,在onReceive結束后廣播即不存在,所以不能在其中給自己異步傳遞結果,如bindService而只能使用startService,如果想跟service交互可使用peekService。
b. 手動控制。registerReceiver為動態注冊,自己可以手動注冊或是取消注冊;<receiver>標簽為靜態注冊,由系統開機時自動掃描注冊,所以無法手動控制,開機一直運行中。
c. 資源消耗不同。registerReceiver可以手動控制,所以適當的注冊和取消注冊能節省系統資源,<receiver>標簽系統開機后一直有效。
d. 有效期不同。通過registerReceiver注冊的BroadcastReceiver在對其進行注冊的Context對象"銷毀"了或者調用了unregisterReceiver方法時也就失效了,而通過<receiver>標簽注冊的BroadcastReceiver只要應用程序沒有被刪除就一直有效。
e. 對registerReceiver函數的調用許可不同。通過registerReceiver注冊的BroadcastReceiver在其onReceive函數中可以再次調用某個Context的registerReceiver函數,而通過<receiver>標簽注冊的BroadcastReceiver不允許再調用某個Context的registerReceiver函數 。
f. 使用情況不同。對於自己發送和接受的廣播可以通過registerReceiver注冊,對於系統常用廣播的接收通常用<receiver>標簽注冊。
2、使用舉例

public class BroadcastReceiverDemo extends Activity { private final static String ACTION_SEND = "com.trinea.android.demo.BroadcastReceiverDemo.sendBroadcast"; private final static String MSG_KEY = "msg"; private MyBroadcastReceiver receiver; private Button sendBtn; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.broadcast_receiver_demo); receiver = new MyBroadcastReceiver(); sendBtn = (Button)findViewById(R.id.sendBroadcast); sendBtn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { sendBtn.setText("發送中"); sendBtn.setClickable(false); Intent i = new Intent(ACTION_SEND); i.putExtra(MSG_KEY, "好聲音馬上開始了啦"); sendBroadcast(i); } }); } @Override public void onPause() { super.onPause(); unregisterReceiver(receiver); } @Override public void onResume() { super.onResume(); registerReceiver(receiver, new IntentFilter(ACTION_SEND)); } public class MyBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { sendBtn.setText("發送廣播"); sendBtn.setClickable(true); Toast.makeText(context, intent.getStringExtra(MSG_KEY), Toast.LENGTH_SHORT).show(); } } }
R.layout.broadcast_receiver_demo的內容為一個簡單的id為sendBroadcast的Button
從上面代碼可以看到我們
a. 新建BroadcastReceiver只需要繼承BroadcastReceiver並重寫OnReceiver函數,加上自己的處理邏輯。
b. 通過registerReceiver注冊廣播,通過unregisterReceiver取消注冊廣播,通過sendBroadcast發送廣播。
其中注冊和取消注冊廣播放在了OnResume和OnPause函數中可以有效的節省系統消耗。如果希望廣播一直運行中可以在Activity的OnCreate函數中注冊,在OnDestrory函數中取消注冊。
這里的MyBroadcastReceiver也可以在AndroidManifest.xml文件中靜態注冊,這樣程序安裝后便一直運行中。比如希望接收到短信到來時的廣播,如下:
<receiver android:name="MyBroadcastReceiver"> <intent-filter> <action android:name="android.provider.Telephony.SMS_RECEIVED" /> </intent-filter> </receiver>
3、生命周期
BroadcastReceiver在onReceive函數執行結束后即表示生命周期結束,所以不適合在onReceive中做綁定服務操作,結束后若某個進程只含有該BroadcastReceiver,則優先級將降低可能被系統回收,所以BroadcastReceiver中不適合做一些異步操作,如新建線程下載數據,BroadcastReceiver結束后可能在異步操作完成前進程已經被系統kill。
同時由於ANR限制BroadcastReceiver的onReceive函數必須在10秒內完成,而且onReceive默認會在主線程中執行,所以BroadcastReceiver中不適合做一些耗時操作,對於耗時操作需要交給service處理,比如網絡或數據庫耗時操作、對話框的顯示(因為現實時間可能超時,用Notification代替)。
4、安全性
BroadcastReceiver的設計初衷就是從全局考慮的,可以方便應用程序和系統、應用程序之間、應用程序內的通信,所以對單個應用程序而言BroadcastReceiver是存在安全性問題的,相應問題及解決如下:
a、當應用程序發送某個廣播時系統會將發送的Intent與系統中所有注冊的BroadcastReceiver的IntentFilter進行匹配,若匹配成功則執行相應的onReceive函數。可以通過類似sendBroadcast(Intent, String)的接口在發送廣播時指定接收者必須具備的permission。或通過Intent.setPackage設置廣播僅對某個程序有效。
b. 當應用程序注冊了某個廣播時,即便設置了IntentFilter還是會接收到來自其他應用程序的廣播進行匹配判斷。對於動態注冊的廣播可以通過類似registerReceiver(BroadcastReceiver, IntentFilter, String, android.os.Handler)的接口指定發送者必須具備的permission,對於靜態注冊的廣播可以通過android:exported="false"屬性表示接收者對外部應用程序不可用,即不接受來自外部的廣播。
c.上面兩個問題其實都可以通過LocalBroadcastManager來解決,LocalBroadcastManager只會將廣播限定在當前應用程序中,具體見下面6特殊的BroadcastReceiver中的介紹
5、分類
BroadcastReceiver可以分為普通和有序兩種,下面6特殊BroadcastReceiver中介紹了其他一些種類。
通過Context.sendBroadcast發送的廣播即為普通廣播,對於普通廣播接收者接收到它的順序是不定的,所以接收者接收到后無法使用其他接收者對它的處理結果也無法停止它。
通過Context.sendOrderedBroadcast發送的廣播即為有序廣播,與普通廣播的不同在於,接收者是有序接收到廣播的並且可以對廣播進行修改或是取消廣播向下傳遞。系統根據接收者定義的優先級順序決定哪個接收者先接收到它,接收者處理完后可以將結果傳遞給優先級低的接收者也可以停止廣播使得其他優先級低的接收者無法接收到該廣播。優先級通過android:priority屬性定義,數值越大優先級別越高,取值范圍:-1000到1000,雖然API文檔介紹對sendBroadcast發送的廣播無效,不過本人測試同樣有效,相同優先級的接收者接收到廣播的順序隨機。Android系統收到短信、接到電話后發送的廣播都是有序廣播,所以可以進行短信或電話的攔截,即取消廣播。
PS:有序廣播可以在onReceive函數中通過BroadcastReceiver的abortBroadcast接口(這個接口對sendBroadcast發送廣播無效)取消廣播,通過接口sendOrderedBroadcast(Intent, String, BroadcastReceiver, android.os.Handler, int, String, Bundle)發送的廣播即便優先級高的廣播取消了廣播,接口參數中指定的BroadcastReceiver依然可以在其他接收者處理完后接收到廣播。通過BroadcastReceiver的getResultExtras接口獲得結果的Bundle再通過Bundle的putString和getString方法修改或獲取數據,可以見本文最后的實例代碼舉例。
6、特殊的BroadcastReceiver
a. LocalBroadcastManager本地廣播
android引入了LocalBroadcastManager解決在第4部分安全性介紹的一些問題,LocalBroadcastManager除了能解決BroadcastReceiver進程間安全性問題外,相對Context操作的BroadcastReceiver而言還具有更高的運行效率。
使用LocalBroadcastManager需要引入Android Support Library,如何引入見Add Support Package。
本地廣播通過LocalBroadcastManager.getInstance(context).sendBroadcast(intent)發送廣播,LocalBroadcastManager.getInstance(context).registerReceiver注冊服務,通過LocalBroadcastManager.getInstance(context).unregisterReceiver取消注冊服務,其他同普通廣播.
b. Sticky Broadcast粘性廣播
如果發送者發送了某個廣播,而接收者在這個廣播發送后才注冊自己的Receiver,這時接收者便無法接收到剛才的廣播,為此Android引入了StickyBroadcast,在廣播發送結束后會保存剛剛發送的廣播(Intent),這樣當接收者注冊完Receiver后就可以繼續使用剛才的廣播。如果在接收者注冊完成前發送了多條相同Action的粘性廣播,注冊完成后只會收到一條該Action的廣播,並且消息內容是最后一次廣播內容。系統網絡狀態的改變發送的廣播就是粘性廣播。
粘性廣播通過Context的sendStickyBroadcast(Intent)接口發送,需要添加權限<uses-permission android:name="android.permission.BROADCAST_STICKY"/>
也可以通過Context的removeStickyBroadcast(Intent intent)接口移除緩存的粘性廣播。
c. OrderedBroadcastReceiver有序廣播
這個在5分類中已經介紹,接收者有序接收廣播並可以修改廣播結果或是取消廣播,通過Context的sendOrderedBroadcast接口發送
d. StickyOrderedBroadcast粘性有序廣播
這個就是粘性廣播和有序廣播的結合了,通過Context的sendStickyOrderedBroadcast接口發送。
各種廣播操作Demo示例代碼見BroadcastReceiverDemo,由於本地廣播的support library暫時下載不了,不包括本地廣播,本地廣播操作間上面介紹。
注意AndroidManifest文件中需要添加粘性廣播操作權限<uses-permission android:name="android.permission.BROADCAST_STICKY"/>
參考:
http://developer.android.com/reference/android/content/BroadcastReceiver.html