我正在开发一款 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 为何变为空的建议或见解将不胜感激。
我有两个建议你可以尝试看看是否有效。
确保您尝试cancelAlarm() 时的意图与scheduledAlarm() 时的意图完全相同。在 cancelAlarm() 中添加具有空值的相同意图额外内容以匹配原始意图。
当您使用 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。