Broadcast知識詳解
今天來看下Android四大組件之一的Broadcast。
一、什么是Broadcast
Android apps can send or receive broadcast messages from the Android system and other Android apps, similar to the publish-subscribe design pattern. These broadcasts are sent when an event of interest occurs.
Android應用可以發送或接收來自 Android 系統和其他 Android 應用的廣播信息,廣播類似於發布訂閱設計模式,它們是在有興趣的事件發生時發送的。
廣播采用觀察者模式:基於消息的發布 / 訂閱事件模型 將廣播的發送和接收進行解耦。
基本原理:
1、廣播接收者通過 Binder 機制在 AMS( Activity Manager Service ) 注冊;
2、廣播發送者通過 Binder 機制向 AMS 發送廣播;
3、AMS 根據廣播發送者要求,在已注冊列表中,尋找合適的 BroadcastReceiver ( 尋找依據:IntentFilter / Permission );
4、AMS 將廣播發送到 BroadcastReceiver 相應的消息循環隊列中;
5、廣播接收者通過消息循環拿到此廣播,並回調 onReceive() 方法。
需要注意的是:廣播的發送和接收是異步的,發送者不會關心有無接收者或者何時收到。
二、廣播的類型
按是否有序分類:
-
普通廣播:也叫標准廣播,是一種完全異步執行的廣播。
在廣播發出之后,所有廣播接收器幾乎都會在同一時刻接收到這條廣播消息,它們之間沒有任何先后順序,廣播的效率較高。
優點: 完全異步, 邏輯上可被任何接受者收到廣播,效率高
缺點: 接受者不能將處理結果交給下一個接受者, 且無法終止廣播. -
有序廣播:是一種同步執行的廣播。
在廣播發出之后,同一時刻只有一個廣播接收器能夠收到這條廣播消息,當其邏輯執行完后該廣播接收器才會繼續傳遞。優先級高的可以較早接收到廣播並可以添加信息或截斷廣播
按是否能遠端通信
-
本地廣播:將export屬性設置為false即為本地廣播,這樣廣播就只能在app內收到。
優點:1、其他的 APP 不會受到局部廣播,不用擔心數據泄露的問題。
2、其他 APP 不可能向當前的 APP 發送局部廣播,不用擔心有安全漏洞被其他 APP 利用。
3、局部廣播比通過系統傳遞的全局廣播的傳遞效率更高。 -
普通廣播:將export屬性設置為true,廣播可以跨進程收到。
按是否是系統定義
-
系統廣播:系統內置了很多廣播,比如電話、短信來臨時都會發出廣播
-
自定義廣播:應用根據需求自定義的廣播
三、廣播的接收
兩種注冊方式及區別
我們都知道四大組件都需要注冊后才能使用。Broadcast提供了兩種方式進行注冊分別是靜態注冊和動態注冊。
靜態注冊
在mainfest中通過
相關屬性:
- android:exported 是否能接收其他app發送的廣播
默認值是由receiver中有無intent-filter決定的:如果有intent-filter,默認值為true,否則為false - android:permission 廣播發送權限
- android:process廣播運行所在進程
動態注冊
通過context.registerReceiver代碼注冊。
區別:
動態注冊較為靈活,但是是非常駐的 並且需要進程啟動后才會接收,
靜態注冊是常駐的,進程未啟動的情況下也可以接收廣播
BroadcastReceiver
廣播接收者當收到對應的已注冊廣播時會回調其onReceive函數。
不過需要注意的是:
使用靜態注冊的方法注冊當接收到對應廣播時系統會創建一個新的 BroadcastReceiver 組件對象來處理它接收到的每個廣播。 此對象僅在調用 onReceive 函數內部有效。 一旦代碼從此方法return,系統將認為組件不再活動。也就是說此方法每次接收廣播都會創建新的Receiver 處理完廣播后該Receiver 會銷毀,下次接收會再次重新創建Receiver 。
使用動態注冊接收者只要其注冊上下文有效就可以接收廣播。 例如,如果在一個activity上下文中注冊,那么只要activity沒有被銷毀,您就會收到廣播。 如果您在應用程序上下文中注冊,那么只要應用程序運行,您就會收到廣播。
Broadcastreceiver對進程狀態的影響
因為在觸發其onReceive 方法時系統會認為當前進程屬於前台進程從而保持較高的優先級,但是onReceive完成后其優先級可能會降低如果此時系統資源緊張有可能進程會被系統殺死以回收資源。
因此不應該從廣播接收器啟動長時間運行的后台線程。 在 onReceive ()之后,系統可以在任何時候終止進程以回收內存,並且在這樣做時,它終止了在進程中運行的派生線程。 為了避免這種情況,您應該調用 goAsync ()(如果需要多一點時間在后台線程中處理廣播) ,或者使用 JobScheduler 從接收方調度一個 JobService,這樣系統就知道該進程繼續執行活動工作。
四、廣播的發送
廣播的發送有3種方式它們分別適用於不同類型的Broadcast:
- sendOrderedBroadcast(Intent, String)
此方式用來發送有序廣播,廣播的傳遞順序是按接收器的優先級又高到低進行的(優先級intent-filter 的 android: priority 屬性來控制),優先級高的會先接收到廣播並可以截斷以讓廣播終止發送,也可以將結果傳遞給下一個廣播。 - sendBroadcast(Intent)
正常廣播以未定義的順序向所有接收器發送廣播,但是意味着接收者不能從其他接收者讀取結果,傳播從廣播接收的數據,或者中止廣播 - LocalBroadcastManager.sendBroadcast
發送本地廣播方法,廣播到發送者與接收器在同一個應用程序中。 如果你不需要跨進程發送廣播,使用本地廣播這樣實現效率更高(不需要進程間通信) ,您不需要擔心與其他應用程序能夠接收或發送您的廣播有關的任何安全問題
發送帶權限的廣播
通過sendBroadcast (Intent,String)發送廣播可以指定一個權限參數。 只有在清單中使用標記請求該權限的接收者(如果該權限是危險的,則隨后被授予該權限)才能接收廣播,這樣可以防止我們的廣播被不該接收的應用接收。
五、發送和接收廣播的一些安全考慮和最佳做法
- 首先如果不需要跨應用接收、發送廣播那么盡量使用LocalBroadcastManager,這樣不僅效率高並且安全性也高
- 其次優先使用動態注冊廣播,因為如果很多應用程序在它們的清單中注冊了相同的廣播,當接收廣播時會導致系統啟動許多應用程序,從而對設備性能和用戶體驗產生重大影響。
- 不要使用隱式intent廣播敏感信息。 任何注冊接收廣播的應用程序都可以讀取這些信息。可以通過增加權限、 通過setPackage(String)設置接收廣播的包名、使用本地廣播來限制接收廣播的應用。
- 同樣的當你注冊一個接收器,任何應用程序都可以發送潛在的惡意廣播到你的應用程序的接收器,所以有必要限制應用接收的廣播,可以通過在注冊廣播接收器時指定權限、在manifest 文件中將android:exported設置為false、使用本地廣播等方法來限制。
- 廣播操作的名稱空間是全局的,所以在自定義action的時候有必要添加包前綴以防止跟其他應用沖突。
- 因為onReceive (Context,Intent)方法在主線程上運行,所以它應該快速執行並返回。 如果需要執行長時間運行的工作,請謹慎使用子線程或啟動后台服務,因為在 onReceive ()返回后,系統可能會殺死整個進程。 因此建議:
如果希望延長執行時間可以使用goAsync()或JobScheduler但是即使這樣也不建議在onReceive執行長時間的任務。 - 不要從廣播接收器啟動activity,因為用戶體驗不好; 特別是如果有多個接收器。 但是可以考慮顯示一個通知。
六、相關源碼分析
廣播機制,從本質來說,它是一種消息訂閱/發布機制,因此,使用這種消息驅動模型的第一步便是訂閱消息;而對Android應用程序來說,訂閱消息其實就是注冊廣播接收器。
注冊廣播(registerReceiver)過程源碼分析
發送廣播(sendBroadcast)的過程源碼分析
本地廣播( LocalBroadcastManager)源碼解析
參考:
https://developer.android.com/guide/components/broadcasts