第4章:BroadcastReceiver动态注册与顺序拦截

本章聚焦Android四大组件之一——BroadcastReceiver在动态注册、有序广播及粘性广播场景下的技术实现边界。通过对比静态注册与动态注册的生命周期差异,解析有序广播的优先级拦截机制,并结合粘性广播的遗留设计,提供可落地的编码与调优方案。

4.1 动态注册 vs 静态注册:核心差异

对比维度 动态注册(registerReceiver) 静态注册(AndroidManifest.xml)
注册时机 运行时在Activity/Service/Application中调用 安装时由PackageManager解析清单
生命周期 随注册组件(如Activity)销毁而失效,需主动unregister 独立于组件生命周期,即使进程被杀也可通过广播唤醒
优先级设置 通过IntentFilter.setPriority(999)设置(1~1000) 在中声明
有序广播拦截 支持abortBroadcast()(有序广播下) 支持abortBroadcast(),但Android 8+限制隐式广播静态注册
粘性广播 支持registerReceiver接收粘性广播(需权限) Android 5.0起已废弃,不再推荐
线程模型 默认在主线程回调onReceive,不宜做耗时操作 同样在主线程,但可通过goAsync()异步处理
实用建议:动态注册适合组件内临时监听(如屏幕点亮/熄灭),静态注册适合需要独立于应用存活的后台事件(如开机完成),但注意Android 8+对隐式广播的限制,静态注册仅对显式广播或系统特定广播有效。

4.2 有序广播处理流程

有序广播(Ordered Broadcast)按照接收者声明的优先级依次传递,每个接收者均可调用abortBroadcast()终止后续传递,也可通过setResultExtras()向下游传递数据。

有序广播拦截流程 sendOrderedBroadcast() Receiver A (优先级999) onReceive → 处理 → 可选 abort abort() 继续传递 Receiver B (优先级500) setResultExtras → 继续 Receiver C (优先级100) 获取上游结果 → 结束 核心规则 • 按priority降序执行 • abort()仅中断后续接收 • 结果通过Bundle传递 动态注册设置 • IntentFilter.setPriority(1000) • 必须在onPause前unregister
关键API:
  • sendOrderedBroadcast(Intent, String) — 发送有序广播,第二个参数为接收者权限(可null)。
  • abortBroadcast() — 在onReceive中调用,阻止后续接收者。
  • setResultExtras(Bundle)/setResultCode(int)/setResultData(String) — 向下游传递结果。

4.3 粘性广播(Sticky Broadcast)

粘性广播在广播发送后,即使没有接收器正在运行,系统也会保留该广播的Intent数据。新注册的BroadcastReceiver会立即收到最后一条粘性广播。该机制在Android 5.0(API 21)被标记为废弃,仅建议用于系统级组件间的即时状态同步(如电池电量变化)。

操作 方法 说明
发送粘性广播 sendStickyBroadcast(Intent) 需要BROADCAST_STICKY权限,数据常驻系统
发送有序粘性广播 sendStickyOrderedBroadcast(Intent, BroadcastReceiver, Handler, int, String, Bundle) 废弃同sendStickyBroadcast,且有序版本更复杂
移除粘性广播 removeStickyBroadcast(Intent) 需相同权限,清理不再需要的粘性数据
接收粘性广播 动态注册时registerReceiver(receiver, filter)自动获取最后一次粘性广播 无需额外标记,但结果在Intent的extra中
弃用警告:粘性广播因安全与性能问题已被废弃。新项目中若需“被注册时立即获取最新状态”,应改用LocalBroadcastManager或LiveData/StateFlow替代。系统级粘性广播(如Intent.ACTION_BATTERY_CHANGED)仅供系统应用使用。

4.4 代码实现:动态注册与有序广播拦截

4.4.1 动态注册广播接收器

