如何在Android(Kotlin)中用Alarm Manager设置空闲时重复闹钟
Android平台用Kotlin结合AlarmManager实现空闲状态重复闹钟功能
核心实现思路
由于Android 6.0+的Doze模式限制,常规重复闹钟方法(如setInexactRepeating)在设备空闲时会被系统推迟。要实现空闲状态下的可靠重复闹钟,需采用单次精确闹钟+触发后重置的方案,结合setExactAndAllowWhileIdle(API 23+)确保Doze模式下也能触发。
完整实现步骤
1. 定义广播接收器
闹钟触发后通过广播接收器处理通知展示,并重置下一次闹钟:
class ReminderNotificationReceiver : BroadcastReceiver() { override fun onReceive(context: Context?, intent: Intent?) { context ?: return intent ?: return // 展示通知 val title = intent.getStringExtra(notificationTitleExtra) ?: "提醒" val message = intent.getStringExtra(notificationMessageExtra) ?: "您有一条提醒" val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channel = NotificationChannel( "REMINDER_CHANNEL", "提醒通知", NotificationManager.IMPORTANCE_DEFAULT ) notificationManager.createNotificationChannel(channel) } val notification = NotificationCompat.Builder(context, "REMINDER_CHANNEL") .setContentTitle(title) .setContentText(message) .setSmallIcon(R.drawable.ic_notification) // 替换为你的通知图标 .build() notificationManager.notify(REMINDER_NOTIFICATION_ID, notification) // 重置下一次重复闹钟 setNextRepeatAlarm(context, title, message) } private fun setNextRepeatAlarm(context: Context, title: String, message: String) { val intent = Intent(context, ReminderNotificationReceiver::class.java) intent.putExtra(notificationTitleExtra, title) intent.putExtra(notificationMessageExtra, message) val pendingIntent = PendingIntent.getBroadcast( context, REMINDER_NOTIFICATION_ID, intent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT ) val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager // 计算下一次触发时间(示例为间隔1年) val nextTriggerTime = Calendar.getInstance().apply { timeInMillis = System.currentTimeMillis() add(Calendar.YEAR, 1) }.timeInMillis // 根据系统版本选择合适的闹钟设置方法 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { alarmManager.setExactAndAllowWhileIdle( AlarmManager.RTC_WAKEUP, nextTriggerTime, pendingIntent ) } else { alarmManager.setExact( AlarmManager.RTC_WAKEUP, nextTriggerTime, pendingIntent ) } } companion object { const val notificationTitleExtra = "notification_title" const val notificationMessageExtra = "notification_message" const val REMINDER_NOTIFICATION_ID = 1001 } }
2. 初始设置闹钟(Activity中调用)
在需要启动闹钟的地方调用此方法,设置首次触发时间:
fun setupInitialRepeatAlarm(activity: Activity, notificationTitle: String, notificationMessage: String) { val billName = activity.binding.etBillName.text.toString().trim() val message = "$notificationMessage $billName" val intent = Intent(activity, ReminderNotificationReceiver::class.java) intent.putExtra(ReminderNotificationReceiver.notificationTitleExtra, notificationTitle) intent.putExtra(ReminderNotificationReceiver.notificationMessageExtra, message) val pendingIntent = PendingIntent.getBroadcast( activity, ReminderNotificationReceiver.REMINDER_NOTIFICATION_ID, intent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT ) val alarmManager = activity.getSystemService(Context.ALARM_SERVICE) as AlarmManager // 设置首次触发时间(示例为立即触发,可根据需求调整) val firstTriggerTime = System.currentTimeMillis() if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { alarmManager.setExactAndAllowWhileIdle( AlarmManager.RTC_WAKEUP, firstTriggerTime, pendingIntent ) } else { alarmManager.setExact( AlarmManager.RTC_WAKEUP, firstTriggerTime, pendingIntent ) } }
3. 清单文件注册广播接收器
在AndroidManifest.xml中添加接收器注册:
<receiver android:name=".ReminderNotificationReceiver" android:exported="false"/>
关键细节说明
- 闹钟类型选择:
AlarmManager.RTC_WAKEUP会在触发时唤醒设备,确保通知及时展示;若无需唤醒设备,可改用AlarmManager.RTC。 - PendingIntent Flag:
FLAG_IMMUTABLE是API 30+的推荐设置,避免PendingIntent被篡改;FLAG_UPDATE_CURRENT用于更新已存在的PendingIntent的Extra数据。 - Doze模式适配:
setExactAndAllowWhileIdle是唯一能在Doze模式下触发的闹钟方法,但仅支持单次设置,因此必须在广播接收器中重置下一次闹钟以实现重复效果。 - 通知渠道:Android 8.0+必须创建通知渠道,否则通知无法正常显示。
内容的提问来源于stack exchange,提问作者Femi
相关产品推荐
相关产品推荐

