【Android】BroadcastReceiver 详解

BroadcastReceiver,即广播接收器,是Activity四大组件之一。

一、简介

BroadcastReceiver 用于监听并响应来自系统或其他应用的广播消息。是一种典型的发布-订阅通信机制。它常被用于监听系统事件(开机、网络变化、电量变化等)、跨应用通信以及应用内组件解耦,其onReceive()回调在主线程执行且有时长限制,注册方式、广播类型、版本适配都直接影响功能能否正常工作。

BroadcastReceiver本质上是一种基于Intent的进程间/组件间时间通信机制。它和Binder的不同之处在于:广播的发送方和接收方不需要知道彼此的存在,发送方只需要通过Action声明"发生了什么",接收方通过IntentFilter声明"关心什么",由系统的ActivityManagerService(AMS)负责路由和匹配。这种松耦合机制带来了系统级的高度可扩展性。

还有一点,它与其他三大组件不同,BroadcastReceiver 在 Activity 中没有长期存在的管理数据结构------它本质上是通过Intent调用应用中的一个方法,不需要一个常驻的对象。每次广播到达时,系统会创建新的Receiver实例,执行完onReceive()后立即销毁,因此不能在Receiver内部保存跨次广播的状态。

二、 Broadcast的分类

2.1 按发送方式划分

类型 发送方法 特点
普通广播 sendBroadcast() 异步发送,所有匹配的接收者几乎同时收到,顺序不确定,无法拦截
有序广播 sendOrderedBroadcast() 按优先级顺序依次传递,高优先级接收者可修改结果或调用 abortBroadcast() 中止传递
粘性广播 sendStickyBroadcast() 已废弃(API 21),广播会一直驻留,新注册者能立即收到最后一次的广播,无安全保护
本地广播 LocalBroadcastManager 仅应用内传递,效率高、无安全问题,官方已废弃,推荐用 LiveData/Flow 替代

2.2 按来源划分

  1. 系统广播: 由Android系统发出,如ACTION_BOOT_COMPLETED(Android应用开机)、ACTION_BATTERY_LOW(低电量)、ACTION_HEADSET_PLUG(耳麦插拔)等。
  2. 自定义广播:由应用自己定义Action并发出,用于应用内或跨应用通信。

三、注册方式

BroadcastReceiver有两种注册方式,分别是静态注册和动态注册,决定了它的生命周期和使用场景。

3.1 静态注册

在AndroidManifest.xml中通过<receiver> 标签声明,应用安装后即可生效,即使进程未运行,系统也会拉起来进程响应广播。

静态注册的示例代码:

javascript 复制代码
java:
<receiver android:name=".BootReceiver"
          android:enabled="true"
          android:exported="true">
    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
    </intent-filter>
</receiver>
kotlin 复制代码
kotlin:
class BootReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        if (intent.action == Intent.ACTION_BOOT_COMPLETED) {
            // 设备启动完成后的处理
            WorkManager.getInstance(context).enqueueUniqueWork(
                "BootSync", ExistingWorkPolicy.KEEP,
                OneTimeWorkRequestBuilder<BootSyncWorker>().build()
            )
        }
    }
}

接收开机广播还需在Manifest中声明权限:

xml 复制代码
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>

3.2 动态注册

在代码中调用Context.registerReceiver(),需要在使用完后调用unregisterReceiver()注销,生命周期与注册者(Activity/Service/Application)绑定。

java 复制代码
java:
public class ScreenActivity extends AppCompatActivity {

    private ScreenReceiver screenReceiver;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_screen);

        // 1. 创建接收器实例
        screenReceiver = new ScreenReceiver();

        // 2. 创建 IntentFilter,指定要监听的 Action
        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_SCREEN_ON);   // 亮屏
        filter.addAction(Intent.ACTION_SCREEN_OFF);  // 灭屏

        // 3. 注册广播
        // 注意:屏幕亮灭广播只能动态注册,静态注册无效
        registerReceiver(screenReceiver, filter);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 4. 必须注销,否则内存泄漏
        unregisterReceiver(screenReceiver);
    }

    // 自定义广播接收器
    private static class ScreenReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (Intent.ACTION_SCREEN_ON.equals(action)) {
                Log.d("ScreenReceiver", "屏幕点亮");
            } else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
                Log.d("ScreenReceiver", "屏幕熄灭");
            }
        }
    }
}
kotlin 复制代码
kotlin
class NetworkActivity : AppCompatActivity() {
    private val networkReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
            // 处理网络变化
        }
    }

    override fun onStart() {
        super.onStart()
        val filter = IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION)
        registerReceiver(networkReceiver, filter)
    }

    override fun onStop() {
        super.onStop()
        unregisterReceiver(networkReceiver) // 必须注销,否则内存泄漏
    }
}

3.3 两种方式对比

维度 静态注册 动态注册
注册位置 AndroidManifest.xml 代码中 registerReceiver()
生效区间 安装后即生效,与进程状态无关 仅在注册者存活时有效
生命周期 系统级,可拉活空进程 与 Context 同生共死
Android 8.0+ 大部分隐式广播失效 不受隐式广播限制
接收实例 每次广播都新建实例 通常是同一个实例
典型场景 开机启动、应用升级 网络/电量变化等临时监听
灵活性 无法运行时调整 可按需注册/注销

四、生命周期与安全

4.1 生命周期特性

BroadcastReceiver 对象的生命周期极短,从onReceiver()被调用开始,到方法返回即结束。这意味着:

  1. onReceive()在主线程执行,系统默认给约10秒响应时间,超时会触发ANR。
  2. onReceive()返回后,系统会认为该进程为"空进程",可能随时被系统杀掉,因此不能在onReceive()中启动异步操作就撒手不管------异步任务在完成前进程可能已经被回收。
  3. 不能在onReceive()中显示对话框或绑定Service,应使用NotificationManager或startService()。

