Update on 06 January 2023
I was facing this issue in my Music Player Application
Context.startForegroundService() did not then call
Service.startForeground()
After a lot of research, I found some solutions. Maybe this will help others
Solution 1:
if your app targets API level 26 or higher, the system imposes restrictions on running background services when the app itself isn't in the foreground. If an app needs to create a foreground service, the app should call startForegroundService()
. The system allows apps to call Context.startForegroundService()
even while the app is in the background. However, the app must call that service's startForeground()
method with five seconds after the service is created.
Note: call startForeground()
in onCreate()
for the service for which you use Context.startForegroundService()
Why this issue is happening because android framework can't guarantee your service get started within 5 seconds but on the other hand framework does have a strict limit on foreground notification must be fired within 5 seconds, without checking if the framework has tried to start the service startForeground()
a notification must be in both onCreate()
and onStartCommand()
because if your service is already created and your activity is trying to start it again, onCreate()
won't be called. notification ID must not be 0 otherwise same crash will happen even if it's not the same reason.
stopSelf
must not be call before startForeground
.
Background Service Limitations:
The app service has five seconds to call startForeground()
, the app does not call startForegroudn()
within the time limit, the system stops the service and declares the app to be ANR.
Possibilities:
- Either your foreground service get destroyed/finished before call
the
startForeground()
method.
- or if the foreground service is already instantiated and it's getting call again, then
onCreate()
method will not be call, instead onStartCommand()
will be called. Then move your logic to onStartCommand()
to call startForeground()
method.
- Your notification id in
startForeground()
must not be 0, otherwise it will also cause the crash.
Solution 2:
Call startForeground()
in both onCreate()
and onStartCommand()
(it's ok to call startForeground()
many times)
Stop your service using context.stopService()
there is no need to call stopForeground()
or stopSelf()
Start your service using ContextCompat.startForegroundService()
. it will handle different APIs.
if your service has actions (need pending intent ) handle your pending intents with a broadcast receiver rather than your current service(it will call your service in onCreate()
and can be dangerous, or use PendingIntent.FLAG_NO_CREATE
), it's good). practice to has a specific broadcast receiver for handling your service notification actions, I mean create all your pending intent using PendingIntent.getBroadcast()
.
some of the crash reports are from issues related to BOOT_COMPLETED
handling in Oreo
basically once you do startForegroundService()
, you have to do startForeground()
. And if you cannot do startForeground()
in your Service, then you better do that type of checking in your Broadcast Receiver, etc. instead - so that you only start Service when are sure will do startForeground() there
Add Handler()
like this:
Handler().postDelayed(()>ContextCompat.startForegroundService(activity, new Intent(activity, ChatService.class)), 500);
Solution 3:
what to use instead of foreground services?
there are JobScheduler
and JobService
now, which are a better alternative for foreground services. It's a better option, because of that:
While a job is running, the system holds a wakelock on behalf of your app. For this reason, you do not need to take any action to guarantee that the device stays awake for the duration of the job.
It means that you don't need to care about handling wakelocks anymore and that's why it's not different from foreground services. From implementation point of view JobScheduler
is not your service, it's a system's one, presumably it will handle the queue right, and Google will never terminate its own child.
Samsung has switched from startForegroundService
to JobScheduler
and JobService
in their Samsung Accessory Protocol (SAP). It's very helpful when devices like smartwatches need to talk to hosts like phones, where the job does need to interact with a user through an app's main thread. Since the jobs are posted by the scheduler to the main thread, it becomes possible. You should remember though that the job is running on the main thread and offload all heavy stuff to other threads and async tasks.
Solution 4:
The issue is to call Service.startForeground(id, notification)
from the service itself, right? Android Framework unfortunately does not guarantee to call Service.startForeground(id, notification)
within Service.onCreate()
in 5 seconds but throws the exception anyway, so I've come up with this way.
Bind the service to a context with a binder from the service before calling Context.startForegroundService()
If the bind is successful, call Context.startForegroundService()
from the service connection and immediately call Service.startForeground()
inside the service connection.
IMPORTANT NOTE: Call the Context.bindService()
method inside a try-catch because in some occasions the call can throw an exception, in which case you need to rely on calling Context.startForegroundService()
directly and hope it will not fail. An example can be a broadcast receiver context, however getting application context does not throw an exception in that case, but using the context directly does.
Solution 5:
Your app will crash if you call Context.startForegroundService(...)
and then call Context.stopService(...)
before Service.startForeground(...)
is called.
Information provided by google team
This is not a framework bug; it's intentional. If the app starts a
service instance with startForegroundService()
, it must transition
that service instance to the foreground state and show the
notification. If the service instance is stopped before
startForeground()
is called on it, that promise is unfulfilled: this
is a bug in the app.
publishing a Service that other apps can start directly is
fundamentally unsafe. You can mitigate that a bit by treating all
start actions of that service as requiring startForeground()
, though
obviously that may not be what you had in mind.
Solution 6:
I was able to get rid of all crashes. What I did was to remove the call to stopSelf()
. (I was thinking about delaying the stop until I was pretty sure the notification was shown, but I don't want the user to see the notification if it isn't necessary.) When the service has been idle for a minute or the system destroys it normally without throwing any exceptions.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
stopForeground(true);
} else {
stopSelf();
}
Solution 7:
A common crash related to usage of foreground services is :
Context.startForegroundService() did not then call
Service.startForeground()
To get rid of this use this strategy :
Instead of starting the service as a foreground service, start the service as a background service, bind to it, then when you have the service instance available in your activity/UI component, you can directly call a method inside the service which calls Service.startForeground()
and adds the notification.
This might sound like a hack but think about how music apps like Spotify start their service. This is a similar approach. The results are outstanding. Using this approach the issue count reduced to 0 from a significant unrevealable number.