You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 07:15:36