关于重复注册通知的问题

本文探讨了在iOS应用中,如果多次发送或注册相同的通知,会导致接收方多次响应,产生潜在的bug。通过示例代码展示了通知发送与接收的场景,并指出为防止错误,应当在注册通知前先注销相同的通知。
iOS中通过通知中心可以实现发送与接收通知的效果,其中若多次发送同一通知,对方就会多次响应,但如果多次注册通知,也会有多次响应的效果,这里容易比较隐形的bug。

多次发送:

//发送通知方
    //self 发送通知
    [[
NSNotificationCenter defaultCenter] postNotificationName:@"justTest" object:nil];
    [[
NSNotificationCenter defaultCenter] postNotificationName:@"justTest" object:nil];
    [[NSNotificationCenter defaultCenter] postNotificationName:@"justTest" object:nil];

//接收通知方
- (void) resignNotification{
    [[
NSNotificationCenter defaultCenter] addObserverForName:@"justTest" object:nil queue:nil
                                                 
usingBlock:^(NSNotification *note) {
                                                     
NSLog(@"luozhiwei");
                                                  }];
}
结果:
2015-07-25 16:15:46.491 multipleRegistrationNotification[5233:171998] luozhiwei
2015-07-25 16:15:46.491 multipleRegistrationNotification[5233:171998] luozhiwei
2015-07-25 16:15:46.491 multipleRegistrationNotification[5233:171998] luozhiwei

多次接收:

//发送通知方
[[NSNotificationCenter defaultCenter] postNotificationName:@"justTest" object:nil];

//接收通知方
- (void) resignNotification{
    [[
NSNotificationCenter defaultCenter] addObserverForName:@"justTest" object:nil queue:nil
                                                 
usingBlock:^(NSNotification *note) {
                                                     
NSLog(@"luozhiwei");
                                                  }];

    [[NSNotificationCenter defaultCenter] addObserverForName:@"justTest" object:nil queue:nil
                                                 
usingBlock:^(NSNotification *note) {
                                                     
NSLog(@"luozhiwei");
                                                  }];


    [[NSNotificationCenter defaultCenter] addObserverForName:@"justTest" object:nil queue:nil
                                                 
usingBlock:^(NSNotification *note) {
                                                     
NSLog(@"luozhiwei");
                                                  }];
}


结果:
2015-07-25 16:15:46.491 multipleRegistrationNotification[5233:171998] luozhiwei
2015-07-25 16:15:46.491 multipleRegistrationNotification[5233:171998] luozhiwei
2015-07-25 16:15:46.491 multipleRegistrationNotification[5233:171998] luozhiwei



