以 Android 14 为目标并动态注册广播应用和服务需要指定exported属性
下面是Android版本对于Broadcast的限制演进。
| 版本 | 改动点 | 目的 |
|---|---|---|
| Android 8 | 不再支持在配置清单中注册任意隐式Intent的Receiver | 避免频繁地广播Intent,导致应用频繁唤醒,过度耗电 |
| Android 13以前 | 设备上的任何应用都可以向动态注册的接收器发送广播 | 避免静态注册广播的问题 |
| Android 13 | 应用可以通过动态代码声明RECEIVER_EXPORTED或 RECEIVER_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)