BroadcastReceiver 广播注册新方式兼容

34 阅读5分钟

在 Android 应用开发中,<receiver> 元素的 android:exported 属性用于控制广播接收器的可见性。以下是 RECEIVER_VISIBLE_TO_INSTANT_APPSRECEIVER_EXPORTEDRECEIVER_NOT_EXPORTEDRECEIVER_EXPORTED_UNAUDITED 这几个属性的区别和用途:

  1. RECEIVER_EXPORTED:

    • 表示广播接收器可以被其他应用访问。如果设置为 true,则其他应用可以通过隐式 Intent 与该接收器交互。
    • 默认情况下,如果不显式设置 android:exported,则为 true
  2. RECEIVER_NOT_EXPORTED:

    • 表示广播接收器只能被同一应用内部的组件访问。这是出于安全考虑,限制了外部应用对接收器的访问。
    • 如果你希望保护接收器不被外部应用发现或访问,应设置 android:exported="false"
  3. RECEIVER_VISIBLE_TO_INSTANT_APPS:

    • 表示广播接收器对即时应用(Instant Apps)可见。即时应用是一种轻量级的应用体验,允许用户在不安装应用的情况下使用应用的功能。
    • 从 Android 12(API 级别 31)开始,RECEIVER_EXPORTED 被废弃,推荐使用 RECEIVER_VISIBLE_TO_INSTANT_APPS 来代替。
  4. RECEIVER_EXPORTED_UNAUDITED:

    • 这个属性值不是标准的 Android 属性。可能是指一个内部状态或第三方库中定义的属性,用于标识尚未经过审核的导出状态。
    • 在 Android 官方文档中,通常不会看到 RECEIVER_EXPORTED_UNAUDITED,它可能是某些特定情况下使用的状态标识,或者与特定版本的 Android Studio 或开发工具相关。

通常,你应该根据你的应用安全需求和组件暴露级别来选择适当的属性。如果你的应用组件不应该被外部访问,确保 android:exported 设置为 false。如果你需要与即时应用集成,并且希望它们能够访问你的广播接收器,你应该设置 android:visibleToInstantApps="true"

请注意,随着 Android 版本的更新,属性和最佳实践可能会发生变化。因此,建议定期查看官方文档,以确保你的应用遵守最新的开发指南和最佳实践。

2.2 动态广播接收器必须指定导出的行为

动态注册的广播接收器必须设置一个标记,用于表明接收器是否被导出到设备上的所有 App。标记位是 RECEIVER_EXPORTEDRECEIVER_NOT_EXPORTED。早在 Android13 就引入了这个功能,可以让应用程序指定一个已注册的广播接收器是否应该被导出,并对设备上的其他应用可见。

只不过在 Android14 上变成了“必须设置”。而在以前的 Android 版本中,设备上的任何应用都可以向动态注册的广播接收器发送未受保护的广播,除非该接收器有签名许可。

举个栗子,在 A 应用中注册 AlarmReceiver 并发送广播:

代码语言:javascript

**复制

// code 7
val filter = IntentFilter("alarmReceiver_custom_action")
val listenToBroadcastsFromOtherApps = true
val receiverFlags = if (listenToBroadcastsFromOtherApps) {
    ContextCompat.RECEIVER_EXPORTED    // 该接收器对其他应用开放
} else {
    ContextCompat.RECEIVER_NOT_EXPORTED    // 该接收器不对其他应用开放
}
// 这里的 registerReceiver 方法必须设置 receiverFlags 参数
registerReceiver(requireContext(), AlarmReceiver(), filter, receiverFlags)

// 发送广播
val intent = Intent("alarmReceiver_custom_action")    // 方式1
//val intent = Intent(requireActivity(), AlarmReceiver::class.java)    // 方式2
requireActivity().sendBroadcast(intent)

在其他的应用中只能通过 code7 中的方式1发送广播,如果 A 应用的 listenToBroadcastsFromOtherApps 设置为 true,那么在 A 应用就能收到其他应用通过方式1发送的广播信息了,否则无法收到。

在实践中还发现,如果 A 应用也通过方式1发送自己应用内部的广播,且设置 ContextCompat.RECEIVER_NOT_EXPORTED,那么这个广播是无法收到的,感兴趣的同学可以试试。

如果应用程序只是通过 Context#registerReceiver 方法 (比如 Context#registerReceiver() )为系统广播注册接收器,那么它可以不在注册接收器时指定该标志。

小结:动态广播的注册方法改了,需要设置是否对其他应用可见,这跟 android:exported 的设置是一样的道理。其实本地广播和全局广播的功能和这个一样,只不过在 targetSdkVersion >= 34 上更加重视了。

  1. android:exported 属性

    • 此属性表明广播接收器是否可以被其他应用访问。
    • 设置为 "true" 表示接收器可以被任何应用访问。
    • 设置为 "false"(默认值)表示接收器只能被同一应用内的组件访问。
    • 在 AndroidManifest.xml 中声明广播接收器时使用。
  2. android:permission 属性

    • 此属性指定了要访问此广播接收器所需的权限。
    • 如果设置了此属性,只有拥有相应权限的应用才能发送或接收意图。
  3. android:directBootAware 属性

    • 此属性表明广播接收器是否能够处理在设备处于直接启动模式时接收到的广播。
    • 设置为 "true" 表示接收器能够处理锁屏下的广播,如 ACTION_BOOT_COMPLETED
  4. android:enabled 属性

    • 此属性用于控制广播接收器是否处于启用状态。
    • 设置为 "true"(默认值)表示接收器处于启用状态。
    • 设置为 "false" 表示接收器被禁用,不会接收任何广播。
  5. android:priority 属性

    • 此属性设置了广播接收器的优先级。
    • 优先级较高的接收器可能会优先于优先级较低的接收器接收到广播。
  6. android:name 属性

    • 此属性指定了广播接收器的类名。
  7. <intent-filter> 元素

    • 通过 <intent-filter> 可以定义接收器可以响应的 intent 类型。
    • <action><category> 和 <data> 元素用于细化 intent-filter。
  8. 动态注册与注销

    • 除了在 AndroidManifest.xml 中静态声明外,还可以在代码中动态注册和注销广播接收器。
    • 使用 Context.registerReceiver() 动态注册,使用 Context.unregisterReceiver() 动态注销。
  9. 清单中声明的权限

    • 在 AndroidManifest.xml 中声明的 <uses-permission> 元素,可以控制应用是否有权使用某些敏感权限。
  10. 运行时权限请求

    • 对于某些权限,如 ACCESS_FINE_LOCATION,即使在清单中声明了,也需要在运行时请求用户同意。