Background Services in Android Oreo

Nicolas Ortiz
Jan 31, 2018 · 3 min read

Or should I say: How to bypass Background Services restrictions in Android O.

If you are like me and have an Android app that relies on a background service, you probably already discover that if you target Android O you cannot start a background service anymore.

I needed my service to run every X minutes, even when the app was in the background (or even not running). Lets be clear, I’m not doing any heavy work on it, but periodicity was important, and that is why I couldn’t trust using a Job. Trust me, I tried GCM & Firebase, and I found times where the Job wasn’t fired for hours. Also, running a Foreground Service for a recurring task it’s not a good idea; almost any user will feel uncomfortably with an ongoing notification saying that your app is ‘always running’ and Android telling you (with another ongoing notification) that app ‘x’ is using the battery, even if the service is not doing any job (Hello new version of Fitbit app)

So, after trying many methods and approaches, I started testing with my old friends AlarmManager and BroadcastReceiver, and this was the only thing that worked for me. (UPDATE: This is almost true, I still find gaps where my task is not fired, but, normally it works pretty well)

Basically what I’m doing is to set the AlarmManager to fire a Broadcast every X minutes. I configured the BroadcastReceiver in my AndroidManifest and inside the onReceive method I just fire a JobIntentService to do the work.

Lets do some code

First, declare the Receiver on the AndroidManifest.xml

<receiver android:name=".AlarmReceiver">
<action android:name="com.test.intent.action.ALARM" />

Second, extend JobIntentService and write your ‘background’ code

public class MyJobIntentService extends JobIntentService {

/* Give the Job a Unique Id */
private static final int JOB_ID = 1000;
public static void enqueueWork(Context ctx, Intent intent) {
enqueueWork(ctx, MyJobIntentService.class, JOB_ID, intent);

protected void onHandleWork(@NonNull Intent intent) {
/* your code here */
/* reset the alarm */


Third, extends BroadcastReceiver

public class AlarmReceiver extends BroadcastReceiver {

public static final String CUSTOM_INTENT = "com.test.intent.action.ALARM";

public void onReceive(Context context, Intent intent) {
/* enqueue the job */
MyJobIntentService.enqueueWork(context, intent);
public static void cancelAlarm() {
AlarmManager alarm = (AlarmManager) ctx.getSystemService(Context.ALARM_SERVICE);

/* cancel any pending alarm */
public static void setAlarm(boolean force) {
AlarmManager alarm = (AlarmManager) ctx.getSystemService(Context.ALARM_SERVICE);
long delay = (1000 * 60 * X);
long when = System.currentTimeMillis();
if (!force) {
when += delay;

/* fire the broadcast */
alarms.set(AlarmManager.RTC_WAKEUP, when, getPendingIntent());
private static PendingIntent getPendingIntent() {
Context ctx; /* get the application context */
Intent alarmIntent = new Intent(ctx, AlarmReceiver.class);

return PendingIntent.getBroadcast(ctx, 0, alarmIntent, PendingIntent.FLAG_CANCEL_CURRENT);

UPDATE: Thanks to @paolop I read more about Doze mode, and even that I couldn’t make the alarm to fire on Doze mode, I think the code for setting the alarm could be updated to

alarmManager.set(AlarmManager.RTC_WAKEUP, when, pendingIntent);
alarmManager.setExact(AlarmManager.RTC_WAKEUP, when, pendingIntent);
else if (SDK_INT >= Build.VERSION_CODES.M) {
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, when, pendingIntent);

That’s it! What you need to do to start this flow is to call

AlarmReceiver.setAlarm(true); /* true will force the broadcast */

This will set an alarm that will broadcast the ‘alarmIntent’ (you can add data to this intent that you will receive in ‘onHandleWork’ from your Job.

The job will recreate the alarm (I chose this path so the job will run ‘X’ minutes after finishing its task)

If you need to cancel the alarm, just call


Remember that Android cancels all the alarms when a device is rebooted. You can follow this to reset your alarm when the device boots:

Another thing to be aware of if that if the user ‘Force Close’ you app for any reason, then your alarm will not fire anymore until you call setAlarm again.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch

Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore

Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store