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.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Triggering Alarms Tutorial for Android: Getting Started
25 mins
- Getting Started
- Triggering an Exact Alarm
- Defining an AlarmManager Instance
- Declaring a Permission
- Checking Your App’s Permissions
- Opening the Alarms & Reminders Screen
- Handling App Permission
- Defining an Alarm Action
- Scheduling an Alarm
- Understanding Alarm Types
- Canceling an Alarm
- Triggering an Inexact Alarm
- Defining Alarm Actions
- Scheduling an Alarm After a Specific Time
- Scheduling an Alarm During a Time Window
- Scheduling a Repeating Alarm
- Rescheduling an Alarm When the Device Restarts
- Exploring Best Practices
- Considering System Resource Consumption
- Understanding Acceptable Use Cases For an Exact Alarm
- Using Alarms For Network Requests
- Alarm Time Precision
- Avoid Basing Your Alarm on Clock Time
- Where to Go From Here?
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 PendingIntent
s 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:
- Use
createInexactAlarmIntent()
to create aPendingIntent
that defines the action the app will execute when the alarm is triggered. - 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.
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.
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.
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.