为何AlarmManager的setRepeating()无法准时触发闹钟通知?
问题重现
以下是我实现闹钟的代码:
val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager Log.e("TAG", "$hour : ${minute+1}: ", ) val intent:Intent = Intent(this,MyReceiver::class.java) intent.putExtra("ALARM_TITLE","DRINK") intent.putExtra("ALARM_TIME","$hour : ${minute+1}") val calendar: Calendar = Calendar.getInstance() calendar.timeInMillis = System.currentTimeMillis() calendar.set(Calendar.HOUR_OF_DAY,hour) calendar.set(Calendar.MINUTE,minute+1) calendar.set(Calendar.SECOND,0) val pendingIntent:PendingIntent = PendingIntent.getBroadcast( this, 0, intent, PendingIntent.FLAG_MUTABLE ) alarmManager.setRepeating( AlarmManager.RTC_WAKEUP, calendar.timeInMillis, AlarmManager.INTERVAL_DAY, pendingIntent, )
使用setExact()时,闹钟能在指定时间准确触发通知,但切换为setRepeating()后,通知会延迟10分钟左右(比如设置22:01的闹钟,实际22:11才收到)。我需要实现每日准确重复的提醒功能。
问题原因
从Android API 19开始,系统为优化电池续航,会对setRepeating()设置的重复闹钟进行批量调度——不会严格按设定时间触发,而是将多个闹钟合并到相近时间点唤醒系统,这就是延迟的根源。
解决方案
放弃setRepeating(),改用精准闹钟API,每次触发后重新设置下一次闹钟,以此实现准确的重复提醒:
1. 设置首次精准闹钟
val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager val intent = Intent(this, MyReceiver::class.java) intent.putExtra("ALARM_TITLE","DRINK") val calendar = Calendar.getInstance().apply { timeInMillis = System.currentTimeMillis() set(Calendar.HOUR_OF_DAY, hour) set(Calendar.MINUTE, minute + 1) set(Calendar.SECOND, 0) // 若设置时间已过,自动加一天避免立即触发 if (timeInMillis < System.currentTimeMillis()) { add(Calendar.DAY_OF_MONTH, 1) } } val pendingIntent = PendingIntent.getBroadcast( this, 0, intent, PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT ) // 用支持Doze模式的精准API,确保锁屏休眠时也能触发 alarmManager.setExactAndAllowWhileIdle( AlarmManager.RTC_WAKEUP, calendar.timeInMillis, pendingIntent )
2. 在广播接收器中设置下一次闹钟
在MyReceiver处理完通知后,计算次日同一时间,再次调用精准闹钟API:
class MyReceiver : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { // 处理通知逻辑(此处省略你的通知实现) showDrinkNotification(context) context?.let { ctx -> val alarmManager = ctx.getSystemService(Context.ALARM_SERVICE) as AlarmManager val nextIntent = Intent(ctx, MyReceiver::class.java) nextIntent.putExtra("ALARM_TITLE","DRINK") // 从SharedPreferences读取之前保存的小时、分钟参数 val sp = ctx.getSharedPreferences("AlarmPrefs", Context.MODE_PRIVATE) val targetHour = sp.getInt("ALARM_HOUR", 22) val targetMinute = sp.getInt("ALARM_MINUTE", 1) val calendar = Calendar.getInstance().apply { timeInMillis = System.currentTimeMillis() add(Calendar.DAY_OF_MONTH, 1) // 加一天 set(Calendar.HOUR_OF_DAY, targetHour) set(Calendar.MINUTE, targetMinute) set(Calendar.SECOND, 0) } val pendingIntent = PendingIntent.getBroadcast( ctx, 0, nextIntent, PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT ) alarmManager.setExactAndAllowWhileIdle( AlarmManager.RTC_WAKEUP, calendar.timeInMillis, pendingIntent ) } } private fun showDrinkNotification(context: Context?) { // 你的通知代码实现 } }
注意事项
- 需提前将闹钟的小时、分钟参数保存到
SharedPreferences,确保接收器能读取到目标时间 - Android 12+ 版本中,
PendingIntent需使用FLAG_MUTABLE(若无需修改Intent内容,可改用FLAG_IMMUTABLE) - 若需在极端后台场景下稳定触发,可配合前台服务,但普通每日提醒用上述方案足够
内容的提问来源于stack exchange,提问作者Ash Kecthum
相关产品推荐
相关产品推荐