结论:为了避免因此重复注册而造成错误,建议每次注册一个通知之前,可以先注销一次该通知。
<think>我们正在解决用户的问题:动态注册的BroadcastReceiver无法接收通知 根据引用[5]:在Android 8.0及以上版本,静态注册的BroadcastReceiver对隐式广播有限制,但动态注册通常不受影响。然而,用户现在遇到的是动态注册无法接收通知问题。 可能的原因及解决方案: 1. **注册时机问题**:动态注册需要在组件(如Activity)的生命周期方法中调用registerReceiver()。确保在接收广播之前已经注册(例如在onCreate()中注册,而不是在onResume()之后才注册)。 2. **IntentFilter匹配问题**:检查发送广播时使用的action字符串是否与接收器注册的action完全一致(包括大小写)。 3. **广播发送方式问题**: - 如果发送的是自定义广播,确保发送的Intent设置了正确的action。 - 注意:动态注册的接收器默认只能接收显式广播(即指定了组件名的广播)或通过IntentFilter匹配的隐式广播。但是,对于自定义广播,通常使用隐式广播(不指定组件名)并通过action匹配。 - 在Android 8.0(API 26)及以上,对隐式广播有更严格的限制,但自定义广播通常不受影响(限制主要针对系统定义的隐式广播)。不过,如果发送广播时指定了接收器的包名或组件名,则成为显式广播,动态注册的接收器可以接收。 4. **权限问题**:如果发送的广播带有权限,则接收器注册时也需要声明相应的权限。 5. **接收器未取消注册导致的问题**:虽然未取消注册通常不会导致无法接收,但可能导致重复注册或内存泄漏。但这里的问题是无法接收,所以重点检查注册发送是否正确。 6. **发送广播的代码是否正确**:检查发送广播的代码,确保使用了正确的action,并且通过sendBroadcast()发送(如果是有序广播则使用sendOrderedBroadcast())。 根据引用[1]中的示例,动态注册代码通常在组件的onCreate()中注册,在onDestroy()中取消注册。我们参考这个示例来检查。 步骤: 1. 确认注册代码:确保在Activity或Service中正确注册,并且没有提前取消注册。 2. 确认发送广播的action与注册的action一致。 3. 如果广播是自定义的,确保发送广播时没有指定接收器的类名(否则变成显式广播,而动态注册的接收器不需要指定类名,但显式广播需要指定组件名,如果发送时指定了其他组件的类名,则当前接收器收不到)。 4. 测试:尝试发送一个简单的广播,看接收器能否收到。 示例代码(动态注册发送): 接收端(在Activity中): ```java public class MainActivity extends AppCompatActivity { private BroadcastReceiver receiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 创建接收器 receiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if ("MY_CUSTOM_ACTION".equals(action)) { // 处理广播 Toast.makeText(context, "收到广播", Toast.LENGTH_SHORT).show(); } } }; // 注册接收器 IntentFilter filter = new IntentFilter(); filter.addAction("MY_CUSTOM_ACTION"); registerReceiver(receiver, filter); } @Override protected void onDestroy() { super.onDestroy(); // 取消注册 if (receiver != null) { unregisterReceiver(receiver); } } } ``` 发送端(在另一个Activity或Service中): ```java // 发送广播 Intent intent = new Intent(); intent.setAction("MY_CUSTOM_ACTION"); // 如果需要传递数据 intent.putExtra("data", "Hello"); sendBroadcast(intent); ``` 如果上述步骤都正确,但仍然无法接收,考虑以下高级问题: 1. **进程状态**:如果接收端应用已经退出(Activity被销毁),动态注册的接收器也会被注销,因此收不到广播。如果需要后台接收,考虑使用Service进行动态注册(注意Service需要在后台运行)或使用静态注册(但注意Android 8.0对静态注册的限制)。 2. **系统版本差异**:在Android 8.0及以上,部分系统广播只能通过动态注册接收(如引用[5]所述),但自定义广播通常不受影响。 3. **广播发送的作用域**:默认情况下,广播只在当前应用内发送接收。如果需要在应用间发送广播,需要设置广播的包名或使用显式Intent,或者使用带权限的广播。 如果广播需要跨应用,则: - 发送广播时,使用隐式Intent(只设置action)并确保接收应用注册了相同的action。 - 在Android 8.0及以上,隐式广播需要设置ComponentName或者使用包名限定,否则无法发送给其他应用。解决方案: ```java Intent intent = new Intent("MY_CUSTOM_ACTION"); // 设置接收应用的包名(假设接收应用的包名为com.example.receiverapp) intent.setPackage("com.example.receiverapp"); sendBroadcast(intent); ``` 4. **权限**:如果发送广播时指定了权限,则接收方注册时也需要声明该权限。例如: 发送方: ```java sendBroadcast(intent, "com.example.permission.MY_PERMISSION"); ``` 接收方在注册时: ```java registerReceiver(receiver, filter, "com.example.permission.MY_PERMISSION", null); ``` 并且双方在各自的AndroidManifest.xml中声明权限。 5. **广播类型**:普通广播(sendBroadcast)有序广播(sendOrderedBroadcast)的区别。动态注册的接收器默认接收普通广播,如果发送的是有序广播,则接收器需要设置优先级(通过IntentFilter.setPriority())并可能被中止(abortBroadcast()),但普通广播不会。 总结排查步骤: 1. 检查注册代码:是否在组件生命周期中正确注册,action是否匹配。 2. 检查发送代码:action是否一致,是否使用sendBroadcast()。 3. 检查跨应用情况:如果是跨应用,是否设置了包名或权限。 4. 检查系统版本:如果目标设备是Android 8.0及以上,注意隐式广播的限制(跨应用时需设置包名)。 5. 使用日志:在发送接收的代码中添加日志,确保广播发送成功,并检查接收器是否被触发。 如果以上都确认无误,仍然无法接收,请提供更具体的代码场景信息以便进一步排查。</think>### Android 动态注册 BroadcastReceiver 无法接收通知的解决方案 动态注册 BroadcastReceiver 无法接收通知通常由以下原因导致,下面是完整的排查步骤解决方案: #### 🔍 1. **注册时机问题**(最常见原因) - **问题**:在组件生命周期外注册或过早注销 - **解决方案**: ```java public class MainActivity extends AppCompatActivity { private BroadcastReceiver receiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // ✅ 正确:在 onCreate 中注册 registerReceiver(); } private void registerReceiver() { receiver = new MyReceiver(); IntentFilter filter = new IntentFilter("MY_CUSTOM_ACTION"); registerReceiver(receiver, filter); } @Override protected void onDestroy() { super.onDestroy(); // ✅ 正确:在 onDestroy 中注销 if (receiver != null) { unregisterReceiver(receiver); } } } ``` **注意**:不要在 `onPause()` 中过早注销,除非有特殊需求[^1]。 #### 🧩 2. **IntentFilter 匹配问题** - **问题**:发送方接收方的 action 不匹配 - **解决方案**: ```java // 发送端 Intent intent = new Intent(); intent.setAction("MY_CUSTOM_ACTION"); // 必须完全一致 intent.putExtra("msg", "Hello"); sendBroadcast(intent); // 接收端 public class MyReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // ✅ 验证 action 匹配 if ("MY_CUSTOM_ACTION".equals(intent.getAction())) { String msg = intent.getStringExtra("msg"); Log.d("Receiver", "Received: " + msg); } } } ``` 使用常量定义 action 避免拼写错误[^3]。 #### 📱 3. **Android 8.0+ 兼容性问题** - **问题**:Android 8.0 (API 26) 限制隐式广播 - **解决方案**: ```java // 发送广播时指定包名 Intent intent = new Intent("MY_CUSTOM_ACTION"); intent.setPackage(getPackageName()); // 限定当前应用 intent.putExtra("msg", "Android 8.0 fix"); sendBroadcast(intent); ``` 或者使用显式广播: ```java Intent intent = new Intent(this, MyReceiver.class); intent.setAction("MY_CUSTOM_ACTION"); sendBroadcast(intent); ``` 此问题在 Android 8.0+ 上尤其重要[^5]。 #### ⏱ 4. **主线程阻塞问题** - **问题**:`onReceive()` 执行耗时操作导致 ANR - **解决方案**: ```java public class MyReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // ❌ 避免直接执行耗时操作 // ✅ 启动 Service 或线程处理 PendingResult pendingResult = goAsync(); new Thread(() -> { // 后台处理 processBroadcast(intent); pendingResult.finish(); }).start(); } private void processBroadcast(Intent intent) { // 实际处理逻辑 } } ``` 使用 `goAsync()` 延展生命周期[^4]。 #### 🔐 5. **权限问题** - **问题**:发送/接收需要权限的广播 - **解决方案**: ```xml <!-- AndroidManifest.xml --> <uses-permission android:name="CUSTOM_PERMISSION"/> <permission android:name="CUSTOM_PERMISSION" android:protectionLevel="signature"/> ``` 发送带权限广播: ```java sendBroadcast(intent, "CUSTOM_PERMISSION"); ``` 接收时声明相同权限: ```java registerReceiver(receiver, filter, "CUSTOM_PERMISSION", null); ``` #### 🧪 调试步骤: 1. 检查注册日志:`adb shell dumpsys activity broadcasts` 2. 添加调试日志: ```java @Override public void onReceive(Context context, Intent intent) { Log.d("Receiver", "Broadcast received: " + intent.getAction()); // ... } ``` 3. 测试基础功能: ```java // 发送测试广播 sendBroadcast(new Intent(Intent.ACTION_AIRPLANE_MODE_CHANGED)); ``` > **关键提示**:在 Android 8.0+ 设备上,优先使用 `setPackage()` 限定广播范围,这是最常见的问题根源[^5][^2]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值