Skip to content

Android - Broadcast Receiver

BroadcastReceiver 是 Android 组件,允许您注册接收系统级或应用特定的广播消息(Intent)。它们充当监听器,监听来自系统(例如,启动完成、电池电量低)或其他应用(例如,数据下载完成)的事件。当注册的事件发生时,Android 系统会通知相应的 BroadcastReceiver,然后它可以执行相应的操作。

使 BroadcastReceiver 工作主要有两种方式:

  • 静态注册(Manifest 声明): 接收器在应用的 AndroidManifest.xml 中声明。系统可以启动您的应用(如果尚未运行)来传递广播。这种方法有明显的限制,特别是在 Android 8.0 (API level 26) 及更高版本上,对隐式广播限制较多,以节省系统资源。
  • 动态注册(Context 注册): 接收器在运行时使用 Context.registerReceiver() 注册。它仅在注册组件(例如 Activity 或 Service)存活期间处于活动状态。对于仅在应用处于前台或特定组件处于活动状态时才相关的广播,通常首选此方法。

如果您要广播自定义 Intent,还需要从您的应用中创建和发送这些 Intent。

BroadcastReceiver 实现为 android.content.BroadcastReceiver 的子类。您必须重写 onReceive() 方法,在该方法中,每个广播消息都作为 Intent 对象接收。

这是一个 Kotlin 的简单示例:

package com.example.myapp
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.widget.Toast
class MyCustomReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
// 检查 context 和 intent 是否为空,并验证 intent 的 action 是个好习惯。
if (context != null && intent != null) {
val action = intent.action
if ("com.example.myapp.CUSTOM_INTENT" == action) {
Toast.makeText(context, "Custom Intent Detected!", Toast.LENGTH_LONG).show()
// 重要:onReceive() 在主线程中运行。
// 对于长时间运行的任务,启动 Service 或使用 WorkManager。
// 请参阅:https://developer.android.com/guide/components/broadcasts#effects-process-state
}
}
}
}

onReceive() 的重要注意事项:

  • 执行时间: onReceive() 方法在主 UI 线程上运行。它应该快速执行(通常在 10 秒内)。对于长时间运行的操作,请将工作委托给 JobIntentService、WorkManager 或启动前台服务。
  • 后台执行限制: 定位到 Android 8.0 (API 26) 及更高版本的应用对后台服务有限制。如果广播接收器触发后台工作,请确保其符合这些限制。例如,如果工作在接收器允许的生命周期内卸载到另一个线程,则可以使用 goAsync() 允许更多时间进行处理。

要在 Manifest 中注册接收器,请在 AndroidManifest.xml 的 <application> 标签内添加一个 <receiver> 元素。这适用于系统广播,例如 android.intent.action.BOOT_COMPLETED(但请注意,在 Android N 之前,如果应用安装在外部存储上且从未启动过,则不会收到此广播)。

<!-- 在 AndroidManifest.xml 中 -->
<application ...>
<receiver
android:name=".MyBootReceiver"
android:enabled="true"
android:exported="true">
<!-- 如果需要接收来自应用外部的广播,请将 exported 设置为 true -->
<!-- 对于 BOOT_COMPLETED,exported 通常应为 true -->
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<receiver android:name=".MyCustomReceiver" android:exported="false">
<intent-filter>
<action android:name="com.example.myapp.CUSTOM_INTENT" />
</intent-filter>
</receiver>
</application>

对应的 MyBootReceiver.kt:

package com.example.myapp
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.util.Log
class MyBootReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (Intent.ACTION_BOOT_COMPLETED == intent.action) {
Log.d("MyBootReceiver", "Boot completed event received.")
// 执行启动时所需的操作,例如调度 alarm 或 job。
}
}
}

Manifest 注册限制(Android 8.0+): 对于定位到 Android 8.0 (API 26) 或更高版本的应用,系统对大多数隐式广播(未专门针对您的应用包的广播)的 Manifest 声明接收器施加了限制。有一些例外情况(例如 BOOT_COMPLETED、ACTION_LOCKED_BOOT_COMPLETED)。对于其他隐式广播,您应使用 Context 注册的接收器。显式广播(Intent 指定您的应用组件)仍会传递给 Manifest 声明的接收器。

