Triggering Alarms Tutorial for Android: Getting Started

Learn how to set up alarms in your Android apps using the AlarmManager API, and find out about the exact and inexact alarm types as well as best practices. By Denis Buketa.

Leave a rating/review
Download materials
Save for later
Share
You are currently viewing page 3 of 4 of this article. Click here to view the first page.

Triggering an Inexact Alarm

An inexact alarm is triggered at the most efficient time for the device’s battery. In other words, the system doesn’t promise the delivery of the alarm at an exact time in the future.

There are three types of inexact alarms:

  • An alarm after a specific time.
  • An alarm during a time window.
  • A repeating alarm.

With this definition in mind, you’ll start working on the app’s second feature: Rest Reminder.

The first step is creating an AlarmManager instance. Open InexactAlarmsImpl.kt. On top of InexactAlarmsImpl replace TODO (12) with:

private val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager

Just like before, with this code, you add an AlarmManager instance.

Defining Alarm Actions

Next, you need to define what will happen once the system triggers an alarm.

Go to InexactAlarmBroadcastReceiver.kt. Remove TODO (13) and add the following code:

class InexactAlarmBroadcastReceiver : BroadcastReceiver() {

  override fun onReceive(context: Context, intent: Intent) {
    Log.d(
        "InexactAlarmBroadcastReceiver",
        "Alarm with request code ${intent.getIntExtra(ALARM_REQUEST_CODE_EXTRA, -1)} triggered"
    )
    showNotification(
        context,
        NOTIFICATION_CHANNEL_ID,
        NOTIFICATION_CHANNEL_NAME,
        NOTIFICATION_ID,
        "Don't forget to stretch and rest a bit! :]"
    )
    (context.applicationContext as StuddyApplication).apply {
      when (intent.getIntExtra(ALARM_REQUEST_CODE_EXTRA, -1)) {
        INEXACT_ALARM_REQUEST_CODE -> inexactAlarms.clearInexactAlarm()
        INEXACT_ALARM_WINDOW_REQUEST_CODE -> inexactAlarms.clearWindowAlarm()
        INEXACT_REPEATING_ALARM_REQUEST_CODE -> {
          // Do nothing
        }
      }
      alarmRingtoneState.value = playRingtone(context)
    }
  }
}

With this code, you defined actions the app will execute once one of the alarms is triggered. The request code distinguishes alarms. You’ll use those same request codes when creating PendingIntents for alarms.

Next, open AndroidManifest.xml and replace TODO (14) with:

<receiver android:name="com.yourcompany.android.studdy.alarm.InexactAlarmBroadcastReceiver" />

Just as before, you need to explicitly tell the system about your new BroadcastReceiver.

Go back to InexactAlarmsImpl.kt. Scroll to the bottom of the file, and replace TODO (15) with the following code:

private fun createInexactAlarmIntent(alarmRequestCode: Int): PendingIntent {
  val intent = Intent(context, InexactAlarmBroadcastReceiver::class.java).apply {
    putExtra(ALARM_REQUEST_CODE_EXTRA, alarmRequestCode)
  }
  return PendingIntent.getBroadcast(
      context,
      alarmRequestCode,
      intent,
      PendingIntent.FLAG_IMMUTABLE
  )
}

Here, you defined a new method for creating PendingIntent needed to schedule any of the three alarms. You used alarmRequestCode to define the type of alarm you’re scheduling.

Scheduling an Alarm After a Specific Time

Move to scheduleInexactAlarm() and replace TODO (16) with:

// 1
val pendingIntent = createInexactAlarmIntent(INEXACT_ALARM_REQUEST_CODE)
// 2
alarmManager.set(AlarmManager.RTC_WAKEUP, inexactAlarm.triggerAtMillis, pendingIntent)

Here, you:

  1. Use createInexactAlarmIntent() to create a PendingIntent that defines the action the app will execute when the alarm is triggered.
  2. Schedule an inexact alarm in the manager using AlarmManager.set().

Starting from API 19, the trigger time passed to this method is treated as inexact — the alarm won’t be delivered before the set time but may be deferred and delivered some time later. If your app runs on a device with Android 12 or higher, the system will invoke the alarm within one hour of the supplied trigger time, but only if the device isn’t in low-battery mode. Those rare gems of apps with targetSdkVersion before API 19 will continue to get the previous alarm behavior — all scheduled alarms are treated as exact.

The system groups inexact alarms to minimize how often the device needs to wake, minimizing battery use. The rule of thumb is that the system won’t defer alarms scheduled in the near future as long as alarms scheduled far in the future.

