Our app is crashing on Android O due to the new background execution limits. We are on Firebase version 10.2.1, which is the one that added Android O support.
Seems like an issue with Firebase? Or is there some change needed to support this on our end?
java.lang.IllegalStateException: Not allowed to start service Intent { act=com.google.firebase.INSTANCE_ID_EVENT pkg=my.package.name cmp=my.package.name/my.package.name.MyFcmIdService (has extras) }: app is in background uid UidRecord{30558fa u0a327 RCVR idle procs:1 seq(0,0,0)}
at android.app.ContextImpl.startServiceCommon(ContextImpl.java:1505)
at android.app.ContextImpl.startService(ContextImpl.java:1461)
at android.content.ContextWrapper.startService(ContextWrapper.java:644)
at android.support.v4.content.WakefulBroadcastReceiver.startWakefulService(WakefulBroadcastReceiver.java:99)
at com.google.firebase.iid.zzg.b(zzg.java:9)
at com.google.firebase.iid.zzg.a(zzg.java:72)
at com.google.firebase.iid.zzg.a(zzg.java:2)
at com.google.firebase.iid.FirebaseInstanceIdService.a(FirebaseInstanceIdService.java:23)
at com.google.firebase.iid.FirebaseInstanceIdService.a(FirebaseInstanceIdService.java:34)
at com.google.firebase.iid.FirebaseInstanceId.<init>(FirebaseInstanceId.java:31)
at com.google.firebase.iid.FirebaseInstanceId.getInstance(FirebaseInstanceId.java:47)
at com.google.firebase.iid.FirebaseInstanceId.a(FirebaseInstanceId.java:4)
at com.google.firebase.iid.FirebaseInstanceIdService.a(FirebaseInstanceIdService.java:19)
at com.google.firebase.iid.FirebaseInstanceIdService.b(FirebaseInstanceIdService.java:35)
at com.google.firebase.iid.zzb$zza$1.run(zzb.java:24)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
at java.lang.Thread.run(Thread.java:764)
Update Upgrading to 11.4.2 resolves this issue.
@KaMyLL is right. I had the same issue with our app and could solve it by replacing the IntentService (which we have started within
onTokenRefresh()
) with an JobIntentService.Because I found the
JobScheduler
andJobIntentService
docs a bit confusing, I would like to some everything up with some code snippets. I hope this makes everything clear to everyone having this issue.What is causing this issue?
Due to the new Background Execution Limits of Android 8, you should not start background services anymore when the app could be in background:
And also:
So for Android 7.x and below, using startService() when the app is in background is (as far as I know) no problem. But in Android 8, this results in a crash. In consequence, you should use a
JobScheduler
now. The behavioral difference betweenJobScheduler
and anIntentService
is that an IntentService is executed immediately. On the other hand, a job enqueued to a JobScheduler is not guaranteed to be executed immediately. The Android OS will determine when there is a good point of time to do so in order to be more energy efficient. So there might be a delay. And I have no idea so far how long this could take. So one solution could be to check the OS version and branch your code using if-else. Fortunately, the support library helps us to solve this in a more elegant way without duplicating any code:JobIntentService
, which basically does this for you under the hood.How to reproduce the issue?
The first quote above states that the app still "has a window of several minutes in which it is still allowed to create and use services.", so in order to reproduce and debug the issue (with the example of
onTokenRefresh()
in Firebase), you could set a breakpoint before your start your service withstartService()
. Close the app and wait there for 5-10 minutes. Continue the execution and you will see theIllegalStateException
from this question. Being able to reproduce the issue as fundamental to make sure that our fixes really solve the problem.How to migrate my IntenService to JobIntentService?
I use
FirebaseInstanceIdService.onTokenRefresh()
as an example:a) Add the BIND_JOB_SERVICE permission to your service:
b) Instead of extending from
IntentService
, simply extend fromandroid.support.v4.app.JobIntentService
, rename theonHandleIntent(Intent)
method toonHandleWork(Intent)
, and add a enqueueWork(Context, Intent) convenient function:c) Start the job using the
enqueueWork()
convenient function:I hope this example is helpful. At least after following these steps, I was not able to reproduce the issue on my Android 8 device anymore, and it continues to work an my Android 7 device.
Update
as
FirebaseInstanceIdService
deprecated we should remove this from the code, and use onNewToken fromFirebaseMessagingService
instead.as of today (26/10/2018), FirebaseInstanceIDServie isdeprecated, try this to fix the above issue link
I've done some research about it and the best option is to transform IntentService into JobIntentService available in app compat library. It would behave like IntentService on all pre-Oreo devices. On Android 8 and above it will enqueue job to android system JobScheduler. This job by default have set deadline parameter to 0, so theoretically it should fire as fast as possible.