使用 Context.registerReceiver() 动态注册接收器。这通常在 Activity 的 onResume() 或 Service 的 onCreate() 中完成。至关重要的是,当组件不再活动时(例如,在 Activity 的 onPause() 中或 Service 的 onDestroy() 中),您必须使用 Context.unregisterReceiver() 注销它,以防止内存泄漏和不必要的系统负载。

// 在 Activity 中(Kotlin)
private var myNetworkReceiver: BroadcastReceiver? = null
override fun onResume() {
super.onResume()
myNetworkReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
// 处理网络变化,例如 ConnectivityManager.CONNECTIVITY_ACTION
// 注意:CONNECTIVITY_ACTION 在 Android N+ 上也有限制
// 使用 NetworkCallback API 进行更可靠的网络监控。
Toast.makeText(context, "Network state changed", Toast.LENGTH_SHORT).show()
}
}
val filter = IntentFilter(android.net.ConnectivityManager.CONNECTIVITY_ACTION)
// 对于仅应由您的应用访问的接收器,您可以指定权限
// 或者如果是在 Manifest 中声明的,可以在 Manifest 中设置 android:exported="false"。
// 对于 Context 注册的接收器,使用 registerReceiver 的变体控制可见性。
// 如果接收器监听不需要权限的系统广播,要使其对您的应用私有:
// 不需要特殊标志,因为它默认不会导出。
// 如果注册的广播需要权限才能发送,请指定该权限:
// registerReceiver(myNetworkReceiver, filter, "com.example.SOME_PERMISSION", null)
// 对于现代 Android,如果可用,请使用带有 `exported` 标志的版本:
// 对于 Android 13 (API 33) 及更高版本,您必须指定接收器是否被导出。
// ContextCompat.registerReceiver(this, myNetworkReceiver, filter, ContextCompat.RECEIVER_NOT_EXPORTED)
// 或者如果它需要被导出:
// ContextCompat.registerReceiver(this, myNetworkReceiver, filter, ContextCompat.RECEIVER_EXPORTED)
// 对于常见的系统广播(如 CONNECTIVITY_ACTION)的简单用法:
registerReceiver(myNetworkReceiver, filter)
}
override fun onPause() {
super.onPause()
myNetworkReceiver?.let {
unregisterReceiver(it)
myNetworkReceiver = null
}
}

对于 Android 13 (API 33) 及更高版本,为非受保护广播注册接收器时,必须使用 Context.RECEIVER_EXPORTED 或 Context.RECEIVER_NOT_EXPORTED 等标志指定其是否被导出。使用 ContextCompat.registerReceiver 以实现兼容性。

可以在 Intent.java 或官方文档中找到常见的系统广播 Action 列表:https://developer.android.com/reference/android/content/Intent

序号现代事件监听方法及描述
1android.intent.action.BATTERY_CHANGED
包含电池状态的粘性广播。仍可使用,但对于精确的电池电量变化,如果绝对需要,请考虑使用前台服务或 WorkManager 进行定期检查,这是由于后台限制造成的。
2android.intent.action.BOOT_COMPLETED
系统启动后广播一次。对于调度任务很有用。需要 RECEIVE_BOOT_COMPLETED 权限。
3android.net.conn.CONNECTIVITY_CHANGE (原 android.net.ConnectivityManager.CONNECTIVITY_ACTION)
指示网络连接变化。对于定位到 N (API 24)+ 的应用,此广播的 Manifest 注册受到限制。使用 ConnectivityManager.registerNetworkCallback() 进行可靠的网络监控。
4android.intent.action.AIRPLANE_MODE (原 android.intent.action.AIRPLANE_MODE_CHANGED)
指示飞行模式已切换。Manifest 注册的接收器也受后台限制。
5android.intent.action.SCREEN_ON / android.intent.action.SCREEN_OFF
用户屏幕活动。通常需要 Context 注册。这些是受保护的 Intent。
6android.intent.action.MY_PACKAGE_REPLACED
应用更新时发送给您的应用。对于更新后重新调度 Job 或 Alarm 很有用。

对于您打算使用的特定广播 Action,请务必查阅官方文档,因为不同 Android 版本之间的行为和限制可能会发生变化。

您的应用可以生成和发送自己的自定义 Intent。这对于应用内部的组件间通信甚至不同应用之间的通信(如果设计允许且权限允许)很有用。

