删除已编辑药物时App因PendingIntent空指针异常崩溃排查
药物提醒App编辑后删除崩溃问题排查与解决
问题现象
- 添加药物后,通知和闹钟正常工作
- 编辑药物(更新名称、剂量、间隔等信息)后,通知和闹钟仍正常
- 删除该已编辑药物时,App因
NullPointerException崩溃,提示cancel() called with a null PendingIntent
崩溃堆栈
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) ...
代码流程
- 添加药物:创建
PendingIntent并设置闹钟,保存药物详情 - 编辑药物:加载原药物数据到编辑字段,保存时更新
PendingIntent并重设闹钟 - 删除药物:尝试取消对应闹钟和通知,调用
cancelAlarm方法时触发空指针异常
疑问
为何删除已编辑药物时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的匹配规则要求Intent的所有属性完全一致(包括Action、Extra、Component等)。你在scheduleAlarm中给Intent添加了medName和quantity两个Extra,但cancelAlarm中的Intent没有这两个参数,导致FLAG_NO_CREATE无法找到已存在的PendingIntent,返回null后调用alarmManager.cancel(null)直接崩溃。
具体修复步骤
统一Intent构造逻辑
抽离公共方法创建AlarmIntent,确保取消和设置闹钟时用的Intent完全匹配:private fun createAlarmIntent(context: Context, medName: String?, quantity: String?): Intent { return Intent(context.applicationContext, AlarmReceiver::class.java).apply { action = "com.example.ALARM_ACTION" medName?.let { putExtra("medName", it) } quantity?.let { putExtra("quantity", it) } } }修改cancelAlarm方法
传入药物名称和剂量,构造匹配的Intent,并添加空指针防护:fun cancelAlarm(context: Context, uniqueID: Int, medName: String, quantity: String) { val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager val intent = createAlarmIntent(context, medName, quantity) val pendingIntent = PendingIntent.getBroadcast( context.applicationContext, uniqueID, intent, PendingIntent.FLAG_NO_CREATE or PendingIntent.FLAG_IMMUTABLE ) // 先判空再取消,避免崩溃 pendingIntent?.let { alarmManager.cancel(it) } }同步调用逻辑
- 编辑保存时,取消旧闹钟要传入旧药物的名称和剂量:
MedicationUtils.cancelAlarm(this, oldMedication.uniqueID, oldMedication.name, oldMedication.quantity) - 删除药物时,传入当前药物的名称和剂量调用
cancelAlarm。
- 编辑保存时,取消旧闹钟要传入旧药物的名称和剂量:
额外优化
即使Intent匹配出现问题,空指针防护也能避免App崩溃,同时建议在日志中添加pendingIntent的判空日志,方便后续排查。
内容的提问来源于stack exchange,提问作者Dulce Corrales
相关产品推荐
相关产品推荐

