动态注册BroadcastReceiver时onReceive不触发问题排查
我需要获取用户在Google ShareSheet中选择的分享渠道用于分析统计,通过PendingIntent来接收用户的选择值,实现代码如下:
override fun doOpenShareSheet( activity: AppCompatActivity ) { var flag: Int = PendingIntent.FLAG_UPDATE_CURRENT if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { flag = PendingIntent.FLAG_IMMUTABLE } val pi: PendingIntent = PendingIntent.getBroadcast( activity, 0, Intent(activity, ChooserBroadcastReceiver::class.java), flag ) // Implement Google ShareSheet val share: Intent = Intent.createChooser( Intent().apply { action = Intent.ACTION_SEND putExtra(Intent.EXTRA_TEXT, config.url) // Passing content URI to image to be displayed flags = Intent.FLAG_GRANT_READ_URI_PERMISSION type = "application/url" }, config.mediaTitle, pi.intentSender ) activity.startActivity(share) }
因为没法在清单文件给BroadcastReceiver的构造函数传参,所以我选择动态注册BroadcastReceiver,代码如下:
class ChooserBroadcastReceiver(private val vm: MyViewModel): BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { val selectedComponent: String = intent?.extras?.get(Intent.EXTRA_CHOSEN_COMPONENT).toString() vm.doOnChooserBroadcastReceiver(selectedComponent = selectedComponent) } }
@AndroidEntryPoint class MyFragment : Fragment() { ... override fun onResume() { super.onResume() registerBroadcast() } private fun registerBroadcast() { chooserBroadcastReceiver = ChooserBroadcastReceiver(vm = vm) val intentFilter = IntentFilter() intentFilter.addAction(Intent.ACTION_SEND) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { requireActivity().registerReceiver(chooserBroadcastReceiver, intentFilter, RECEIVER_EXPORTED) } else { requireActivity().registerReceiver(chooserBroadcastReceiver, intentFilter) } } ... }
现在的问题是:能正常打开Google ShareSheet并选择分享渠道,但onReceive方法接收不到事件。测试发现如果移除ViewModel参数,改用清单注册BroadcastReceiver,功能就能正常运行,但这种方式满足不了我传递构造参数的需求。
请问哪里操作有误?为什么清单注册可行但动态注册时onReceive无法触发?
核心问题是动态注册的IntentFilter和PendingIntent发送的广播完全不匹配:
- 你创建PendingIntent时用的是指向
ChooserBroadcastReceiver的显式Intent,但没有指定自定义Action; - 动态注册时,你给IntentFilter加的是
Intent.ACTION_SEND的过滤条件,而ShareSheet触发PendingIntent发送的广播根本不是这个Action,所以系统找不到你动态注册的接收器,自然不会回调onReceive。
而清单注册可行的原因是:清单注册的广播接收器如果没有在<receiver>标签里加<intent-filter>,系统会直接通过Intent里指定的组件名找到接收器,不需要匹配Action;但动态注册必须严格匹配IntentFilter的条件才能触发。
按以下步骤修改代码即可解决:
1. 定义唯一的自定义广播Action
先定义一个全局唯一的Action常量(比如放在你的Constants类里):
const val ACTION_CHOOSER_SELECTED = "com.your.package.name.ACTION_CHOOSER_SELECTED"
(注意替换成你自己的包名)
2. 修改PendingIntent的Intent
创建PendingIntent时,给Intent设置自定义的Action:
val pi: PendingIntent = PendingIntent.getBroadcast( activity, 0, Intent(ACTION_CHOOSER_SELECTED) .setClass(activity, ChooserBroadcastReceiver::class.java), // 保持显式指定组件 flag )
3. 修正动态注册的IntentFilter
注册广播时,监听我们自定义的Action,而不是Intent.ACTION_SEND:
private fun registerBroadcast() { chooserBroadcastReceiver = ChooserBroadcastReceiver(vm = vm) val intentFilter = IntentFilter(ACTION_CHOOSER_SELECTED) // 这里改成自定义Action if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { requireActivity().registerReceiver(chooserBroadcastReceiver, intentFilter, RECEIVER_EXPORTED) } else { requireActivity().registerReceiver(chooserBroadcastReceiver, intentFilter) } }
4. 别忘了注销广播(关键)
动态注册的广播必须在Fragment销毁/暂停时手动注销,避免内存泄漏:
override fun onPause() { super.onPause() try { requireActivity().unregisterReceiver(chooserBroadcastReceiver) } catch (e: IllegalArgumentException) { // 防止重复注销抛出异常 } }
内容的提问来源于stack exchange,提问作者Sirop4ik

