AlarmManager & JobScheduler
Sometimes background work isn't triggered by an event – it needs to happen at a specific time, or only when certain conditions are met (charging, on WiFi). Android has offered two lower-level APIs for this for years: AlarmManager for time-based triggers, and JobScheduler for constraint-based work. This page covers both honestly, including where they now take a back seat to WorkManager.
AlarmManager: Exact Time Triggers
AlarmManager schedules something to happen at a precise future time, even if RecipeBox isn't running at that moment. It's the right tool when the trigger genuinely is a clock time – for example, a daily reminder telling the user "time to plan dinner":
// Scheduling a daily reminder at 5:00 PM
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, DinnerReminderReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
context, 0, intent, PendingIntent.FLAG_IMMUTABLE);
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.HOUR_OF_DAY, 17);
calendar.set(Calendar.MINUTE, 0);
if (calendar.getTimeInMillis() <= System.currentTimeMillis()) {
calendar.add(Calendar.DAY_OF_YEAR, 1); // already passed today, schedule for tomorrow
}
alarmManager.setExactAndAllowWhileIdle(
AlarmManager.RTC_WAKEUP,
calendar.getTimeInMillis(),
pendingIntent);
- setExactAndAllowWhileIdle() fires at (close to) the exact time requested, even in Doze mode.
- RTC_WAKEUP uses wall-clock time and wakes the device if it's asleep.
- A BroadcastReceiver (see Broadcast Receivers) usually receives the alarm and shows the reminder notification.
The Exact-Alarm Permission
On recent Android versions, scheduling exact alarms requires the SCHEDULE_EXACT_ALARM permission, declared in the manifest:
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
JobScheduler: Constraint-Based Work
JobScheduler takes a different approach: instead of "run at this exact time", you describe conditions under which the work is allowed to run, and the system picks the best moment – batching jobs from different apps together to save battery.
// RecipeSyncJobService.java
public class RecipeSyncJobService extends JobService {
@Override
public boolean onStartJob(JobParameters params) {
new Thread(() -> {
RecipeRepository.getInstance(this).syncPendingRecipes();
jobFinished(params, false); // false = no need to reschedule
}).start();
return true; // work continues on another thread
}
@Override
public boolean onStopJob(JobParameters params) {
return true; // reschedule if the system stopped it early
}
}
// Scheduling the job: only when charging and on an unmetered network
JobScheduler scheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE);
JobInfo jobInfo = new JobInfo.Builder(1001,
new ComponentName(context, RecipeSyncJobService.class))
.setRequiresCharging(true)
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_UNMETERED)
.build();
scheduler.schedule(jobInfo);
Honest Take: JobScheduler Has Been Superseded
JobScheduler was Android's answer to constraint-based background work for years, but it's honestly been largely superseded by WorkManager. WorkManager doesn't replace JobScheduler from scratch – on the versions of Android where JobScheduler is the best available mechanism, WorkManager actually uses JobScheduler internally, alongside AlarmManager and a custom fallback executor on older API levels, and picks whichever is appropriate behind the scenes.
Reference: Comparing the Options
| API | Best for | Guarantees survival of app restart/reboot? | Recommended today? |
|---|---|---|---|
| AlarmManager | An exact, time-precise trigger (a specific clock time) | Yes, if rescheduled after reboot | Only when you truly need exact timing |
| JobScheduler | Constraint-based work (charging, network) on API 21+ | Yes | Rarely used directly anymore – prefer WorkManager |
| WorkManager | Deferrable, guaranteed background work with constraints | Yes, across all supported API levels | Yes – the default choice for new code |
Recommendation
Use WorkManager for constraint-based, deferrable background work in new code – it is covered next and gives you one API that adapts to the device automatically.
Reach for AlarmManager directly only when you need an exact, time-precise trigger that WorkManager's more flexible, battery-friendly scheduling windows can't guarantee.
You generally do not need to call JobScheduler yourself anymore – let WorkManager decide when to use it under the hood.
Troubleshooting
Exact alarm never fires? → Confirm SCHEDULE_EXACT_ALARM is declared, and that the user hasn't revoked exact-alarm access in system settings.
JobScheduler job never runs? → Its constraints (charging, network type) may simply never be met at once during testing – try relaxing them temporarily to confirm the job itself works.
Alarm doesn't survive a reboot? → Re-register it from a BOOT_COMPLETED receiver; alarms don't persist across a device restart on their own.