// 在Activity中注册与取消注册
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        if ("com.example.CUSTOM_ORDERED".equals(action)) {
            String data = intent.getStringExtra("key_data");
            // 处理广播
            int code = getResultCode();
            Bundle extras = getResultExtras(true);
            // 示例:拦截
            // abortBroadcast();
        }
    }
};

@Override
protected void onStart() {
    super.onStart();
    IntentFilter filter = new IntentFilter("com.example.CUSTOM_ORDERED");
    filter.setPriority(999);          // 最高优先级
    registerReceiver(mReceiver, filter);
}

@Override
protected void onStop() {
    super.onStop();
    unregisterReceiver(mReceiver);    // 必须取消注册,否则内存泄漏
}

4.4.2 发送有序广播并传递结果

// 发送有序广播
Intent intent = new Intent("com.example.CUSTOM_ORDERED");
intent.putExtra("key_data", "payload");
sendOrderedBroadcast(intent, null, null, null, Activity.RESULT_OK, null, null);

// 在接收者中向下游传递修改后的数据
public void onReceive(Context context, Intent intent) {
    setResultCode(Activity.RESULT_OK);
    Bundle bundle = new Bundle();
    bundle.putString("modified", "by receiver B");
    setResultExtras(bundle);
}

4.4.3 粘性广播(仅演示,不建议生产使用)

<!-- AndroidManifest.xml 需声明权限 -->
<uses-permission android:name="android.permission.BROADCAST_STICKY" />

// 发送粘性广播
Intent sticky = new Intent("com.example.STICKY_STATE");
sticky.putExtra("status", "charging");
sendStickyBroadcast(sticky);

// 任意时刻注册接收器,立即收到最后一次粘性广播
registerReceiver(receiver, new IntentFilter("com.example.STICKY_STATE"));

4.5 调试与常见错误

  • 内存泄漏:未在onStop()/onDestroy()中unregisterReceiver,导致Activity被销毁后广播仍持有引用。使用LeakCanary或ActivityLifecycleCallbacks辅助检测。
  • 优先级无效:有序广播中若所有接收者priority相同,系统按注册顺序(动态优先于静态)处理,但不保证顺序。建议设置明确的高差(如999、500、100)。
  • 隐式广播限制:Android 8.0+禁止清单文件注册隐式广播(非显式Intent)。动态注册不受此限制,但若静态注册隐式广播,需提供ComponentName或使用setPackage()。
  • 粘性广播ANR:粘性广播通常携带大量数据(如BatteryManager.EXTRA_PLUGGED),接收者做耗时操作极易触发ANR(onReceive限制10秒)。
  • 权限冲突:发送有序广播时指定permission参数,接收者若未声明对应权限则无法收到。动态注册的接收者无法跨应用接收受权限保护的广播。

4.6 性能与稳定性优化

  1. 减少广播数据量:Intent extra Bundle应小于1MB,避免IPC传输阻塞。使用LocalBroadcastManager(AndroidX)替代系统广播实现进程内通信。
  2. 异步处理:在onReceive中调用goAsync()并返回BroadcastReceiver.PendingResult,然后将耗时操作切换至工作线程。
  3. 生命周期对齐:使用registerReceiver(mReceiver, filter, broadcastPermission, backgroundHandler)指定后台Handler,避免主线程阻塞。
  4. 有序广播超时:系统对有序广播设置默认超时(约10秒),若某个接收者长时间不返回,系统会终止后续广播。建议接收者快速执行或使用goAsync()后台处理。
  5. 取消注册时机:在onPause()中取消注册比onStop()更安全,因为onPause()后Activity可能仍可见(如dialog遮盖),但广播仍可触发。
  6. 粘性广播替换方案:使用LiveData + ViewModel共享数据,或EventBus / Flow实现粘性事件语义。
生产建议:优先使用动态注册+有序广播处理关键优先级事件,如支付结果回调。对于“注册时立即获取最新状态”的场景,使用SharedFlow(replay=1)比粘性广播更安全、可测试。