Android開發之BroadcastReceiver
BroadcastReceiver介紹
BroadcastReceiver是Android四大組件之一,是Android開發中使用最頻繁的類之一,是一種廣泛運用的在應用之間消息傳輸機制。
Android的廣播機制是一個典型的發佈-訂閱模式,就是觀察者模式,它最大的特點就是發送方不關心接收方是否接收到數據,也不關心接收方是如何處理數據。通過這樣的形式來達到接收雙方的完全解耦。一個廣播可以有一個或多個接收者。
廣播機制主要用來監聽系統或者應用發出的廣播信息,然後根據廣播信息做出相應的邏輯處理,也可以用來傳輸少量的低頻數據。
Android中的廣播機制包含三部分,分別是用於發送廣播的Broadcast、接收廣播的BroadcastReceiver和用於傳遞消息的Intent。BroadcastReceiver自身不實現圖形界面,當它收到某個通知後可以啓動Activity、Service或Notification來提醒用戶。在實際開發過程中開機啓動服務、網絡狀態變化、電量變化、短信和來電通知都可以通過接收系統的廣播來讓我們自己的應用做出相應的處理。
廣播的種類
標準廣播(Normal Broadcast)
標準廣播是完全異步的。廣播發出之後所有的廣播接收器幾乎都會在同一時刻接收到這條廣播消息,順序不分先後。
這種廣播的優點是傳播的效率高,但是所有receiver的執行順序是不確定的。缺點是接收者不能將處理結果傳遞給下一個接收者,並且無法終止廣播Intent的傳播,直到沒有與之匹配的廣播接收器爲止。
有序廣播(Ordered Broadcast)
有序廣播是一種同步執行的廣播。在有序廣播發出之後同一時刻只會有一個廣播接收器能收到這條廣播消息,當這個廣播接收器中的邏輯執行完畢後,廣播纔會繼續傳播。
此時的廣播是有先後順序的,所有的廣播接收器按照優先級順序依次執行,優先級高的先執行,低的後執行。廣播接收器的優先級通過清單文件AndroidManifest.xml中receiver標籤下intent-filter中的android:priority屬性來設置,數值越大優先級越高,數值越小優先級越低。
有序廣播可以在廣播傳遞的過程中對廣播進行中斷操作,通過在onReceiver()方法中調用abortBroadcast()方法實現,這樣後面的廣播接收器就無法收到廣播消息了。
本地廣播(Local Broadcast)
爲了解決廣播的安全性問題,Android引入了本地廣播機制,該機制能夠在引用內部進行傳遞,並且廣播接收器也只能接收來自本應用發出的廣播。
標準、有序廣播都是屬於系統全局的廣播,就是發出的廣播可以被其他任何應用都接收到,並且我們也可以接收到來自其他任何應用的廣播,這樣就會帶來一定的安全隱患,例如我們發送的一些攜帶關鍵性數據的廣播就很有可能被其他應用截獲,或者其他應用不停地向我們的廣播接收器發送各種垃圾廣播。
本地廣播主要是一個LocalBroadcastManager對廣播進行管理,並提供發送跟註冊廣播接收器的方法。
粘性廣播(Sticky Broadcast)
粘性廣播通發送的廣播會一直滯留,當有匹配此廣播的廣播接收器被註冊後,該廣播接收器就會收到這條廣播。
sendStickyBroadcast()只保留最後一條廣播,並且一直保留下去,這樣即使已經有廣播接收器處理了該廣播,當再有匹配的廣播接收器被註冊時,此廣播仍會被接收。
使用Sticky Broadcast發送廣播之前需要獲取到BROADCAST_STICKY權限;
<uses-permission android:name="android.permission.BROADCAST_STICKY">
本地廣播的優勢
- 明確地知道正在發送的廣播不會離開我們的程序,不需要擔心機密數據泄漏;
- 其他程序無法將廣播發送到我們程序的內部,不需要擔心會有安全漏洞的隱患;
- 發送本地廣播比起發送系統全局廣播將會更加高效;
註冊廣播的方式
廣播註冊的方式有兩種,分別是靜態註冊和動態註冊;
靜態註冊
靜態註冊就是將廣播的註冊行爲在清單文件AndroidManifest.xml中進行。靜態註冊的廣播接收者只要App在系統中運行則一直可以接收到廣播消息。
application標籤內出現了一個新的標籤receiver,所有靜態註冊的廣播接收器都是在這裏進行註冊的。通過android:name來指定具體註冊哪一個廣播接收器。然後在intent-filter標籤里加入想要接收的廣播。
應用裏所有的receiver都可以在清單文件中查看。
要銷燬靜態註冊的廣播接收器可以通過PackageManager的Receiver禁用。
<application>
......
<receiver android:name=".BootCompleteReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
</application>
動態註冊
動態註冊的廣播接收器可以自由地控制註冊與註銷,在靈活性方面有很大優勢。它的缺點是必須要在程序啓動之後才能接收到廣播,因爲註冊的邏輯是寫在onCreate()方法裏的。動態註冊的廣播接收者當註冊的Activity或Service銷燬了就接收不到廣播了。
BrodacastReceiver receiver = new BroadcastReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(CALL_ACTION);
context.registerReceiver(receiver, intentFilter);
動態註冊和靜態註冊的區別
1.靜態註冊的廣播接收器一經安裝就常駐在系統之中,不需要重新啓動喚醒接收者;
動態註冊的廣播接收器隨着應用的生命週期,由registerReceiver開始監聽,由unregisterReceiver撤銷監聽,如果應用退出後沒有撤銷已經註冊的接收器應用將會報錯
2.當廣播接收器通過intent啓動一個Activity或者Service時,如果intent中無法匹配到相應的組件。動態註冊的廣播接收器將會導致應用報錯,靜態註冊的廣播接收器不會有任何報錯,因爲自從應用安裝完成後,廣播接收器跟應用就脫離了關係。
廣播的發送
發送標準廣播
發送廣播之前需要定義一個廣播接收器用來準備接收此廣播;
public class MyBroadcastReceiver extends BroadcastReceiver{}
發送標準廣播需要通過Context.sendBroadcast()實現。
Context.sendBroadcast();
然後對該receiver進行註冊,註冊分爲動態註冊和靜態註冊,我們既可以選擇在Java代碼中直接對目標receiver進行註冊,也可以在清單文件AndroidManifest.xml中對該receiver進行註冊;
<application>
......
<receiver android:name=".BootCompleteReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
</intent-filter>
</receiver>
</application>
在Activity或Fragment中使用廣播發送廣播,最後在onDestroy()方法中對廣播進行銷燬資源回收。
發送有序廣播
發送有序廣播通過調用方法Context.sendOrderedBroadcast()方法去實現,該方法接收兩個參數,Intent & 權限相關的字符串,無特殊要求可置爲null。
Context.sendOrderedBroadcast(intent,null);
當廣播接收器接收到廣播後可以用setResult()將結果傳遞給下一個廣播接收器,通過getResult()獲取上個廣播接收器返回的內容,可以通過abortBroadcast()讓系統中止該廣播,使得該廣播不再繼續傳送到別的廣播接收器了。
發送本地廣播
本地廣播的使用需要通過LocalBroadcastManager(Context context)的sendBroadcast()、registerReceiver()、、unregisterReceiver()等方法去實現。
本地廣播和普通廣播只是操作的類不一樣,其他的接口基本上都累死,因此替換爲本地廣播的成本相對較低。爲了程序的安全,建議在不需要其他進程接收廣播的情況下使用本地廣播。
//註冊Receiver
LocalBroadcastManager.getInstance(Context context)
.registerReceiver(receiver,intentFilter);
//註銷Receiver
LocalBroadcastManager.getInstance(Context context)
.unregisterReceiver(receiver);
//發送異步廣播
LocalBroadcastManager.getInstance(Context context)
.sendBroadcast(new Intent(HELLO_ACTION));
//同步廣播
LocalBroadcastManager.getInstance(Context context)
.sendBroadcastSync(new Intent());
本地廣播無法通過靜態註冊方式接收。靜態註冊主要是爲了讓程序在未啓動的情況下也能收到廣播,而發送本地廣播時我們的應用肯定已經啓動了,因此完全不需要使用靜態註冊的功能。
發送Sticky廣播
粘性廣播Sticky Broadcast通過Context.sendStickyBroadcast()方法來發送。如果只想處理一遍該廣播,可以通過removeStickyBroadcast()函數實現。
Context.sendStickyBroadcast();
Context.removeStickyBroadcast();
BroadcastReceiver使用注意
當系統或應用發出廣播時,將會掃描系統中的所有廣播接收者,通過action匹配將廣播發送給相應的接收者,接收者收到廣播後將會產生一個廣播接收者的實例,執行其中的onReceiver()這個方法,這個實例的生命週期只有10秒,10秒內沒有執行結束onReceiver()系統將會報錯;
在onReceiver()執行完畢之後,該實例將會被銷燬,所以不要在onReceiver()中執行耗時操作,也不要在裏面創建子線程處理業務(可能子線程沒處理完,接收者就被回收了,子線程也會跟着被回收掉);正確的處理方法就是通過in調用Activity或者Service處理業務。