要发送普通广播,使用 sendBroadcast(Intent)。对于有序广播,使用 sendOrderedBroadcast(...)。

// 在 Activity 或 Service 中(Kotlin)
fun broadcastCustomIntent() {
val intent = Intent()
// Action 应唯一,通常使用您的包名作为前缀。
intent.action = "com.example.myapp.CUSTOM_INTENT"
intent.putExtra("data", "Some important value") // 可选:添加数据
// 将广播限制到应用内的组件:
intent.setPackage(packageName) // 如果是内部广播,出于安全考虑强烈推荐
sendBroadcast(intent)
// 如果接收器需要权限才能接收此广播:
// sendBroadcast(intent, "com.example.myapp.permission.RECEIVE_CUSTOM_INTENT")
}

LocalBroadcastManager(已弃用但值得了解的概念): 对于仅在您的应用内部发送广播,以前推荐使用 LocalBroadcastManager(来自 androidx.localbroadcastmanager:localbroadcastmanager)。它更高效、更安全,因为 Intent 不会离开您的应用。但是,LocalBroadcastManager 现已弃用。官方建议对于应用内通信使用其他观察者模式,如 LiveData、Flow(Kotlin 协程)或事件总线库,因为它们更具生命周期感知能力且更易于测试。如果您需要直接替代应用内广播行为且无法重构,仍可以使用全局 sendBroadcast 并结合 Intent.setPackage(packageName) 将其限制在您的应用内。

关于粘性广播的注意事项: sendStickyBroadcast(Intent) 方法自 API level 21 起已弃用。粘性广播由于安全和性能问题而存在问题。请避免使用它们。如果您需要类似的行为,请考虑使用其他组件可以绑定的前台服务,或者持久化状态并在组件激活时查询它。

此示例演示了从 Activity 广播自定义 Intent,并使用动态注册的 BroadcastReceiver 接收它。

  1. 创建 BroadcastReceiver(MyCustomReceiver.kt - 前面已展示): 确保它监听 "com.example.myapp.CUSTOM_INTENT"。

  2. 修改 MainActivity.kt 以注册接收器并发送广播:

package com.example.myapp
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.content.Intent
import android.content.IntentFilter
import android.widget.Button
import androidx.core.content.ContextCompat // 用于 registerReceiver 兼容性
class MainActivity : AppCompatActivity() {
private var customReceiver: MyCustomReceiver? = null
private val CUSTOM_INTENT_ACTION = "com.example.myapp.CUSTOM_INTENT"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main) // 假设有一个包含按钮的布局
val sendBroadcastButton: Button = findViewById(R.id.sendBroadcastButton)
sendBroadcastButton.setOnClickListener {
broadcastIntent()
}
}
override fun onResume() {
super.onResume()
customReceiver = MyCustomReceiver()
val intentFilter = IntentFilter(CUSTOM_INTENT_ACTION)
// 对于 Android 13+ 目标,指定 exported 标志。对于旧版本使用 ContextCompat。
ContextCompat.registerReceiver(this, customReceiver, intentFilter, ContextCompat.RECEIVER_NOT_EXPORTED)
}
override fun onPause() {
super.onPause()
customReceiver?.let {
unregisterReceiver(it)
customReceiver = null
}
}
private fun broadcastIntent() {
val intent = Intent(CUSTOM_INTENT_ACTION)
intent.putExtra("message", "Hello from MainActivity!")
// 出于安全考虑,限制为本应用的包名
intent.setPackage(packageName)
sendBroadcast(intent)
}
}
  1. 布局文件(res/layout/activity_main.xml): 添加一个按钮。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center">
<Button
android:id="@+id/sendBroadcastButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Send Custom Broadcast" />
</LinearLayout>
  1. 在此动态示例中,MyCustomReceiver 不需要 Manifest 注册。 如果要对自定义 Intent 使用 Manifest 注册,则需要将其添加到 AndroidManifest.xml 中,如前所示,如果仅用于应用内部使用,则设置 android:exported="false"。

运行应用。点击按钮将发送自定义广播,并且 MyCustomReceiver 将显示一个 Toast 消息。这展示了使用现代实践在应用内进行自定义 Intent 广播和接收的基本流程。

对于更复杂的场景或应用间通信,请仔细考虑权限、Intent 过滤器和安全隐患(例如,用于您自己应用之间可信通信的基于签名的权限)。