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

Android Studio中TODO应用新增定时通知不覆盖旧通知问题求助

问题根因

你遇到的旧通知被覆盖问题核心源于两个常见的配置错误:

  • 构造 PendingIntent 时使用了固定的 requestCode 参数,Android 系统会判定 requestCode 相同的 PendingIntent 为同一实例,新创建的实例会直接覆盖已存在的旧实例
  • 弹出通知时使用了固定的通知ID,相同ID的新通知会直接替换通知栏中已有的旧通知
解决方案
  • 每个待办任务对应唯一的 requestCode,可直接使用待办任务在数据库中的主键ID作为 requestCode,无数据库存储的场景也可以使用当前时间戳的整型值作为唯一标识
  • 每个通知使用唯一的通知ID,可直接复用待办任务的ID,保证不会重复
代码实现

1. 广播接收器实现

首先创建接收定时触发事件的广播接收器 NotificationReceiver.kt:

import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.app.NotificationManager
import androidx.core.app.NotificationCompat
import com.yourpackage.R // 替换为你自己项目的R文件路径

class NotificationReceiver : BroadcastReceiver() {
    companion object {
        const val CHANNEL_ID = "TODO_REMINDER_CHANNEL"
        const val EXTRA_TODO_ID = "todo_id"
        const val EXTRA_TODO_TITLE = "todo_title"
        const val EXTRA_TODO_CONTENT = "todo_content"
    }

    override fun onReceive(context: Context, intent: Intent) {
        val todoId = intent.getIntExtra(EXTRA_TODO_ID, 0)
        val todoTitle = intent.getStringExtra(EXTRA_TODO_TITLE) ?: "待办提醒"
        val todoContent = intent.getStringExtra(EXTRA_TODO_CONTENT) ?: "你有一条待办事项待处理"

        // 配置点击通知跳转逻辑,此处示例跳转到应用启动页,可自行修改为待办详情页
        val launchIntent = context.packageManager.getLaunchIntentForPackage(context.packageName)
        val contentPendingIntent = PendingIntent.getActivity(
            context,
            todoId, // 此处也使用待办ID作为requestCode避免覆盖
            launchIntent,
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
        )

        // 构建通知
        val notification = NotificationCompat.Builder(context, CHANNEL_ID)
            .setSmallIcon(R.drawable.ic_reminder) // 替换为你自己的通知图标资源
            .setContentTitle(todoTitle)
            .setContentText(todoContent)
            .setPriority(NotificationCompat.PRIORITY_HIGH)
            .setAutoCancel(true)
            .setContentIntent(contentPendingIntent)
            .build()

        val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        // 使用待办ID作为通知ID,避免覆盖
        notificationManager.notify(todoId, notification)
    }
}

在 AndroidManifest.xml 中注册广播:

<receiver
    android:name=".NotificationReceiver"
    android:exported="false" />

2. 通知通道初始化

Android 8.0及以上系统需要先创建通知通道才能正常弹出通知,建议在Application的onCreate方法中调用:

import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import android.os.Build

private fun initNotificationChannel(context: Context) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        val channelName = "待办提醒"
        val channelDesc = "待办事项定时通知通道"
        val importance = NotificationManager.IMPORTANCE_HIGH
        val channel = NotificationChannel(NotificationReceiver.CHANNEL_ID, channelName, importance).apply {
            description = channelDesc
        }
        val notificationManager = context.getSystemService(NotificationManager::class.java)
        notificationManager.createNotificationChannel(channel)
    }
}

3. 定时通知设置调用代码

需要设置定时通知时调用以下方法:

import android.app.AlarmManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.os.Build
import java.util.Calendar

fun setTodoReminder(context: Context, todoId: Int, title: String, content: String, triggerTime: Calendar) {
    val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
    val intent = Intent(context, NotificationReceiver::class.java).apply {
        putExtra(NotificationReceiver.EXTRA_TODO_ID, todoId)
        putExtra(NotificationReceiver.EXTRA_TODO_TITLE, title)
        putExtra(NotificationReceiver.EXTRA_TODO_CONTENT, content)
    }

    // 核心:使用待办ID作为PendingIntent的requestCode,保证每个定时任务对应唯一实例
    val pendingIntent = PendingIntent.getBroadcast(
        context,
        todoId,
        intent,
        PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
    )

    // 设置精准定时
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        alarmManager.setExactAndAllowWhileIdle(
            AlarmManager.RTC_WAKEUP,
            triggerTime.timeInMillis,
            pendingIntent
        )
    } else {
        alarmManager.setExact(
            AlarmManager.RTC_WAKEUP,
            triggerTime.timeInMillis,
            pendingIntent
        )
    }
}

4. 取消定时通知工具方法

用户删除待办时可调用此方法取消对应定时通知:

fun cancelTodoReminder(context: Context, todoId: Int) {
    val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
    val intent = Intent(context, NotificationReceiver::class.java)
    // 必须使用相同的待办ID作为requestCode才能匹配到对应的PendingIntent
    val pendingIntent = PendingIntent.getBroadcast(
        context,
        todoId,
        intent,
        PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
    )
    alarmManager.cancel(pendingIntent)

    // 同时移除通知栏中的对应通知
    val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
    notificationManager.cancel(todoId)
}

注意事项:Android 12及以上系统需要在AndroidManifest.xml中添加权限android.permission.SCHEDULE_EXACT_ALARM才能使用精准定时功能;Android 13及以上系统需要添加android.permission.POST_NOTIFICATIONS权限并动态申请后才能正常弹出通知。

内容的提问来源于stack exchange,提问作者Daniel Jebarson K

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.02 03:30:00