Android 14 之 动态注册Broadcast必须声明exported属性

7,938 阅读1分钟

以 Android 14 为目标并动态注册广播应用和服务需要指定exported属性

下面是Android版本对于Broadcast的限制演进。

版本改动点目的
Android 8不再支持在配置清单中注册任意隐式Intent的Receiver避免频繁地广播Intent,导致应用频繁唤醒,过度耗电
Android 13以前设备上的任何应用都可以向动态注册的接收器发送广播避免静态注册广播的问题
Android 13应用可以通过动态代码声明RECEIVER_EXPORTEDRECEIVER_NOT_EXPORTED是否接收外部程序发送的广播增加了拒绝选择项,提高运行时接收器的安全性
Android 14必须得要为动态注册的广播接收器声明是否接收外部广播,否则系统就会抛出异常进一步收缩广播安全性
  • 在 Android 13以前的版本中,设备上的任何应用都可以向动态注册的广播接收器发送广播,除非该接收器受签名权限的保护。
 // This broadcast receiver should be able to receive broadcasts from other apps.
    // This option causes the same behavior as setting the broadcast receiver's
    // "exported" attribute to true in your app's manifest.
  context.registerReceiver(sharedBroadcastReceiver, intentFilter,
        RECEIVER_EXPORTED)

    // For app safety reasons, this private broadcast receiver should **NOT**
    // be able to receive broadcasts from other apps.
    context.registerReceiver(privateBroadcastReceiver, intentFilter,
        RECEIVER_NOT_EXPORTED)

测试(Android 14 不注册情况下会报以下错误)


E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.cao.myapplication, PID: 19589
    java.lang.SecurityException: com.cao.myapplication: One of RECEIVER_EXPORTED or RECEIVER_NOT_EXPORTED should be specified when a receiver isn't being registered exclusively for system broadcasts
        at android.os.Parcel.createExceptionOrNull(Parcel.java:3057)
        at android.os.Parcel.createException(Parcel.java:3041)
        at android.os.Parcel.readException(Parcel.java:3024)
        at android.os.Parcel.readException(Parcel.java:2966)
        at android.app.IActivityManager$Stub$Proxy.registerReceiverWithFeature(IActivityManager.java:5393)
        at android.app.ContextImpl.registerReceiverInternal(ContextImpl.java:1851)
        at android.app.ContextImpl.registerReceiver(ContextImpl.java:1791)
        at android.app.ContextImpl.registerReceiver(ContextImpl.java:1779)
        at android.content.ContextWrapper.registerReceiver(ContextWrapper.java:754)
        at android.content.ContextWrapper.registerReceiver(ContextWrapper.java:754)
        at com.cao.myapplication.MainActivity.onCreate$lambda$0(MainActivity.kt:36)

  • 正常示例
val br: BroadcastReceiver = MyBroadcastReceiver()
val filter = IntentFilter("com.example.myintentfilter")
val listenToBroadcastsFromOtherApps = false
val receiverFlags = if (listenToBroadcastsFromOtherApps) {
    ContextCompat.RECEIVER_EXPORTED
} else {
    ContextCompat.RECEIVER_NOT_EXPORTED
}
ContextCompat.registerReceiver(context, br, filter, receiverFlags)