Kotlin中取消已调度本地通知失败,求解决方案
我开发了一款支持自定义本地通知的App:用户可设置通知名称、弹出时间及重复频率,通知在另一Fragment的RecyclerView中展示,左滑可删除对应通知条目。但删除后,通知仍会按时弹出。我将notificationID以创建日期形式存储在SharedPreferences中,通过putExtra传递给BroadcastReceiver类,在Receiver中以getStringExtra获取并转为Int使用。在RecyclerView所在Fragment中,我尝试用manager.cancel()和alarmManager.cancel()结合对应notificationID取消通知,但均无效。怀疑是Flags使用错误,附上相关代码,请求帮助:
BroadcastReceiver类代码
const val titleExtra = "titleExtra" const val descriptionExtra = "descriptionExtra" val notificationID = "notificationID" class Notification: BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { val intentToRepeat = Intent(context, MainActivity::class.java) val id = intent.getStringExtra(notificationID).toString() val pendingIntent = PendingIntent.getActivity(context, id.toInt(), intentToRepeat, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE) val notification = NotificationCompat.Builder(context, channelID) .setSmallIcon(R.drawable.ic_launcher_foreground) .setContentTitle(intent.getStringExtra(titleExtra)) .setContentText(intent.getStringExtra(descriptionExtra)) .setContentIntent(pendingIntent) .setAutoCancel(true) .build() val manager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager if (intent.action == "cancel") { manager.cancel(id.toInt()) } else { manager.notify(id.toInt(), notification) } } }
AndroidManifest配置
<receiver android:name=".powiadomienia.Powiadomienie" android:enabled="true" android:exported="false"> <intent-filter> <action android:name="cancel"/> <action android:name="create" /> </intent-filter> </receiver>
RecyclerView中的取消代码
val currentNotification: SetNotification = listAdapter.getNotificationByPosition(viewHolder.bindingAdapterPosition) if(direction == ItemTouchHelper.LEFT) { // CANCEL //val manager = requireContext().getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager //manager.cancel(currentPowiadomienie.notificationId!!) val alarmManager = requireActivity().getSystemService(Context.ALARM_SERVICE) as AlarmManager val intent = Intent(requireContext(), Notification::class.java) intent.action = "cancel" val pendingIntent = PendingIntent.getService(requireContext(), currentNotification.notificationId!!.toInt(), intent, PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE) pendingIntent?.let { _pendingIntent -> alarmManager.cancel(_pendingIntent) } }
核心问题是取消Alarm时的PendingIntent与创建时不匹配,以及几处细节错误,修正方案如下:
1. 修正PendingIntent类型
创建通知时目标是BroadcastReceiver,应该用PendingIntent.getBroadcast(),但你取消时误用了PendingIntent.getService(),导致PendingIntent不匹配,alarmManager.cancel()无法识别。修改取消代码:
val currentNotification: SetNotification = listAdapter.getNotificationByPosition(viewHolder.bindingAdapterPosition) if(direction == ItemTouchHelper.LEFT) { val alarmManager = requireActivity().getSystemService(Context.ALARM_SERVICE) as AlarmManager val manager = requireContext().getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager // 构建与创建时完全匹配的Intent val intent = Intent(requireContext(), Notification::class.java) intent.action = "cancel" intent.putExtra(notificationID, currentNotification.notificationId) // 必须带上创建时传递的extra // 替换getService为getBroadcast,使用FLAG_NO_CREATE避免创建新Intent val pendingIntent = PendingIntent.getBroadcast( requireContext(), currentNotification.notificationId!!.toInt(), intent, PendingIntent.FLAG_NO_CREATE or PendingIntent.FLAG_IMMUTABLE ) pendingIntent?.let { alarmManager.cancel(it) // 取消调度的Alarm manager.cancel(currentNotification.notificationId!!.toInt()) // 清理已显示的通知 } }
2. 确保PendingIntent完全匹配
取消Alarm时的PendingIntent必须和创建时完全一致:
- 相同的Intent(包括action、extra参数)
- 相同的requestCode(即你的
notificationID转成的Int) - 兼容的Flags(创建时用
FLAG_UPDATE_CURRENT,取消时用FLAG_NO_CREATE仅获取已存在的Intent)
3. 修正Receiver类名匹配
注意Manifest中注册的Receiver类是.powiadomienia.Powiadomienie,但代码中的类名是Notification,类名不匹配会导致Intent无法正确关联Receiver,务必保证两者完全一致。
4. 重复通知的处理
如果是重复Alarm(如setRepeating/setInexactRepeating创建的),同样需要严格匹配创建时的PendingIntent才能成功取消,否则重复触发的通知不会停止。
内容的提问来源于stack exchange,提问作者Paula

