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?
Defining an Alarm Action
Now that all requirements for using exact alarm APIs are implemented, it’s time to implement an actual alarm.
In ExactAlarmBroadcastReceiver.kt, replace TODO (5) with:
class ExactAlarmBroadcastReceiver : BroadcastReceiver() {
// 1
override fun onReceive(context: Context, intent: Intent) {
// 2
showNotification(
context,
NOTIFICATION_CHANNEL_ID,
NOTIFICATION_CHANNEL_NAME,
NOTIFICATION_ID,
"Time to study!"
)
(context.applicationContext as StuddyApplication).apply {
exactAlarms.clearExactAlarm()
alarmRingtoneState.value = playRingtone(context)
}
}
}
With this code:
- You defined the action that the app will execute once the alarm fires. You created a custom
BroadcastReceiver
class. It has only one method, which is necessary for eachBroadcastReceiver
. - Once the
BroadcastReceiver
receives an event, it shows a notification, removes the running alarm, and starts playing the ringtone.
Next, open AndroidManifest.xml and find TODO (6) below the activity
tag. Replace it with:
<receiver android:name="com.yourcompany.android.studdy.alarm.ExactAlarmBroadcastReceiver" />
Here, you declared your custom class as BrodcastReceiver
to the system. The system must know this info to be able to respond appropriately.
Now, you need to define a method for PendingIntent
needed to schedule an alarm.
Navigate to ExactAlarmsImpl.kt. At the bottom of the class, replace TODO (7) with:
private fun createExactAlarmIntent(): PendingIntent {
// 1
val intent = Intent(context, ExactAlarmBroadcastReceiver::class.java).
// 2
return PendingIntent.getBroadcast(
context,
EXACT_ALARM_INTENT_REQUEST_CODE,
intent,
PendingIntent.FLAG_IMMUTABLE
)
}
Here’s a breakdown:
- You defined an
Intent
with theExactAlarmBroadcastReceiver
you created before. - The
return
statement builds thePendingIntent
needed to schedule an alarm.PendingIntent
receivescontext
, a private request code for the sender and the intent. The last argument isFLAG_IMMUTABLE
, a flag indicating that the createdPendingIntent
should be immutable. In most cases for scheduling alarms, there’s no need to allow updatingPendingIntent
.
Scheduling an Alarm
Your app now correctly handles special app access and defines the action that’s executed when the alarm is triggered. The next step is to write logic for scheduling exact alarms.
Before doing that, ensure you switch back on the Allow setting alarms and reminders option.
Go to ExactAlarmsImpl.kt and look for setExactAlarmSetAlarmClock()
. Notice it contains TODO (8). Replace the TODO with the following code:
// 1
val pendingIntent = createExactAlarmIntent()
// 2
val alarmClockInfo = AlarmManager.AlarmClockInfo(triggerAtMillis, pendingIntent)
// 3
alarmManager.setAlarmClock(alarmClockInfo, pendingIntent)
Two things happened here:
- You used
createExactAlarmIntent()
to create aPendingIntent
, which defines the action the app executes when the alarm is triggered. - You created an
AlarmClockInfo
object that specifies what time to trigger the alarm. - Using
setAlarmClock()
ofAlarmManager
, you scheduled the alarm.
By scheduling an alarm this way, the system invokes an alarm at a precise time in the future. This kind of alarm is highly visible to users and, to ensure absolute precision, the system won’t modify the delivery time. The system will even leave low-power modes, if necessary, to deliver exact alarms.
Build and run the app. Enter a time in the future, and tap Set. The quickest way to test the alarm is to schedule it to trigger a minute or two from the current device time. Once the alarm is triggered, stop the ringtone by tapping Stop Alarm or by swiping off the notification.
But what if it’s not necessary to trigger the alarm immediately? Then, you should use setExact()
.
In setExactAlarmSetExact()
, replace TODO (9) with the following code:
// 1
val pendingIntent = createExactAlarmIntent()
// 2
alarmManager.setExact(AlarmManager.RTC_WAKEUP, triggerAtMillis, pendingIntent)
With these lines:
- You also schedule an exact alarm. You use the same
createExactAlarmIntent()
method to create aPendingIntent
. - Instead of
setAlarmClock()
, you usesetExact()
to schedule an alarm.
By passingAlarmManager.RTC_WAKEUP
for an alarm type, you declared that the system should invoke an alarm at a nearly precise time in the future. The device will wake up and fire a pending intent. Keep in mind that if a device is in a low-power mode, the system won’t invoke an alarm.
If an action that you want to trigger isn’t time-critical, you should use this method to set exact alarms.
To test this, in scheduleExactAlarm()
replace the setExactAlarmSetAlarmClock()
call with this method:
setExactAlarmSetExact(exactAlarm.triggerAtMillis)
Handling Idle State
Surely you’re wondering how to ensure an alarm invokes on the device in low-power mode. The solution is using setExactAndAllowWhileIdle()
.
In setExactAlarmSetExactAndAllowWhileIdle()
, replace TODO (10) with the following lines:
val pendingIntent = createExactAlarmIntent()
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerAtMillis, pendingIntent)
This code is very similar to the previous example, but instead of setExact()
, you use setExactAndAllowWhileIdle()
. Just like before, the system invokes an alarm at a moment very close to the specified time in the future, but this time, it invokes an alarm even if the system is in a low-battery mode.
Understanding Alarm Types
In the previous section, you used AlarmManager.RTC_WAKEUP
for an alarm type when scheduling an exact alarm. There are four exact alarm types in total:
- ELAPSED_REALTIME: The system considers the time since your device booted in calculating when to fire a pending intent. The system doesn’t wake the device for this alarm. The time during which the device was asleep is also taken into account.
- ELAPSED_REALTIME_WAKEUP: This is similar to previous type, but the system wakes the device to fire the pending intent.
- RTC: Fires the pending intent at the specified time but doesn’t wake the device.
- RTC_WAKEUP: At the specified time, the system fires an alarm.
Canceling an Alarm
Now that you know how to schedule alarm, you’ll learn how to cancel one. In clearExactAlarm()
, replace TODO (11) with:
// 1
val pendingIntent = createExactAlarmIntent()
alarmManager.cancel(pendingIntent)
// 2
sharedPreferences.clearExactAlarm()
exactAlarmState.value = ExactAlarm.NOT_SET
Here is an explanation:
- This code first creates a
PendingIntent
that you use to schedule an alarm. Then, it callscancel()
on theAlarmManager
and passes thatPendingIntent
.This action cancels the scheduled alarm. - You clear the alarm data from the shared preferences and update the
exactAlarmState
.
Congrats! You learned a lot about exact alarms. Next, you’ll try out inexact alarms.