使用 PendingIntent 删除已编辑的药物时,应用程序崩溃并出现 NullPointerException

问题描述 投票:0回答:1

我正在开发一款 Android 应用程序,该应用程序可以通过通知和警报来处理药物提醒。功能大部分按预期工作:

I can add a medication.
The notifications are triggered and alarms work fine.
I can delete a medication and the notifications are canceled successfully.

但是,我在编辑药物时遇到了问题。这是行为:

I add a medication and the notifications and alarms work as expected.
I edit the medication (update its details such as name, quantity, interval, etc.).
After editing, the notifications and alarms still work as they should.
When I attempt to delete the edited medication, the app crashes with a NullPointerException error stating that the PendingIntent is null.

堆栈跟踪指向以下内容:

java.lang.NullPointerException: cancel() called with a null PendingIntent
    at android.app.AlarmManager.cancel(AlarmManager.java:1366)
    at com.example.medicamentoreminder.MedicationUtils.cancelAlarm(MedicationUtils.kt:75)
    ...


我已确保药物的唯一 ID 在编辑过程中保持不变。仅当我删除编辑后的药物时才会出现此问题,而当我删除原始药物时不会出现此问题。 这是代码的基本流程:

Adding a medication:
    A PendingIntent is created for scheduling the alarm.
    The medication details are saved and alarms are set.

Editing a medication:
    The original medication’s data is loaded into the edit fields.
    When saving the edited medication, the PendingIntent is updated with the new data and the alarm is re-scheduled.

Deleting a medication:
    I attempt to cancel the alarm and notification for the medication being deleted.
    The cancelAlarm method is called, but it crashes with a NullPointerException.

问题:为什么尝试删除已编辑的药物时 PendingIntent 为空?编辑后更新或取消 PendingIntent 时是否缺少某些内容?

这里是取消闹钟相关的代码:

fun cancelAlarm(context: Context, uniqueID: Int) {

        val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
        val intent = Intent(context.applicationContext, AlarmReceiver::class.java).apply {
            action = "com.example.ALARM_ACTION"  // Custom action to identify the intent
        }

        val pendingIntent = PendingIntent.getBroadcast(
            context.applicationContext,
            uniqueID,
            intent,
            PendingIntent.FLAG_NO_CREATE or PendingIntent.FLAG_IMMUTABLE
        )

        alarmManager.cancel(pendingIntent)
    }

这是编辑药物后安排警报的代码:

fun scheduleAlarm(
        context: Context,
        medication: Medication,
        alarmID: Int
    ) {
        // Log para depurar
        Log.d("MedicationUtils", "Configurando alarma con alarmID: $alarmID para ${medication.name}")

        val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
        val intent = Intent(context.applicationContext, AlarmReceiver::class.java).apply {
            action = "com.example.ALARM_ACTION"  // Custom action to identify the intent
            putExtra("medName", medication.name)
            putExtra("quantity", medication.quantity)
        }

        val pendingIntent = PendingIntent.getBroadcast(
            context.applicationContext,
            alarmID,
            intent,
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
        )

        val triggerTime = System.currentTimeMillis() + (medication.intervalInMinutes * 60 * 1000)
        val intervalMillis = medication.intervalInMinutes * 60 * 1000L

        alarmManager.setRepeating(
            AlarmManager.RTC_WAKEUP,
            triggerTime,
            intervalMillis,
            pendingIntent
        )

    }

这个是用来编辑的

private fun saveUpdatedMedication() {
        if (medicationIndex != -1) {
            // Primero, obtenemos el medicamento antiguo
            val oldMedication = medications[medicationIndex]
            MedicationUtils.cancelAlarm(this, oldMedication.uniqueID)
            MedicationUtils.cancelNotification(this, oldMedication.uniqueID)
            MedicationUtils.deleteMedication(this, medicationIndex, medications)


            // Ahora, actualizamos el medicamento con los nuevos valores
            val updatedMedication = Medication(
                name = nameEditText.text.toString(),
                quantity = quantityEditText.text.toString(),
                interval = intervalEditText.text.toString(),
                intervalInMinutes = MedicationUtils.parseIntervalToMinutes(intervalEditText.text.toString()),
                medicationIndex = medicationIndex,
                uniqueID = oldMedication.uniqueID
            )

            // Actualizamos el medicamento en la lista
            medications[medicationIndex] = updatedMedication

            // Guardamos los cambios en SharedPreferences
            MedicationUtils.saveMedications(sharedPreferences, medications)

            // Establecemos la nueva alarma para el medicamento editado
            MedicationUtils.scheduleAlarm(this, updatedMedication, updatedMedication.uniqueID)

            // Devolvemos los datos actualizados a MedicationDetailsActivity
            val resultIntent = Intent(applicationContext, AlarmReceiver::class.java).apply {
                action = "com.example.ALARM_ACTION"
                putExtra("medName", updatedMedication.name)
                putExtra("quantity", updatedMedication.quantity)
                putExtra("interval", updatedMedication.interval)
                putExtra("medicationIndex", medicationIndex)
                putExtra("uniqueID", updatedMedication.uniqueID)
            }
            setResult(RESULT_OK, resultIntent)
            finish() // Terminar la actividad después de guardar
        }
    }

任何有关编辑药物后 PendingIntent 为何变为空的建议或见解将不胜感激。

android notifications nullpointerexception alarmmanager android-pendingintent
1个回答
0
投票

我有两个建议你可以尝试看看是否有效。

  1. 确保您尝试cancelAlarm() 时的意图与scheduledAlarm() 时的意图完全相同。在 cancelAlarm() 中添加具有空值的相同意图额外内容以匹配原始意图。

  2. 当您使用 cancelAlarm() 而不是使用意图 FLAG_NO_CREATE 时,请使用 FLAG_CANCEL_CURRENT 来取消具有给定意图的现有警报。 FLAG_NO_CREATE 将查看是否存在具有给定意图(操作和附加内容)的 PendingIntent,如果未找到匹配则返回 null。

尝试使用此代码:

fun cancelAlarm(context: Context, uniqueID: Int) {
    val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
    val intent = Intent(context.applicationContext, AlarmReceiver::class.java).apply {
        action = "com.example.ALARM_ACTION"
        putExtra("medName", "")
        putExtra("quantity", "")
    }

    // Using FLAG_CANCEL_CURRENT to ensure any previous intent instance is canceled
    val pendingIntent = PendingIntent.getBroadcast(
        context.applicationContext,
        uniqueID,
        intent,
        PendingIntent.FLAG_CANCEL_CURRENT or PendingIntent.FLAG_IMMUTABLE
    )

    if (pendingIntent != null) {
       alarmManager.cancel(pendingIntent)
    } else {
       Log.w("MedicationUtils", "No PendingIntent found for uniqueID: 
       $uniqueID")
    }
}

我的分析为什么当您不编辑药物时它可能看起来有效:

当你在编辑过程中调用cancelAlarm()来删除原来的PendingIntent时,如果一切都正确匹配,它应该取消原来的PendingIntent。但是,如果用于在 cancelAlarm 中检索 PendingIntent 的 Intent 与最初设置的 Intent 不完全匹配,则取消可能会失败而不会出现错误,从而留下孤立的 PendingIntent。这种差异只有在编辑后尝试删除药物时才会出现,这解释了为什么此时会出现 NullPointerException。

© www.soinside.com 2019 - 2024. All rights reserved.