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

为何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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 04:43:13