Similar to exact alarms, setAndAllowWhileIdle() allows you to schedule inexact alarms that will trigger even if the system is in low-power idle, aka Doze mode.

To cancel this alarm, find clearInexactAlarm() and replace TODO (17) with following lines:

val pendingIntent = createInexactAlarmIntent(INEXACT_ALARM_REQUEST_CODE)
alarmManager.cancel(pendingIntent)

sharedPreferences.clearInexactAlarm()
inexactAlarmState.value = ExactAlarm.NOT_SET

Here, you create a PendingIntent, which should be canceled, and pass it to AlarmManager. Then, you clean up SharedPreferences and reset inexactAlarmState‘s state.

Build and run. Open the Rest tab, enter a time in the future for the Rest alarm and tap Set.

Scheduling inexact alarms

Logs showing when inexact alarm was triggered

In the demo, the alarm was scheduled for 2:32:00 PM, but the logs show the alarm was triggered at 2:33:04 PM. The system decided that was a good moment to trigger it.

Scheduling an Alarm During a Time Window

You can also have a time window in which you want to trigger an alarm. In scheduleWindowAlarm(), replace TODO (18) with:

val pendingIntent = createInexactAlarmIntent(INEXACT_ALARM_WINDOW_REQUEST_CODE)
alarmManager.setWindow(
    AlarmManager.RTC_WAKEUP,
    windowAlarm.triggerAtMillis,
    windowAlarm.windowLengthMillis,
    pendingIntent
)

To schedule an alarm during a time window, you use setWindow(). The key difference from the previous methods you used is that you pass a window start time and a window length. The system will never trigger the alarm before the trigger time. If the device isn’t in low-battery mode, it delivers the alarm within the specified time window, starting from the given trigger time.

For apps targeting Android 12 (API level 31) or higher, the system can delay invoking an inexact alarm by at least 10 minutes. Because of that, be careful to pass more than 600000 (milliseconds) as the windowLengthMillis argument.

If your app requires a higher time precision, use exact alarms instead.

To cancel this alarm, in clearWindowAlarm(), replace TODO (19) with this:

val pendingIntent = createInexactAlarmIntent(INEXACT_ALARM_WINDOW_REQUEST_CODE)
alarmManager.cancel(pendingIntent)

sharedPreferences.clearWindowAlarm()
windowAlarmState.value = WindowAlarm.NOT_SET

The code is the same as the previous canceling, but more specified for the inexact alarm (different state names, different request code, etc.). The logic is the same.

Build and run. Open the Rest screen, enter a time in the future for the Rest Window, specify a window length of at least 10 minutes, and tap Set.

Scheduling window alarm

Logs showing when window alarm was triggered

In the demo, the alarm was scheduled for 7:40:00 PM with a window of 10 minutes. The logs show the alarm was triggered at 7:42:19 PM.

Scheduling a Repeating Alarm

It’s also possible to schedule a repeating alarm, for example, every Saturday.

Move to scheduleRepeatingAlarm() and replace TODO (20) with:

val pendingIntent = createInexactAlarmIntent(INEXACT_REPEATING_ALARM_REQUEST_CODE)
alarmManager.setInexactRepeating(
    AlarmManager.RTC_WAKEUP,
    repeatingAlarm.triggerAtMillis,
    repeatingAlarm.intervalMillis,
    pendingIntent
)

To schedule a repeating alarm, you use setInexactRepeating(). The main difference from the previous methods you used is that you pass a trigger time and an interval. The first alarm is triggered within the specified time window, starting from the given trigger time. On average, the system triggers subsequent alarms after the specified time window elapses. But keep in mind that the time between two consecutive alarms might vary.

To cancel this alarm, in clearRepeatingAlarm(), replace TODO (21) with this:

val pendingIntent = createInexactAlarmIntent(INEXACT_REPEATING_ALARM_REQUEST_CODE)
alarmManager.cancel(pendingIntent)

sharedPreferences.clearRepeatingAlarm()
repeatingAlarmState.value = RepeatingAlarm.NOT_SET

Again, you’re using a similar code for canceling, just with a different request code and state. The logic stays the same as well.

Build and run. Open the Rest screen, enter a time in the future for the Repeating Alarm, specify interval length, and tap Set.

Scheduling repeating alarm

Logs showing when repeating alarm was triggered

In the demo, the alarm was scheduled for 7:50:00 PM with an interval of five minutes. The logs show the first alarm was triggered at 7:51:39 PM and the second one at 7:56:19.