安卓手機APP開發___廣播概述
目錄
概述
關于系統廣播
系統廣播所發生的更改
接收廣播
清單聲明的接收器
上下文注冊的接收器
對進程狀態的影響
發送廣播
通過權限限制廣播
帶權限的發送
帶權限的接收
安全注意事項和最佳做法
概述
Android 應用可以通過 Android 系統和其他 Android應用發送或接收廣播消息,
類似于發布-訂閱設計模式。這些廣播會在所關注的事件發生時發送。
例如,Android 系統會在發生各種系統事件時發送廣播,例如系統啟動或設備
開始充電時。應用還可以發送自定義廣播,例如,通知其他應用它們可能感
興趣的內容(例如,一些新數據已下載)。
為了保持最佳系統運行狀況,系統會優化廣播的傳送。因此,廣播的傳送時間
無法保證。需要低延遲進程間通信的應用應考慮綁定服務。
應用可以注冊接收特定的廣播。發送廣播后,系統會自動將廣播路由到
已訂閱接收該特定類型的廣播的應用。
一般來說,廣播可用作跨應用和正常用戶流之外的消息傳遞系統。但是,
您必須小心,不要濫用在后臺響應廣播和運行作業的機會,否則可能會
導致系統性能變慢。
注意 :請盡可能讓廣播僅對您的應用保持私密狀態。
關于系統廣播
當系統發生各種事件時(例如,當系統進入和退出飛行模式時),系統會
自動發送廣播。系統會向所有訂閱接收該事件的應用發送系統廣播。
廣播消息本身封裝在一個 Intent 對象中,該對象的操作字符串會標識
所發生的事件(例如 android.intent.action.AIRPLANE_MODE)。
該 intent 還可能包含捆綁到其 extra 字段中的其他信息。例如,
飛行模式 intent 包含一個布爾值 extra,用于指示是否已開啟飛行模式。
如需詳細了解如何讀取 intent 并從 intent 獲取操作字符串,請參閱
intent 和 intent 過濾器。
如需查看系統廣播操作的完整列表,請參閱 Android SDK 中的
BROADCAST_ACTIONS.TXT 文件。每個廣播操作都有一個與之關聯的常量字段。
例如,常量 ACTION_AIRPLANE_MODE_CHANGED
的值為android.intent.action.AIRPLANE_MODE。
每個廣播操作的文檔都可以在關聯的常量字段中找到。
系統廣播所發生的更改
隨著 Android 平臺的發展,它會定期更改系統廣播的行為方式。
為了支持所有 Android 版本,請注意以下更改。
Android 14
當應用處于緩存狀態時,廣播傳送針對系統運行狀況進行了優化。
例如,當應用處于緩存狀態時,系統會延遲不太重要的系統廣播(如
ACTION_SCREEN_ON)。一旦應用從緩存狀態進入活躍進程生命周期,
系統就會傳遞所有延遲的廣播。
在清單中聲明的重要廣播會暫時從緩存狀態中移除應用以進行分發。
Android 9
從 Android 9(API 級別 28)開始,NETWORK_STATE_CHANGED_ACTION
廣播不接收與用戶位置信息或個人身份數據相關的信息。
此外,如果您的應用安裝在搭載 Android 9 或更高版本的設備上,通過 Wi-Fi
發送的系統廣播不包含 SSID、BSSID、連接信息或掃描結果。
如需獲取此信息,請改為調用 getConnectionInfo()。
Android 8.0
從 Android 8.0(API 級別 26)開始,系統會對清單聲明的接收器施加其他限制。
如果您的應用以 Android 8.0 或更高版本為目標平臺,則對于大多數隱式廣播
(未明確針對您的應用的廣播)而言,您無法使用清單聲明接收器。
當用戶正在活躍地使用您的應用時,您仍然可以使用上下文注冊的接收器。
Android 7.0
Android 7.0(API 級別 24)及更高版本不會發送以下系統廣播:
??? ACTION_NEW_PICTURE
??? ACTION_NEW_VIDEO
此外,以 Android 7.0 及更高版本為目標平臺的應用必須使用 registerReceiver(BroadcastReceiver, IntentFilter) 注冊 CONNECTIVITY_ACTION 廣播。無法在清單中聲明接收器。
接收廣播
應用可以通過兩種方式接收廣播:通過清單聲明的接收器和上下文注冊的接收器。
清單聲明的接收器
如果您在清單中聲明廣播接收器,系統會在廣播發送后啟動您的應用(如果應用尚未運行)。
注意 :如果您的應用以 API 級別 26 或更高級別為目標平臺,則您無法使用
清單為隱式廣播(不專門針對您的應用的廣播)聲明接收器,但一些
不受該限制約束的隱式廣播除外。在大多數情況下,您可以改用預定作業。
要在清單中聲明廣播接收器,請執行以下步驟:
??? 在應用清單中指定 <receiver> 元素。
<!-- If this receiver listens for broadcasts sent from the system or from
???? other apps, even other apps that you own, set android:exported to "true". -->
<receiver android:name=".MyBroadcastReceiver" android:exported="false">
??? <intent-filter>
??????? <action android:name="APP_SPECIFIC_BROADCAST
" />
??? </intent-filter>
</receiver>
Intent 過濾器指定您的接收器所訂閱的廣播操作。
創建 BroadcastReceiver 子類并實現 onReceive(Context, Intent)。以下示例中的廣播接收器會記錄并顯示廣播的內容:
Kotlin
private const val TAG = "MyBroadcastReceiver"class MyBroadcastReceiver : BroadcastReceiver() {override fun onReceive(context: Context, intent: Intent) {StringBuilder().apply {append("Action: ${intent.action}\n")append("URI: ${intent.toUri(Intent.URI_INTENT_SCHEME)}\n")toString().also { log ->Log.d(TAG, log)val binding = ActivityNameBinding.inflate(layoutInflater)val view = binding.rootsetContentView(view)Snackbar.make(view, log, Snackbar.LENGTH_LONG).show()}}}}
??? 如需啟用視圖綁定,請在模塊級 build.gradle 文件中配置 viewBinding。
系統軟件包管理器會在安裝應用時注冊接收器。然后,該接收器就會成為應用的單獨入口點,這意味著,如果應用當前未運行,系統可以啟動應用并傳遞廣播。
系統會創建一個新的 BroadcastReceiver 組件對象來處理它收到的每條廣播。此對象僅在調用 onReceive(Context, Intent) 期間有效。一旦從此方法返回代碼,系統就會認為該組件不再處于活動狀態。
上下文注冊的接收器
只要注冊上下文有效,上下文注冊的接收器就會接收廣播。例如,如果您在 Activity 上下文中注冊,只要 activity 未銷毀,您就會收到廣播。如果您在應用上下文中注冊,只要應用正在運行,您就會收到廣播。
要使用上下文注冊接收器,請執行以下步驟:
??? 在應用的模塊級 build 文件中,添加 1.9.0 版或更高版本的 AndroidX Core 庫:
?? ?
??? Kotlin
dependencies {val core_version = "1.13.1"// Java language implementationimplementation("androidx.core:core:$core_version")// Kotlinimplementation("androidx.core:core-ktx:$core_version")// To use RoleManagerCompatimplementation("androidx.core:core-role:1.0.0")// To use the Animator APIsimplementation("androidx.core:core-animation:1.0.0")// To test the Animator APIsandroidTestImplementation("androidx.core:core-animation-testing:1.0.0")// Optional - To enable APIs that query the performance characteristics of GMS devices.implementation("androidx.core:core-performance:1.0.0")// Optional - to use ShortcutManagerCompat to donate shortcuts to be used by Googleimplementation("androidx.core:core-google-shortcuts:1.1.0")// Optional - to support backwards compatibility of RemoteViewsimplementation("androidx.core:core-remoteviews:1.1.0-rc01")// Optional - APIs for SplashScreen, including compatibility helpers on devices prior Android 12implementation("androidx.core:core-splashscreen:1.2.0-alpha01")
}
創建 BroadcastReceiver 的實例:
Kotlin
val br: BroadcastReceiver = MyBroadcastReceiver()
創建 IntentFilter 的實例:
Kotlin
Java
val filter = IntentFilter(APP_SPECIFIC_BROADCAST
)
選擇廣播接收器是否應導出并對設備上的其他應用可見。如果此接收器正在監
聽從系統或其他應用(甚至是您擁有的其他應用)發送的廣播,請使用
RECEIVER_EXPORTED 標志。如果此接收器僅監聽應用發送的廣播,請使用
RECEIVER_NOT_EXPORTED 標志。
某些系統廣播來自具有較高特權的應用(例如藍牙和電話應用),屬于 Android
框架的一部分,但不在系統的唯一進程 ID (UID) 下運行。如需接收所有系統廣播
(包括來自高特權應用的廣播),請使用 RECEIVER_EXPORTED 標記接收器。
如果您使用 RECEIVER_NOT_EXPORTED 標記接收器,則該接收器能夠接收來自
您的應用的一些系統廣播和廣播,但無法接收來自高特權應用的廣播。
如果您的應用監聽多個廣播,但只有部分廣播應標記 RECEIVER_NOT_EXPORTED 和部分 RECEIVER_EXPORTED,請在不同的廣播接收器之間對廣播進行分區。
Kotlin
val listenToBroadcastsFromOtherApps = false
val receiverFlags = if (listenToBroadcastsFromOtherApps) {
??? ContextCompat.RECEIVER_EXPORTED
} else {
??? ContextCompat.RECEIVER_NOT_EXPORTED
}
注意 :如果導出廣播接收器,其他應用可能會向您的應用發送不受保護的廣播。
通過調用 registerReceiver() 注冊接收器:
Kotlin
??? ContextCompat.registerReceiver(context, br, filter, receiverFlags)
?? 如需停止接收廣播,請調用unregisterReceiver(android.content.BroadcastReceiver)。
?? 當您不再需要接收器或上下文不再有效時,請務必取消注冊接收器。
??? 請注意注冊和取消注冊接收器的位置,例如,如果您使用 activity
??? 的上下文在 onCreate(Bundle) 中注冊接收器,則應在 onDestroy()
??? 中取消注冊接收器,以防止將接收器泄露到 activity 上下文之外。如果您在
??? onResume() 中注冊接收器,則應在 onPause() 中取消注冊,以防止對其進行
?? 多次注冊(如果您不想在暫停時接收廣播,這樣可以減少不必要的系統開銷)。
?? 請勿在 onSaveInstanceState(Bundle)中取消注冊,因為如果用戶
? 在歷史記錄堆棧中后退,系統不會調用此方法。
對進程狀態的影響
BroadcastReceiver 是否在運行會影響其包含的進程,這可能會改變其系統終止可能性。
前臺進程執行接收器的 onReceive() 方法。除非遇到極大的內存壓力,
否則系統會運行該進程。
BroadcastReceiver 在 onReceive() 后被停用。接收器的主機進程的重要性
與其應用組件一樣重要。如果該進程僅托管清單聲明的接收器
(對于用戶從未與之交互或最近未與之交互的應用的情況,這種情況很常見),
系統可能會在 onReceive() 后終止該接收器,以便將資源提供給其他更關鍵的進程。
因此,廣播接收器不應啟動長時間運行的后臺線程。系統可以在 onReceive()
之后隨時停止進程以回收內存,從而終止創建的線程。如需使進程保持活躍狀態,
請使用 JobScheduler 從接收器調度 JobService,以便系統知道
該進程仍在運行。后臺工作概覽提供了更多詳情。
發送廣播
Android 為應用提供三種方式來發送廣播:
??? sendOrderedBroadcast(Intent, String) 方法一次向一個接收器發送廣播。當接收器
?? 逐個輪流執行時,接收器可以將結果傳播給下一個接收器,也可以完全中止廣播,
? 使其不會再傳遞給其他接收器。接收器運行順序可以通過匹配的 intent-過濾器
? 的 android:priority 屬性控制;具有相同優先級的接收器將以任意順序運行。
??? sendBroadcast(Intent) 方法以未定義的順序向所有接收器發送廣播。這稱為常規廣播。
?? 這種方式效率更高,但也意味著接收器無法從其他接收器讀取結果、傳播從
? 廣播接收到的數據或中止廣播。
以下代碼段演示了如何通過創建 intent 并調用 sendBroadcast(Intent) 來發送廣播。
Kotlin
Intent().also { intent ->
??? intent.setAction("com.example.broadcast.MY_NOTIFICATION")
??? intent.putExtra("data", "Nothing to see here, move along.")
??? sendBroadcast(intent)
}
廣播消息封裝在 Intent 對象中。intent 的操作字符串必須提供應用的 Java 軟件包名稱語法,并唯一標識廣播事件。您可以使用 putExtra(String, Bundle) 向 intent 附加其他信息。您還可以通過對 intent 調用 setPackage(String),將廣播限定為同一組織內的一組應用。
注意 :雖然 intent 既用于發送廣播,也用于通過 startActivity(Intent) 啟動 activity,但這些操作是完全無關的。廣播接收器無法查看或捕獲用于啟動 activity 的 intent;同樣,當您廣播 intent 時,無法找到或啟動 activity。
通過權限限制廣播
通過權限,您可以將廣播限定為擁有特定權限的一組應用。您可以對廣播的
發送者或接收者施加限制。
帶權限的發送
當您調用 sendBroadcast(Intent, String) 或 sendOrderedBroadcast(Intent, String,
BroadcastReceiver, Handler, int, String, Bundle) 時,可以指定權限參數。
只有通過其清單中的 標記請求該權限(并且隨后被授予了該權限
(如果權限危險)的接收器)才能收到該廣播。例如,以下代碼會發送一條廣播:
Kotlin
sendBroadcast(Intent(BluetoothDevice.ACTION_FOUND),
????????????? Manifest.permission.BLUETOOTH_CONNECT)
如需接收廣播,接收方應用必須請求權限,如下所示:
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
您可以指定現有的系統權限(如 BLUETOOTH_CONNECT),也可以使用 <permission> 元素定義自定義權限。如需大致了解權限和安全性,請參閱系統權限。
注意:自定義權限將在安裝應用時注冊。必須先安裝定義自定義權限的應用,再安裝使用該自定義權限的應用。
帶權限的接收
如果您在注冊廣播接收器時指定了權限參數(使用 registerReceiver(BroadcastReceiver,
IntentFilter, String, Handler) 或在清單中的 <receiver> 標記中),
則只有已在其清單中使用 <uses-permission> 標記請求權限(并且隨后被授予權限,
如果其危險的話)的廣播方可以向接收器發送 intent。
例如,假設您的接收方應用具有如下所示的清單聲明的接收器:
<receiver android:name=".MyBroadcastReceiver"
????????? android:permission="android.permission.BLUETOOTH_CONNECT">
??? <intent-filter>
??????? <action android:name="android.intent.action.ACTION_FOUND"/>
??? </intent-filter>
</receiver>
或者您的接收方應用具有如下所示的上下文注冊的接收器:
Kotlin
var filter = IntentFilter(Intent.ACTION_FOUND)
registerReceiver(receiver, filter, Manifest.permission.BLUETOOTH_CONNECT, null )
然后,為了能夠向這些接收器發送廣播,發送方應用必須請求如下所示的權限:
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT"/>
安全注意事項和最佳做法
以下是一些關于發送和接收廣播的安全注意事項和最佳做法:
??? 如果許多應用在其清單中注冊接收同一廣播,可能會導致系統啟動大量應用,
??? 從而對設備性能和用戶體驗產生重大影響。為避免這種情況,請優先使用
?? 上下文注冊而非清單聲明。有時,Android 系統本身會強制使用上下文注冊的
??? 接收器。例如,CONNECTIVITY_ACTION 廣播僅會傳送給上下文注冊的接收器。
??? 請勿使用隱式 intent 廣播敏感信息。任何注冊接收廣播的應用都可以
?? 讀取這些信息。您可以通過以下三種方式控制哪些應用可以接收您的廣播:
??????? 您可以在發送廣播時指定權限。
??????? 在 Android 4.0 及更高版本中,您可以在發送廣播時使用 setPackage(String)
?? ?指定軟件包。系統會將廣播限制為與該軟件包匹配的一組應用。
??? 注冊接收器后,任何應用都可以向應用的接收器發送潛在的惡意廣播。
?? 您可以通過以下幾種方式限制應用收到的廣播:
??????? 您可以在注冊廣播接收器時指定權限。
??????? 對于清單聲明的接收器,您可以在清單中將 android:exported
?? ?屬性設置為“false”。接收器不會接收來自應用外部來源的廣播。
??? 廣播操作的命名空間是全局性的。請確保在您擁有的命名空間中編寫
?? 操作名稱和其他字符串,否則您可能會無意中與其他應用發生沖突。
??? 由于接收器的 onReceive(Context, Intent)方法在主線程上運行,因此它應該快
??? 速執行并返回。
??? 如果您需要執行長時間運行的工作,請謹慎生成線程或啟動后臺服務,
??? 因為系統可能會在 onReceive() 返回后終止整個進程。如需了解詳情,
?? 請參閱對進程狀態的影響。如需執行長時間運行的工作,我們建議:
??????? 在接收器的 onReceive() 方法中調用 goAsync(),并將
?? ?BroadcastReceiver.PendingResult 傳遞給后臺線程。這樣可在從 onReceive()
????? 返回后使廣播保持活動狀態。不過,即使采用這種方法,系統也希望
???? 您很快就能完成廣播(10 秒以內)。不過,它允許您將工作
???? 移至另一個線程,以免干擾主線程。使用 JobScheduler 調度作業。
?? ?如需了解詳情,請參閱智能作業調度。
??? 請勿從廣播接收器啟動 activity,因為用戶體驗會很糟糕,尤其是在有多個
?? 接收器的情況下。因此,您可以考慮顯示通知。