4.2 goAsync():延长处理窗口

如果需要稍微更长的处理时间(例如写数据库、快速网络请求),可以调用goAsync()获取一个PendingResult对象,告诉系统"还没处理完",从而在onReceive()返回后继续保有前台进程优先级。但必须在合适时机手动调用pendingResule.finish(),否则会触发ANR。

java 复制代码
java:
public class MyReceiver extends BroadcastReceiver {

    private static final String TAG = "MyReceiver";
    // 单例线程池,避免每次广播都新建线程
    private static final ExecutorService EXECUTOR = Executors.newSingleThreadExecutor();

    @Override
    public void onReceive(Context context, Intent intent) {
        // 1. 必须在 onReceive() 内调用 goAsync(),拿到 PendingResult
        final PendingResult pendingResult = goAsync();

        final String action = intent.getAction();
        final String data = intent.getStringExtra("data");

        // 2. 把任务丢给后台线程执行
        EXECUTOR.execute(() -> {
            try {
                // 模拟耗时但轻量的操作(写库、快速网络请求、解析数据等)
                Log.d(TAG, "后台处理开始,action=" + action + ", data=" + data);
                Thread.sleep(3000);

                // ⚠️ 不要在这里调用 getResultCode()/setResult 等,
                //    应改用 pendingResult 的对应方法

            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } finally {
                // 3. 无论成功还是异常,都必须调用 finish()
                //    否则会触发 ANR(broadcast timeout)
                pendingResult.finish();
                Log.d(TAG, "后台处理完成,已调用 finish()");
            }
        });
    }
}
kotlin 复制代码
kotlin:
class MyReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context, intent: Intent) {
        val pendingResult = goAsync()
        CoroutineScope(Dispatchers.IO).launch {
            try {
                // 耗时但轻量的操作
            } finally {
                pendingResult.finish() // 必须调用
            }
        }
    }
}

需要注意的是,goAsync()只是有界地延长回调窗口,不是持久化的后台任务机制------真正耗时的任务仍应交给WorkManager或前台Service。

4.3 安全注意事项

  1. 避免广播被劫持/嗅探:自定义广播若不加以限制,可被任意应用接收或伪造。推荐使用intent.setPackage(packageName)限制接收范围,或使用自定义权限。
  2. android:exported属性:静态注册时控制是否允许其他应用的广播触达此类接收器。仅应用内部使用的Receiver应设为false。
  3. 及时注销:动态注册的Receiver必须在合适的生命周期阶段(如onDestroy()或onStop())注销,否则造成内存泄漏。
  4. 不在onSaveInstanceState()中注销:因为该回调可能不被调用。

五、现代替代方案

随着Android架构演进,BroadcastReceiver的适用范围正在收窄,许多场景有更优选择。

场景 推荐替代
应用内组件通信 Kotlin Flow、SharedFlow、LiveData、事件总线
后台周期/条件任务 WorkManager(替代 JobScheduler/隐式广播监听)
数据状态查询(替代粘性广播) DataStore、状态流、直接 API 查询
跨进程通信 Binder/AIDL、Messenger、ContentProvider
推送 FCM、厂商推送通道

官方也明确建议:对于应用内事件分发,优先使用Flow/LiveData;对于需要响应系统条件的后台任务,优先使用WorkManager的约束系统,而不是监听系统广播。

六、总结

BroadcastReceiver是Android早期为系统级事件通知和松耦合跨应用通信设计的重要组件,在开机启动、监听硬件状态变化、跨应用消息等场景仍有不可替代的作用。其核心机制可以概括为:发送方通过Intent广播事件,AMS统一路由,接收方通过静态或动态注册并通过IntentFilter匹配,最终在onReceiver()中处理,该回调在主线程且生命周期极短。

在BroadcastReceiver中动态注册已成为主流,但从演进趋势来说,应用内通信正在被Flow/LiveData取代,后台任务也被WorkManager取代,BroadcastReceiver的核心价值越来越聚焦在监听系统级事件这一较为狭窄但不可替代的场景上。理解其机制、限制与替代方案,是写出既稳定又合规的现代Android应用的基础。

感谢看完全文,比心!

相关推荐
_祝你今天愉快2 小时前
Paint Cavans高级绘制:[二] Paint-滤镜,XFERMODE
android
小乖写代码2 小时前
字段获取代码(Android) · Android Input Examples
android·whatsapp·账号注册
booboosui2 小时前
停一个活只有杀进程:界面上少了「说点要求」
android
传奇开心果编程4 小时前
【Flutter踩坑排雷】第28课:平台目录缺失与修复坑|`flutter create` 的“重建”与“覆盖”边界
android·flutter·ios·移动开发·跨平台·dart·声明式ui
longxibo14 小时前
【AIBIZ低代码平台:能长出业务系统的平台】
android·低代码·rxjava
AI_Gump1 天前
Harness篇|DeepSeek Harness 上手机:五条安卓移植路线与 App 内 Linux 拆解
android·linux·人工智能·智能手机·deepseek
疯华整猫1 天前
否决 Flutter 提案那天,我发现自己也说不清 Framework 管什么
android
传奇开心果编程2 天前
【Flutter踩坑排雷】第25课:ScrollController 误用坑|跳转失效、监听泄漏与“控制器未附加”
android·flutter·ios·移动开发·跨平台·dart·声明式ui
慕容雪_2 天前
Capacitor8-Android打包完整手册
android·打包·capacitor