Runtime exception Android O with boot_completed
Asked Answered
D

2

29

I'm trying to start an IntentService within my BOOT_COMPLETED receiver, but in Android O (API 26) I get:

java.lang.RuntimeException: 
java.lang.IllegalStateException: 
Not allowed to start service Intent { act=intent.action.update cmp=packageName.services.OwnService }: 
app is in background

(Message is in one line, but this way it's easier readable)

How can I do this the correct way?

Demagogic answered 12/6, 2017 at 14:41 Comment(3)
Aw, crap. I'll need to try to reproduce this. Assuming that your analysis is correct, your choices are to either make this a foreground service (use Android 8.0's startForegroundService() instead of startService()) or switch to scheduling a job with JobScheduler.Coexist
Aha! So I'm not the only one suddenly getting crashes on O because of this.Taurine
Yes, my solution was to start a JobScheduler (or JobIntentService). It works very well.Demagogic
C
55

Here are some options that I outlined in a blog post:

Workaround #1: startForegroundService()

Your BroadcastReceiver that receives the ACTION_BOOT_COMPLETED broadcast could call startForegroundService() instead of startService() when on Android 8.0+:

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Build;

public class OnBootReceiver extends BroadcastReceiver {

  @Override
  public void onReceive(Context context, Intent intent) {
    Intent i=new Intent(context, TestIntentService.class);

    if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.O) {
      context.startForegroundService(i);
    }
    else {
      context.startService(i);
    }
  }
}

Note that this works, to an extent, even if your service does not actually ever call startForeground(). You are given a window of time to get around to calling startForeground(), "comparable to the ANR interval to do this". If your work is longer than a millisecond but less than a few seconds, you could skip the Notification and the startForeground() call. However, you will get an error in LogCat:

E/AndroidRuntime: FATAL EXCEPTION: main
 Process: com.commonsware.myapplication, PID: 5991
 android.app.RemoteServiceException: Context.startForegroundService() did not then call Service.startForeground()
     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1775)
     at android.os.Handler.dispatchMessage(Handler.java:105)
     at android.os.Looper.loop(Looper.java:164)
     at android.app.ActivityThread.main(ActivityThread.java:6541)
     at java.lang.reflect.Method.invoke(Native Method)
     at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)

Of course, if you do not mind having a Notification briefly, you are welcome to use startForeground() as Android expects you to, in which case you can do background work normally, albeit with an entry showing up in the user's notification shade.

Workaround #2: goAsync()

BroadcastReceiver has offered goAsync() since API Level 11. This allows your receiver to do work off the main application thread, so you could get rid of the IntentService entirely and move your code into the BroadcastReceiver. You still only have the ANR timeout period to work with, but you will not be tying up your main application thread. This is better than the first workaround, insofar as it has the same time limitation but avoids the nasty error. However, it does require some amount of rework.

Workaround #3: JobScheduler

If your work will take more than a few seconds and you want to avoid the Notification, you could modify your code to implement a JobService and work with JobScheduler. This has the added advantage of only giving you control when other criteria are met (e.g., there is a usable Internet connection). However, not only does this require a rewrite, but JobScheduler is only available on Android 5.0+, so if your minSdkVersion is less than 21, you will need some other solution on the older devices.

UPDATE: Eugen Pechanec pointed out JobIntentService, which is an interesting JobService/IntentService mashup.

Coexist answered 12/6, 2017 at 17:48 Comment(17)
Use the open source Firebase JobDispatcher might be an option for backward compatibility: github.com/firebase/firebase-jobdispatcher-androidOudh
@M66B: Yes, as might JobIntentService in the v26 support library -- see answer update.Coexist
Although JobIntentService is certainly interesting, it doesn't support job (work) conditions (like unmetered network available or device is charging). However, JobDispatcher can replace JobScheduler with minor code changes in most cases. At least I had success with it with minor effort.Oudh
I add that it's not needed if the app is ignoring battery optimizations, in that case it can call startServiceSeigniory
The "goAsync" option work for about 10 seconds till it gets ANR, right ? It even says so in the docs : "As a general rule, broadcast receivers are allowed to run for up to 10 seconds" (here: developer.android.com/reference/android/content/… ) . I think it should cover most cases.Mashburn
Does using JobIntentService mean the job will try to work right away?Mashburn
@androiddeveloper: "I think it should cover most cases" -- that would depend on what work the IntentService did. "Does using JobIntentService mean the job will try to work right away?" -- it will be scheduled to run right away. However, at boot time, there is a fair bit of memory thrashing, because dozens of apps request this broadcast. The JobIntentService documentation indicates that the work may be delayed somewhat in these sorts of conditions.Coexist
@Coexist About JobIntentService , will it try to work right away in normal conditions (meaning not on boot) ?Mashburn
FYI, the RemoteServiceException causes Android to display the app has stopped dialog on the pixel.Glycoside
In my case, I start service as below: startForegroundService(new Intent(...)). from onStartCommand() of service class I am putting a notification and make this foreground service via startForeground(). Once task is done, I call stopService(). But in many cases I get a force code like below: Context.startForegroundService() did not then call Service.startForeground() Based on some AOSP code analysis, i realised that force close is shown as showing Notification is not completed by framework.Famine
// Check to see if the service had been started as foreground, but being // brought down before actually showing a notification. That is not allowed. Message msg = mAm.mHandler.obtainMessage( ActivityManagerService.SERVICE_FOREGROUND_CRASH_MSG); Not sure how I can I make sure that before stop service call that notification is already posted. Any clue on this ? Note: this happens only when some tasks get finished very quickly. In my case, most of the tasks works fine but only for super quick tasks this is happeningFamine
@vicky: I recommend that you ask a separate Stack Overflow question, as it is very difficult to follow what you have in your two comments.Coexist
Would schedule a WorkManager be another alternative? Using WorkManager seem much easy and straightforward.Verdi
@CheokYanCheng: Probably, though I have not tried one from an ACTION_BOOT_COMPLETED receiver. Also, in general, I'm not sure how well this works from a receiver -- I don't know how they are handling ensuring that your work gets scheduled with JobScheduler when you are not in the foreground.Coexist
According to developer.android.com/about/versions/oreo/background, when you receive BOOT_COMPLETED shouldn't your app be put on a whitelist, since it is handling a task like "Receiving a broadcast, such as an SMS/MMS message"?Pagan
@jpmcosta: I guess so, though I have not tried it personally.Coexist
Thanks for answer. As far as I can tell, that may vary with the device, as I'm having issues with some devices. I will have to test it further.Pagan
E
1

You may want to check the following section of the Android O behaviour changes documentation https://developer.android.com/preview/features/background.html#services

It now limits when the app is able to start background services.

Ehman answered 12/6, 2017 at 14:59 Comment(1)
Link is not working, can you update it please?Outspoken

© 2022 - 2024 — McMap. All rights reserved.