onStart() call startService() sometimes causes exception in Android O

Viewed 927

Our app is targeting Android O.

After reading Background Service Limitation, I have come to notice that it is safe for a foreground app to launch services. Therefore in our app we called startService() in our Fragment's onStart() method. We think this is ok because in this document it says when onStart is called, fragment is visible to the user and when it is visible, it means this app is a foreground app.

But sometimes, and I must admit it happens pretty rare, we still receive the following exception

java.lang.IllegalStateException: Not allowed to start service Intent { act=ACTION_DEACTIVATE cmp=com.adyxe.sync/.ClientService }: app is in background uid UidRecord{db2a697 u0a19 LAST bg:+7m30s540ms idle change:cached procs:1 seq(0,0,0)}

Why is this happening? Is it safer to call startService() in onResume() just to be more sure that the app is now a foreground app?

1 Answers

First of all, You might have found a bug in Android :)

Regardless, You should use a JobIntentService and enqueue it.

This way the system will run your service when the app is considered in the foreground and you will not see the error. In pre-Oreo versions the service should run immediately regardless of the app foreground state.

Here is an example:

public class ExampleJobIntentService extends JobIntentService {
  static final int JOB_ID = 1000;

  static void enqueueWork(Context context, Intent work) {
    enqueueWork(context, ExampleJobIntentService.class, JOB_ID, work);
  }

  @Override
  public void onCreate() {
    super.onCreate();
    // Some initializations...
  }

  @Override
  protected void onHandleWork(Intent work) {
    // Do your stuff...
  }

  @Override
  public boolean onStopCurrentWork() {
    // return true to reschedule this service if your work failed.
    return false;
  }
}

Then you enqueue it like this:

// The intent is the one that will be received here: onHandleWork(Intent work)
ExampleJobIntentService.enqueueWork(context, intent); 

Register it in the Manifest like this:

<service
     android:name=".ExampleJobIntentService"
     android:permission="android.permission.BIND_JOB_SERVICE" />

If you plan on running the service in the background as well you should add the WAKE_LOCK permission in the Manifest for pre-Oreo versions:

<uses-permission android:name=”android.permission.WAKE_LOCK” />
Related