Scheduling repeating tasks in Android with AlarmManager
Many Android apps need to perform work at specific times or after certain intervals, whether checking for new messages, refreshing weather data, or running a daily backup. In Australia, where a huge share of the smartphone market runs on Android, developers often build features that respect local time zones across AEST, ACST, and AWST, and that account for the unique ways people here interact with their phones during long commutes on Sydney's train network or quiet evenings in regional towns.
AlarmManager remains one of the most reliable ways to schedule repeating tasks on Android, even as newer APIs like WorkManager gain popularity. By understanding how it works, how it interacts with battery savers, and where alternatives fit better, you can choose the right tool for each situation and keep your app responsive without draining the user's battery.
What AlarmManager actually does
AlarmManager is a system service that lets your app schedule code to run at a particular moment in the future, either as a one-off trigger or as a repeating cycle. It hands the work off to the operating system, so your process does not need to be alive for the alarm to fire. When the time arrives, the system wakes your app by delivering an Intent to a BroadcastReceiver, and your logic runs in the background.
This model is well suited to tasks that need to happen at a precise clock time, such as posting a daily cricket score update, sending a reminder before the user's morning commute, or syncing data at the top of every hour. For inexact, deferrable work like compressing logs or syncing analytics, modern alternatives are usually a better fit.
Compared with a simple Handler or Timer, AlarmManager survives device reboots and works even when the app is not running. That comes with trade-offs, however, because tighter scheduling puts the app into restricted power states on later Android versions.
Preparing permissions and time zones
Before scheduling any recurring alarm, you need to think about a few basics. On Android 12 and above, declaring SCHEDULE_EXACT_ALARM or using the USE_EXACT_ALARM permission is required if you want precise timing. For most repeating tasks that are not tied to a strict wall-clock moment, inexact alarms avoid the need for any special permission and are kinder to battery life.
Because Australia spans three main time zones, any scheduling logic should derive its offsets from java.util.TimeZone rather than hardcoding UTC values, otherwise users in Perth and Cairns will see their tasks fire at the wrong moment. Daylight saving also shifts AEST to AEDT during summer in the south-east states, so always pull the current zone through the user's locale settings before calculating trigger times.
You can declare the permission your app needs in the manifest, then build a PendingIntent that targets your receiver. The PendingIntent carries your action and extras through process boundaries, which is exactly what AlarmManager needs to wake your code later.
Creating a recurring alarm step by step
To schedule a repeating task, you call one of the setRepeating variants on AlarmManager, passing in the alarm type, the trigger time, the interval, and the PendingIntent. The interval can be INTERVAL_FIFTEEN_MINUTES, INTERVAL_HALF_HOUR, INTERVAL_HOUR, INTERVAL_HALF_DAY, or INTERVAL_DAY, which keeps scheduling predictable across devices.
On Android 4.4 and later, setRepeating became inexact, meaning the system batches similar alarms to save power. If you need precise timing for something like a medication reminder, use setExactAndAllowWhileIdle and reschedule the next occurrence yourself inside the BroadcastReceiver. Studying deeper background execution patterns can help you decide whether your use case really warrants the cost.
A typical setup grabs the AlarmManager through context.getSystemService, builds a PendingIntent with FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE on newer platforms, and calls setRepeating with RTC_WAKEUP so the alarm fires even when the screen is off. Inside the receiver, perform the small piece of work quickly, then hand off heavier tasks to WorkManager.
Handling Doze mode and App Standby
Doze mode arrived in Android 6.0 and quietly changed how alarms behave when the phone has been sitting idle for a while. Standard alarms are deferred until the device leaves the deep idle state, which means a repeating task might run hours later than expected. For Australians who park their phone on the bench during a long shift or out in the shed on the property, this can throw off the timing of background syncs.
Workarounds include scheduling the next alarm with setExactAndAllowWhileIdle, requesting the user to add your app to the battery optimisation whitelist, or using setAlarmClock, which the system treats as a high-priority user-visible alarm. App Standby Buckets, introduced in Android 9, also throttle alarms for apps the user rarely opens, so a single whitelist grant may not be enough.
If you find yourself wrestling with these limits, WorkManager is often a saner default for deferrable, guaranteed work because it handles Doze and bucket restrictions on your behalf. Reserve AlarmManager for cases where the wall-clock moment matters, such as a calendar alert, a timer for a podcast download, or a recurring alert for the Bureau of Meteorology's severe weather updates during storm season.
Comparing AlarmManager with other schedulers
Choosing the right scheduling API depends on whether your task needs precise timing, guaranteed execution, or just eventual completion. AlarmManager is lightweight and precise but offers no chaining, no constraints, and very little resilience against power savers. WorkManager trades precision for robust guarantees and integrates with JobScheduler underneath. Foreground services are an option for long-running user-facing work, but they require a persistent notification.
For most everyday repeating tasks in consumer apps, WorkManager is the recommended starting point. Reach for AlarmManager when the trigger moment has business meaning, like a daily journal prompt at 9pm local time, a payment reminder, or a one-off wake-up call. JobScheduler is the older low-level layer that WorkManager and AlarmManager both build on, and you usually talk to it directly only when implementing a custom scheduling library.
| Feature | AlarmManager | WorkManager | JobScheduler |
|---|---|---|---|
| Precision timing | Yes (with setExact) | No | Limited |
| Survives reboot | Yes (with RECEIVE_BOOT_COMPLETED) | Yes | Yes |
| Doze-friendly | No, unless exact variant | Yes | Yes |
| Constraints support | No | Yes (network, battery, charging) | Yes |
| Minimum API | 1 | 14 | 21 |
| Best fit | Time-critical triggers | Deferrable background work | Low-level scheduling |