AlarmManager — 是一种 Android 系统服务,允许应用程序在指定时间执行任务,即使应用程序未运行或设备处于睡眠模式。与 JobScheduler 或 WorkManager 不同,AlarmManager 保证了触发的准确性,使其成为闹钟、日历提醒和时间关键型任务不可或缺的工具。根据 Android Developers, 2026,从 Android 4.4 开始 setRepeating 表现为不精确的重复,而精确闹钟需要使用 setExact 或 setAlarmClock。
要点
AlarmManager — 是一种 Android 系统服务,提供用于安排具有精确或近似执行时间的任务的 API。自第一个 Android 版本以来就已存在,并且仍然是独立于应用程序和设备状态在指定时刻执行代码的唯一可靠方式。
工作原理很简单:应用程序向系统发送一个 PendingIntent,指定触发时间。当所需时刻到来时,系统向注册的 BroadcastReceiver 发送 Intent 或启动 Service。即使设备处于睡眠模式,WakeLock 也允许处理器唤醒并处理事件。
AlarmManager 的主要应用领域 — 需要精确时间的任务:闹钟、日历提醒、在特定时间启动长时间操作。对于精度不关键的任务(每天同步一次),Google 推荐使用 WorkManager 或 JobScheduler,因为它们更节能。
AlarmManager 从应用程序接收 PendingIntent 和触发时间。系统将该请求存储在其内部调度程序中,并在指定时间唤醒处理器以传递 Intent。开发人员必须提前注册一个 BroadcastReceiver 来处理此 Intent。
AlarmManager 支持 4 种闹钟类型:ELAPSED_REALTIME(从启动开始的时间,不唤醒)、RTC(实时时间,不唤醒)、ELAPSED_REALTIME_WAKEUP(从启动开始的时间,唤醒设备)和 RTC_WAKEUP(实时时间,唤醒)。如果任务需要在设备休眠时执行,则需要 WAKEUP 版本。
| 类型 | 时间 | 唤醒 | 示例 |
|---|---|---|---|
| ELAPSED_REALTIME | 从启动开始 | 否 | 运行计时器 |
| RTC | Unix timestamp | 否 | 日志记录 |
| ELAPSED_REALTIME_WAKEUP | 从启动开始 | 是 | 周期性任务 |
| RTC_WAKEUP | Unix timestamp | 是 | 闹钟 |
从 Android 12(API 31)开始,使用精确闹钟(setExact)需要 SCHEDULE_EXACT_ALARM 权限。用户可以在设置中撤销该权限。对于需要显示精确闹钟的应用程序(例如时钟),使用 USE_EXACT_ALARM,该权限在安装时授予。
AlarmManager 提供了三种主要的任务调度方法。方法的选择决定了触发的精度和对设备能耗的影响。
set — 用于单次不精确触发的基本方法。系统可以将时间移动几分钟以与其他事件分组。适用于精度不关键的任务:提醒进入应用程序。
setRepeating — 用于周期性重复的方法。从 Android 4.4(API 19)开始 setRepeating 变得不精确 — 间隔可能会变化。系统不再保证恒定周期。建议使用 setExact 手动重新调度或使用 PeriodicWorkRequest 的 WorkManager 来代替 setRepeating。
setExact — 用于精确单次触发的方法。系统在尽可能接近指定时间时唤醒设备。setAlarmClock — setExact 的一种特殊情况,它还在状态栏中显示闹钟图标,并在所有闹钟类型中具有最高优先级。
val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
// 精确单次闹钟
val intent = Intent(this, AlarmReceiver::class.java)
val pendingIntent = PendingIntent.getBroadcast(
this, 0, intent,
PendingIntent.FLAG_IMMUTABLE
)
alarmManager.setAlarmClock(
AlarmManager.AlarmClockInfo(
targetTime, pendingIntent
),
pendingIntent
)
典型场景 — 在指定时间创建每日提醒。为此使用带有 setExact 的 RTC_WAKEUP。触发时,BroadcastReceiver 启动通知或 Service。处理完成后,需要将任务重新调度到第二天。
class ReminderReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent?) {
val notificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE)
as NotificationManager
val notification = NotificationCompat.Builder(
context, "reminder_channel"
)
.setContentTitle("提醒")
.setContentText("是时候执行任务了")
.setSmallIcon(R.drawable.ic_reminder)
.build()
notificationManager.notify(1001, notification)
}
}
对于每日重复,使用 setExact 计算下一次触发。可以在 Intent 中传递标志以识别不同类型的提醒。确保 BroadcastReceiver 已在 AndroidManifest.xml 中注册并处理 WAKEUP 操作。
fun scheduleDailyReminder(context: Context, hour: Int, minute: Int) {
val calendar = Calendar.getInstance().apply {
set(Calendar.HOUR_OF_DAY, hour)
set(Calendar.MINUTE, minute)
set(Calendar.SECOND, 0)
if (before(Calendar.getInstance())) {
add(Calendar.DAY_OF_MONTH, 1)
}
}
val alarmManager =
context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
alarmManager.setExactAndAllowWhileIdle(
AlarmManager.RTC_WAKEUP,
calendar.timeInMillis,
pendingIntent
)
}
AlarmManager — 一个强大但耗能的工具。每个 WAKEUP 闹钟都会将设备从睡眠模式中唤醒,从而消耗电池。Google 建议尽量减少精确闹钟的使用,并在 Android 6+ 上优先使用 setExactAndAllowWhileIdle 以减少对 Doze 模式的影响。对于不需要精度的周期性任务,请使用带有 PeriodicWorkRequest 的 WorkManager,它不需要唤醒设备并且每次执行时不会消耗电池。
要测试 AlarmManager,请使用 Android Test Framework 中的 TestAlarmManager。它允许模拟闹钟触发而无需等待实际时间。在 Robolectric 中,ShadowAlarmManager 可用,它拦截 set、setExact 和 setRepeating 调用,提供强制触发和检查计划任务数量的方法。对于 BroadcastReceiver 的单元测试,请使用 Robolectric.getForegroundScheduler()。还提供了带有 IdlingResource 的 Espresso 测试,用于在集成测试中等待闹钟触发。
如果应用程序使用 AlarmManager 进行不需要精确时间的周期性任务,请考虑迁移到 WorkManager。最小间隔为 15 分钟的 PeriodicWorkRequest 替代了 setRepeating,同时 WorkManager 保证重启后执行、处理 Doze 模式并且不需要 SCHEDULE_EXACT_ALARM 权限。对于时间关键型任务(早上 7 点的闹钟),AlarmManager 仍然是唯一正确的选择。最佳策略 — 仅对带有 setAlarmClock 的闹钟使用 AlarmManager,并将所有其他后台任务转移到 WorkManager。
对于不需要精度的周期性任务,请使用带有 PeriodicWorkRequest 的 WorkManager。对于具有精确时间但在睡眠模式下触发不关键的任务 — 使用不带 WAKEUP 的 setExact。仅对于必须唤醒的闹钟 — 使用 setAlarmClock 或 RTC_WAKEUP。
同时,在 Android 12+ 上检查 SCHEDULE_EXACT_ALARM 权限是否存在也很重要。如果未授予权限,setExact 将像常规 set(不精确)一样工作。使用 AlarmManager.canScheduleExactAlarms() 进行检查。如果缺少权限,您可以建议用户通过 Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM) 前往设置。
AlarmManager 的一个关键特性 — 所有计划的闹钟在设备重启后都会重置。要恢复它们,您需要声明一个处理 Intent.ACTION_BOOT_COMPLETED 的 BroadcastReceiver,并在 onReceive 方法中重新调度所有活动闹钟。否则,用户在关闭并重新打开手机后将丢失所有提醒。
class BootReceiver : BroadcastReceiver() {
override fun onReceive(
context: Context,
intent: Intent
) {
if (intent.action ==
Intent.ACTION_BOOT_COMPLETED
) {
val prefs =
context.getSharedPreferences("alarms", 0)
val savedTime =
prefs.getLong("next_alarm", 0L)
if (savedTime > System.currentTimeMillis()) {
scheduleReminder(context, savedTime)
}
}
}
}
在实践中,AlarmManager 仍然是闹钟应用、日历、用药提醒以及任何触发时间对用户至关重要的任务的最佳解决方案。对于所有其他后台工作场景,推荐使用 WorkManager 或 JobScheduler。
在 AlarmManager 和 WorkManager 之间选择时,请遵循以下规则:如果用户明确要求在 14:30 提醒 — 使用带有 setAlarmClock 的 AlarmManager。如果任务应“大约每小时执行一次” — 使用带有 PeriodicWorkRequest 的 WorkManager 将更节能且更可靠。
常见问题解答
两种方法都保证精确触发,但 setAlarmClock 还会在状态栏中显示闹钟图标,并通知系统这是用户的闹钟。在 Android 6+ 上,setAlarmClock 对 Doze 模式具有免疫力,而 setExact 可能会被延迟。
重启后,所有计划的闹钟都会重置。要恢复它们,需要注册一个 BroadcastReceiver 来处理 BOOT_COMPLETED 操作,并在 onReceive 中重新调度所有任务。否则,设备开机后闹钟将无法工作。
从 API 19 开始,setRepeating 变得不精确 — 系统可能会移动间隔以节省能源。请改用带有手动重新调度的 setExact 或带有 PeriodicWorkRequest 的 WorkManager,后者提供更可预测的行为。
setExact 需要 SCHEDULE_EXACT_ALARM 权限,用户可以在设置中授予或撤销该权限。对于在时钟界面中显示的 setAlarmClock,使用 USE_EXACT_ALARM,该权限在从商店安装时自动授予。
不可以,AlarmManager 始终通过 PendingIntent 工作。可以是 BroadcastReceiver 的 PendingIntent.getBroadcast、Service 的 PendingIntent.getService 或 Activity 的 PendingIntent.getActivity。没有 PendingIntent,系统将无法将事件传递到应用程序。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。