Context.startForegroundService() did not then call Service.startForeground()
Asked Answered
C

36

388

I am using Service Class on the Android O OS.

I plan to use the Service in the background.

The Android documentation states that

If your app targets API level 26 or higher, the system imposes restrictions on using or creating background services unless the app itself is in the foreground. If an app needs to create a foreground service, the app should call startForegroundService().

If you use startForegroundService(), the Service throws the following error.

Context.startForegroundService() did not then call
Service.startForeground() 

What's wrong with this?

Chinquapin answered 8/6, 2017 at 2:15 Comment(11)
IOW, please provide a minimal reproducible example. That would include the entire Java stack trace and the code that is triggering the crash.Shane
The bug still here in API 26 and 27 (27.0.3). Affected android versions is 8.0 and 8.1 You could reduce number of crashes by adding the startForeground() both to onCreate() and to onStartCommand(), but the crashes will still happens for some users. The only way to fix it atm is targetSdkVersion 25 in your build.gradle.Garrison
FYI issuetracker.google.com/issues/76112072Militiaman
we can check response from google team here issuetracker.google.com/issues/76112072#comment56 and issuetracker.google.com/issues/76112072#comment36Ron
I have the same problem. I fix this issue. I shared my implementation in this topic #55895136Swansea
Check out here! Context.startForegroundService() did not then call Service.startForeground()Farmstead
The main reason this occurs is because there is an exception before you call startForeground(), check your code againCombustion
Moreover if the service get killed by the system onCreate will never called if started in onDestroy by itself. Move startForeground to onStartCommandCombustion
I also had the same issue with my Pixel 3. I used to manually dismiss the notification before calling stopForeground(true) and stopSelf() stopForeground(true) means letting the os remove the foreground notification. So don't manually dismiss notification if u r calling stopForeground(true)Gawlas
I'm facing the same problem. As a result, I noticed that if you don't use putExtra in the intent, the service will start.Birgitbirgitta
I finally found the solution and its working for all the devices https://mcmap.net/q/82270/-context-startforegroundservice-did-not-then-call-service-startforegroundMarpet
P
149

From Google's docs on Android 8.0 behavior changes:

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 within five seconds after the service is created.

Solution: Call startForeground() in onCreate() for the Service which you use Context.startForegroundService()

See also: Background Execution Limits for Android 8.0 (Oreo)

Pazia answered 12/7, 2017 at 2:51 Comment(10)
I did this in the onStartCommand method, but I'm still getting this error. I called startForegroundService(intent) in my MainActivity. Maybe the service is started too slow. I think the five seconds limit should not exist before they can promise the service is started immediately.Araminta
5 seconds period is definitely not enough, this exception happens very often in debug sessions. I suspect it also WOULD happen in release mode from time to time. And being FATAL EXCEPTION it just crashes the app! I tried to catch it with Thread.setDefaultUncaughtExceptionHandler(), but even on getting it there and ignoring android freezes the application. That's becuase this exception is triggered from handleMessage(), and main loop effectively ends... looking for workaround.Cathe
I called Context.startForegroundService() method in broadcast receiver. so how to handle in this situation? because onCreate() is not available in broadcast receiverWail
If I call startForeground in onCreate(), then even startService() will start it in foreground. what's the purpose of startForegroundService()?Stutsman
@southerton I think you should call it on onStartCommand() instead of onCreate, because if you close the service and start it again, it might go to onStartCommand() without calling onCreate ...Designer
We can check response from google team here issuetracker.google.com/issues/76112072#comment56Ron
I have an app that has 30k active users on Play Store. And I have the same issue: ~30 daily crashes on Android 8+ because of 5 sec time limit to start foreground. Crashes comes from various devices, even this year phones releases(S20Ultra, Motorola...)The Android design does not guarantee the call will be triggered within 5 sec and the service get killed by the OS. Google has a long list of feedback regarding this issue but they do not admit this is design flaw. They should redesign it better before for force us to drop background services.Combustion
Some phone like VIVO, you will still crash even if you are call stopService() after startForeground(), on Android 8.1. see: my logcatBrita
what an offtopic question. it's not about oreo, it still will crash from time to time, just test it with app which has at least 30K usersThaumatology
Guys see my answer its working for me https://mcmap.net/q/82270/-context-startforegroundservice-did-not-then-call-service-startforegroundMarpet
D
126

I called ContextCompat.startForegroundService(this, intent) to start the service then

In service onCreate

 @Override
 public void onCreate() {
        super.onCreate();

        if (Build.VERSION.SDK_INT >= 26) {
            String CHANNEL_ID = "my_channel_01";
            NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
                    "Channel human readable title",
                    NotificationManager.IMPORTANCE_DEFAULT);

            ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);

            Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
                    .setContentTitle("")
                    .setContentText("").build();

            startForeground(1, notification);
        }
}
Demp answered 27/9, 2017 at 14:4 Comment(15)
Me too. But I'm still getting this error occasionally. Maybe the Android can't guarantee it will call the onCreate in 5 seconds. So they should redesign it before they force us follow the rules.Araminta
I was calling startForeground in onStartCommand(), and I was occasionally getting this error. I moved it to onCreate and I haven't seen it since (crossing fingers).Kleeman
This creates a local notification in Oreo saying "APP_NAME is running. Tap to close or see info". How to stop showing that notification?Pachyderm
you can't if you want to run Foreground service this is a new restriction introduced in android o.Demp
@KimiChiu was doing exactly same but still it was crashing, but only while i was in the foreground so I put a check on the ContextCompat.startForegroundService(this, intent) , if app is in foreground do ContextCompat.startService(this, intent), but if it is in background then only do ContextCompat.startForegroundService(this, intent). Check if it helpsPachyderm
No, the Android Team claimed this is an intended behavior. So I just redesigned my app. This is ridiculous. Always need to redesign apps due to these "intended behavior". It's the third time.Araminta
The main cause of this problem is the service was stopped before it was promoted to the foreground. But the assertion didn't stop after the service get destroyed. You can try to reproduce this by adding StopService after calling startForegroundService.Araminta
Do we need to make these changes in case of JobIntentService? Or it is handling it internally?Bolyard
no, the job classes are created to handle the background work in android Oreo. so you don't need to show notification.Demp
I was getting this exception quite often in debug mode, finally decided to catch the exception with custom exception handler and keep the app running. choruscode.blogspot.com/2018/05/…Cathe
Don't forget to add setSmallIcon method to the chain of builder methods, otherwise the default notification "App is running" will be shownBagby
That was a quick solution. Thanks ^^Bismuthic
you are so smart!!! we all do it... but boy your users will still face crash from time to timeThaumatology
Guys see my answer its working for me https://mcmap.net/q/82270/-context-startforegroundservice-did-not-then-call-service-startforegroundMarpet
i need to get user current location without service it not possible.Largely
B
85

Why this issue is happening is because Android framework can't guarantee your service get started within 5 second but on the other hand framework does have strict limit on foreground notification must be fired within 5 seconds, without checking if framework had tried to start the service.

This is definitely a framework issue, but not all developers facing this issue are doing their best:

  1. startForeground a notification must be in both onCreate and onStartCommand, because if your service is already created and somehow your activity is trying to start it again, onCreate won't be called.

  2. notification ID must not be 0 otherwise same crash will happen even it's not same reason.

  3. stopSelf must not be called before startForeground.

With all above 3 this issue can be reduced a bit but still not a fix, the real fix or let's say workaround is to downgrade your target sdk version to 25.

And note that most likely Android P will still carry this issue because Google refuses to even understand what is going on and does not believe this is their fault, read #36 and #56 for more information

Baldwin answered 5/6, 2018 at 3:17 Comment(11)
Why both onCreate and onStartCommand? Can you just put it in onStartCommand?Ontiveros
stopSelf must not be called before startForeground => or directly after, because it seems to cancel the "startForeground" when you do.Bianco
I executed some tests, and even if onCreate is not called if the service is already created, you don't need to call startForeground again. Hence you could call it only on the onCreate method and not in the onStartCommand. Probably they consider the single instance of service as being in foreground after the first call till its end.Monorail
I'm using 0 as the notification ID of the initial call for startForeground. Changing it to 1 fixes the issue for me (hopefully)Aquacade
So what is the solution?Arreola
I have been calling startForeground() in both onCreate() and onStartCommand() and I have no issues. Before when I had in one of them there were some crashes.Inseverable
because if your service is already created and somehow your activity is trying to start it again, onCreate won't be called. - if service was started before then this service is already in foreground because it becomes foreground in onCreate, so this sentence doesn't make any sense...Thaumatology
@Thaumatology Your comments to this topic are quite reasonable. Have you found a solution? I'm also struggling to find one. Looks like the best option we can do is to ensure that startForegroundService is called when an app is not overloaded. For example calling it on an app initialization should be avoided.Dimaggio
I had wasted so much time figuring out why it was crashing and not understanding why. The issue was the notification ID for startForeground(). Change it to something other than 0.Restaurant
I'm using 0 as the notification ID of the initial call for startForeground. Changing it to 1 fixes the issue for me (hopefully) This solution resolved my problem too. Has anyone figured out why it is the way it is?Parrot
Guys see my answer its working for me https://mcmap.net/q/82270/-context-startforegroundservice-did-not-then-call-service-startforegroundMarpet
J
69

I know, too many answers have been published already, however the truth is - startForegroundService can not be fixed at an app level and you should stop using it. That Google recommendation to use Service#startForeground() API within 5 seconds after Context#startForegroundService() was called is not something that an app can always do.

Android runs a lot of processes simultaneously and there is no any guarantee that Looper will call your target service that is supposed to call startForeground() within 5 seconds. If your target service didn't receive the call within 5 seconds, you're out of luck and your users will experience ANR situation. In your stack trace you'll see something like this:

Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{1946947 u0 ...MessageService}

main" prio=5 tid=1 Native
  | group="main" sCount=1 dsCount=0 flags=1 obj=0x763e01d8 self=0x7d77814c00
  | sysTid=11171 nice=-10 cgrp=default sched=0/0 handle=0x7dfe411560
  | state=S schedstat=( 1337466614 103021380 2047 ) utm=106 stm=27 core=0 HZ=100
  | stack=0x7fd522f000-0x7fd5231000 stackSize=8MB
  | held mutexes=
  #00  pc 00000000000712e0  /system/lib64/libc.so (__epoll_pwait+8)
  #01  pc 00000000000141c0  /system/lib64/libutils.so (android::Looper::pollInner(int)+144)
  #02  pc 000000000001408c  /system/lib64/libutils.so (android::Looper::pollOnce(int, int*, int*, void**)+60)
  #03  pc 000000000012c0d4  /system/lib64/libandroid_runtime.so (android::android_os_MessageQueue_nativePollOnce(_JNIEnv*, _jobject*, long, int)+44)
  at android.os.MessageQueue.nativePollOnce (MessageQueue.java)
  at android.os.MessageQueue.next (MessageQueue.java:326)
  at android.os.Looper.loop (Looper.java:181)
  at android.app.ActivityThread.main (ActivityThread.java:6981)
  at java.lang.reflect.Method.invoke (Method.java)
  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:493)
  at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:1445)

As I understand, Looper has analyzed the queue here, found an "abuser" and simply killed it. The system is happy and healthy now, while developers and users are not, but since Google limits their responsibilities to the system, why should they care about the latter two? Apparently they don't. Could they make it better? Of course, e.g. they could've served "Application is busy" dialog, asking a user to make a decision about waiting or killing the app, but why bother, it's not their responsibility. The main thing is that the system is healthy now.

From my observations, this happens relatively rarely, in my case approximately 1 crash in a month for 1K users. Reproducing it is impossible, and even if it's reproduced, there is nothing you can do to fix it permanently.

There was a good suggestion in this thread to use "bind" instead of "start" and then when service is ready, process onServiceConnected, but again, it means not using startForegroundService calls at all.

I think, the right and honest action from Google side would be to tell everyone that startForegourndServcie has a deficiency and should not be used.

The question still remains: what to use instead? Fortunately for us, 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.

This service executes each incoming job on a Handler running on your application's main thread. This means that you must offload your execution logic to another thread/handler/AsyncTask of your choosing

The only pitfall of switching to JobScheduler/JobService is that you'll need to refactor old code, and it's not fun. I've spent last two days doing just that to use the new Samsung's SAP implementation. I'll watch my crash reports and let you know if see the crashes again. Theoretically it should not happen, but there are always details that we might not be aware of.

UPDATE No more crashes reported by Play Store. It means that JobScheduler/JobService do not have such a problem and switching to this model is the right approach to get rid of startForegroundService issue once and forever. I hope, Google/Android reads it and will eventually comment/advise/provide an official guidance for everyone.

UPDATE 2

For those who use SAP and asking how SAP V2 utilizes JobService explanation is below.

In your custom code you'll need to initialize SAP (it's Kotlin) :

SAAgentV2.requestAgent(App.app?.applicationContext, 
   MessageJobs::class.java!!.getName(), mAgentCallback)

Now you need to decompile Samsung's code to see what's going on inside. In SAAgentV2 take a look at the requestAgent implementation and the following line:

SAAgentV2.d var3 = new SAAgentV2.d(var0, var1, var2);

where d defined as below

private SAAdapter d;

Go to SAAdapter class now and find onServiceConnectionRequested function that schedules a job using the following call:

SAJobService.scheduleSCJob(SAAdapter.this.d, var11, var14, var3, var12); 

SAJobService is just an implementation of Android'd JobService and this is the one that does a job scheduling:

private static void a(Context var0, String var1, String var2, long var3, String var5, SAPeerAgent var6) {
    ComponentName var7 = new ComponentName(var0, SAJobService.class);
    Builder var10;
    (var10 = new Builder(a++, var7)).setOverrideDeadline(3000L);
    PersistableBundle var8;
    (var8 = new PersistableBundle()).putString("action", var1);
    var8.putString("agentImplclass", var2);
    var8.putLong("transactionId", var3);
    var8.putString("agentId", var5);
    if (var6 == null) {
        var8.putStringArray("peerAgent", (String[])null);
    } else {
        List var9;
        String[] var11 = new String[(var9 = var6.d()).size()];
        var11 = (String[])var9.toArray(var11);
        var8.putStringArray("peerAgent", var11);
    }

    var10.setExtras(var8);
    ((JobScheduler)var0.getSystemService("jobscheduler")).schedule(var10.build());
}

As you see, the last line here uses Android'd JobScheduler to get this system service and to schedule a job.

In the requestAgent call we've passed mAgentCallback, which is a callback function that will receive control when an important event happens. This is how the callback is defined in my app:

private val mAgentCallback = object : SAAgentV2.RequestAgentCallback {
    override fun onAgentAvailable(agent: SAAgentV2) {
        mMessageService = agent as? MessageJobs
        App.d(Accounts.TAG, "Agent " + agent)
    }

    override fun onError(errorCode: Int, message: String) {
        App.d(Accounts.TAG, "Agent initialization error: $errorCode. ErrorMsg: $message")
    }
}

MessageJobs here is a class that I've implemented to process all requests coming from a Samsung smartwatch. It's not the full code, only a skeleton:

class MessageJobs (context:Context) : SAAgentV2(SERVICETAG, context, MessageSocket::class.java) {


    public fun release () {

    }


    override fun onServiceConnectionResponse(p0: SAPeerAgent?, p1: SASocket?, p2: Int) {
        super.onServiceConnectionResponse(p0, p1, p2)
        App.d(TAG, "conn resp " + p1?.javaClass?.name + p2)


    }

    override fun onAuthenticationResponse(p0: SAPeerAgent?, p1: SAAuthenticationToken?, p2: Int) {
        super.onAuthenticationResponse(p0, p1, p2)
        App.d(TAG, "Auth " + p1.toString())

    }


    override protected fun onServiceConnectionRequested(agent: SAPeerAgent) {


        }
    }

    override fun onFindPeerAgentsResponse(peerAgents: Array<SAPeerAgent>?, result: Int) {
    }

    override fun onError(peerAgent: SAPeerAgent?, errorMessage: String?, errorCode: Int) {
        super.onError(peerAgent, errorMessage, errorCode)
    }

    override fun onPeerAgentsUpdated(peerAgents: Array<SAPeerAgent>?, result: Int) {

    }

}

As you see, MessageJobs requires MessageSocket class as well that you would need to implement and that processes all messages coming from your device.

Bottom line, it's not that simple and it requires some digging to internals and coding, but it works, and most importantly - it doesn't crash.

Jinn answered 10/6, 2019 at 22:53 Comment(17)
Good answer but there is a major issue, JobIntentService runs immediately as an IntentService below Oreo, but schedules a Job on and above Oreo, so JobIntentService doesn't start immediately. More infoSememe
@Sememe Service doesn't start immediately when you call startFregroundService either. It starts when Mr. Looper decides to start it, which is obvious from the stack trace, and that's actually the root cause of the problem.Jinn
You're right foreground service may not start within 5 seconds of the time limit (and Google is the one to be blamed for this), but JobService is for like 15 mins interval stuff, I upvoted your post because the approach is good but not useful.Sememe
@Sememe It's very useful and it starts immediately in my case, As I wrote, it is used for real time interactions between phone and a smartwatch. No way, a user would wait 15 minutes to send data between these two. Works very well and never crashes.Jinn
Sounds cool, would make your answer much worth if you can share some sample code, I am new to Android, and found that Job... takes time to start and it is advance version of AlarmManager.Sememe
Probably I will when time permits: I'll need to de-couple it from custom-specific code and de-compile some proprietary Samsung libsJinn
No worries, take your time. Good day!Sememe
hey, i just voted your comment up because i am exactly looking for SAP for samsung. Do you have any code sample or github repo where i can see your implementation.Uncaused
@batmaci I used examples provided by Samsung in AccessorySDK_v2.6.1.zip. Go to Samples(Web), find HelloMessage, Provider(Android)_Consumer(Tizen) and see ProviderActiviy.java and ProviderService.java. I had to convert them to Kotlin in my case.Jinn
@OlegGryb when you use JobService, does it work independent than provider android app. Currently with Foreground service, I am updating Provider app on real time using Consumer watch app. Are you able to do this? FitnesPall app has interesting implementation. When you start watch app, it automatically initializes fitnesspall service on your android phone. I guess that they use job instead of foregroundserviceUncaused
@batmaci - JobService is used by SAP internally. See Update 2 in OP for details.Jinn
quick quesiton: SAAgentV2.requestAgent - where do you call this? within mainactivity oncreate? using SAAgent it wasnt required as it it was registered automatically.Uncaused
@OlegGryb i achieved connection using SAAgentV2 and tested it yesterday for an hour between my watch and phone. it is fluent. Amazing how awesome is the connection without dropping. thank you for your help. I did it using xamarin.android and with c# was bit trickier from java and kotlin ;) now have to check in older android versions if it works also.Uncaused
I think the first comment about it not running immediately is right. I am experiencing this. I am using a widget button to start a JobIntentService.Expulsive
@OlegGryb Does JobService work all the time?. For example will it work for background video recording with screen turned of for 24 hours?Thaumatology
So this is wrong to call JobService as good replacement, cause it can't run all the time, only sticky foreground service can do itThaumatology
Guys see my answer its working for me https://mcmap.net/q/82270/-context-startforegroundservice-did-not-then-call-service-startforegroundMarpet
C
51

Your app will crash if you call Context.startForegroundService(...) and then call Context.stopService(...) before Service.startForeground(...) is called.

I have a clear repro here ForegroundServiceAPI26

I have opened a bug on this at : Google issue tracker

Several bugs on this have been opened and closed Won't Fix.

Hopefully mine with clear repro steps will make the cut.

Information provided by google team

Google issue tracker Comment 36

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.

Re #31, 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.

Google issue tracker Comment 56

There are a couple of different scenarios that lead to the same outcome here.

The outright semantic issue, that it's simply an error to kick something off with startForegroundService() but neglect to actually transition it to foreground via startForeground(), is just that: a semantic issue. That's treated as an app bug, intentionally. Stopping the service before transitioning it to foreground is an app error. That was the crux of the OP, and is why this issue has been marked "working as intended."

However, there are also questions about spurious detection of this problem. That's is being treated as a genuine problem, though it's being tracked separately from this particular bug tracker issue. We aren't deaf to the complaint.

Cosmo answered 21/3, 2018 at 23:39 Comment(5)
Best update I have seen is issuetracker.google.com/issues/76112072#comment56. I rewrote my code to use Context.bindService which completely avoids the problematic call to Context.startForegroundService. You can see my sample code at github.com/paulpv/ForegroundServiceAPI26/tree/bound/app/src/…Cosmo
yes, as I wrote, bind should work, but I've switched already to JobServive and am not going to change anything unless this one gets broken too ':)Jinn
@Cosmo your solution helped me greatly.Delphinus
in my case it's not possible because I don't use context.stopService. I send local broadcast from activity to stop service, service registers to this broadcast, calls stopSelf, so it stops itself, when it calls stopSelf, startForeground was called before 100%Thaumatology
Guys see my answer its working for me https://mcmap.net/q/82270/-context-startforegroundservice-did-not-then-call-service-startforegroundMarpet
G
31

Since everybody visiting here is suffering the same thing, I want to share my solution that nobody else has tried before (in this question anyways). I can assure you that it is working, even on a stopped breakpoint which confirms this method.

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.

  1. Bind the service to a context with a binder from the service before calling Context.startForegroundService()
  2. If the bind is successful, call Context.startForegroundService() from the service connection and immediately call Service.startForeground() inside the service connection.
  3. 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.

This even works when I'm waiting on a breakpoint after binding the service and before triggering the "startForeground" call. Waiting between 3-4 seconds do not trigger the exception while after 5 seconds it throws the exception. (If the device cannot execute two lines of code in 5 seconds, then it's time to throw that in the trash.)

So, start with creating a service connection.

// Create the service connection.
ServiceConnection connection = new ServiceConnection()
{
    @Override
    public void onServiceConnected(ComponentName name, IBinder service)
    {
        // The binder of the service that returns the instance that is created.
        MyService.LocalBinder binder = (MyService.LocalBinder) service;

        // The getter method to acquire the service.
        MyService myService = binder.getService();

        // getServiceIntent(context) returns the relative service intent 
        context.startForegroundService(getServiceIntent(context));

        // This is the key: Without waiting Android Framework to call this method
        // inside Service.onCreate(), immediately call here to post the notification.
        myService.startForeground(myNotificationId, MyService.getNotification());

        // Release the connection to prevent leaks.
        context.unbindService(this);
    }

    @Override
    public void onBindingDied(ComponentName name)
    {
        Log.w(TAG, "Binding has dead.");
    }

    @Override
    public void onNullBinding(ComponentName name)
    {
        Log.w(TAG, "Bind was null.");
    }

    @Override
    public void onServiceDisconnected(ComponentName name)
    {
        Log.w(TAG, "Service is disconnected..");
    }
};

Inside your service, create a binder that returns the instance of your service.

public class MyService extends Service
{
    public class LocalBinder extends Binder
    {
        public MyService getService()
        {
            return MyService.this;
        }
    }

    // Create the instance on the service.
    private final LocalBinder binder = new LocalBinder();

    // Return this instance from onBind method.
    // You may also return new LocalBinder() which is
    // basically the same thing.
    @Nullable
    @Override
    public IBinder onBind(Intent intent)
    {
        return binder;
    }
}

Then, try to bind the service from that context. If it succeeds, it will call ServiceConnection.onServiceConnected() method from the service connection that you're using. Then, handle the logic in the code that's shown above. An example code would look like this:

// Try to bind the service
try
{
     context.bindService(getServiceIntent(context), connection,
                    Context.BIND_AUTO_CREATE);
}
catch (RuntimeException ignored)
{
     // This is probably a broadcast receiver context even though we are calling getApplicationContext().
     // Just call startForegroundService instead since we cannot bind a service to a
     // broadcast receiver context. The service also have to call startForeground in
     // this case.
     context.startForegroundService(getServiceIntent(context));
}

It seems to be working on the applications that I develop, so it should work when you try as well.

Grain answered 16/8, 2019 at 8:33 Comment(9)
Thank you, just implemented your solution in an app with well over 5M installs, let's see. Hope for the best.Kierkegaard
It works for me so far - almost a month later and no more crashes or ANRs related to startForegroundService() - Thanks!Kierkegaard
a tricky solution, I will try it, have 80k active users in my appThaumatology
but you have to unbind service in onStop of activity cycleThaumatology
` // This is the key: Without waiting Android Framework to call this method // inside Service.onCreate(), immediately call here to post the notification.` - I'm not sure about this, how can you start notification before onCreate of Service was called. And when onServiceConnected was called, then it means onCreate was already called, so there is quite nonsense in your sentenceThaumatology
myService.startForeground(myNotificationId, MyService.getNotification()); you can move it to onCreate, because onServiceConnected is called after onCreate!!!Thaumatology
this is still didn't help, I have 80K active users (200K downloads)Thaumatology
Fixed it for us, thanks.Armandinaarmando
Guys see my answer its working for me https://mcmap.net/q/82270/-context-startforegroundservice-did-not-then-call-service-startforegroundMarpet
C
24

Now in Android O you can set the background limitation as below

The service which is calling a service class

Intent serviceIntent = new Intent(SettingActivity.this,DetectedService.class);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
    SettingActivity.this.startForegroundService(serviceIntent);
} else {
    startService(serviceIntent);
}

and the service class should be like

public class DetectedService extends Service { 
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return START_STICKY;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        int NOTIFICATION_ID = (int) (System.currentTimeMillis()%10000);
         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            startForeground(NOTIFICATION_ID, new Notification.Builder(this).build());
        }


        // Do whatever you want to do here
    }
}
Carve answered 5/4, 2018 at 13:27 Comment(14)
Did you tried it in some app that has at least several thousands users? I tried, some users still have the crash issues.Garrison
No no I am using the exact code and I don't see the clients getting crash.Carve
How much users u have? I seen ~10-30+ crashes daily (with the error) in app that had 10k users per day. Of course, it happens only for users with android 8.0 and android 8.1, in case targetSdkVersion is 27. All the problems disappear to 0 crashes right after I set targetSdkVersion to 25.Garrison
only 2200 user :-/ but did not get any crashCarve
@Garrison strange I am getting the ANR on nexus and HTC devices :( do you have any solution ?Carve
@Garrison how did you fix this issue??Minuscule
I was using intent service right inside of this foreground service which creates error and sometimes crashes, I have removed it and worked perfectly wellCarve
@Jeeva, not really. Atm, I use targetSdkVersion 25 with compileSdkVersion 27. It looks likes the best way after lot of experiments.... Hope they will finish developer.android.com/topic/libraries/architecture/… before August 2018 because android-developers.googleblog.com/2017/12/…Garrison
@Garrison Thanks mate let me know if you have any better solutionsMinuscule
This causes a crash "Bad notification for startForeground: java.lang.RuntimeException: invalid channel for service notification". Also, Notification.Builder is deprecated as of API 26Requiescat
no need for version check manually, simply use ContextCompat.startForegroundService(), it will do that for you.Hydrocarbon
why do make it foreground only for Oreo, nonsenseThaumatology
If you are testing on your AVD, remove the code: startService(serviceIntent). Well, AVD will always try to run this code - but it will find a version greater than 26.Antung
Randomizing the NOTIFICATION_ID is quite problematic - a public static constant would be better, because one needs it to update the Notification later on ...anything but 0 works.Slacker
H
18

I have a widget which does relatively frequent updates when the device is awake and I was seeing thousands of crashes in just a few days.

The issue trigger

I even noticed the issue even on my Pixel 3 XL when I wouldn't have thought the device to have much load at all. And any and all code paths were covered with startForeground(). But then I realized that in many cases my service gets the job done really quickly. I believe the trigger for my app was that the service was finishing before the system actually got around to showing a notification.

The workaround/solution

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();
}
Handbreadth answered 20/11, 2018 at 21:44 Comment(3)
But, according to docs, stopForeground ...does not stop the service from running (for that you use stopSelf() or related methods)Robbert
@DimaRostopira Yes, in practice I see the system taking care of stopping the service from running after being inactive for a while (maybe a minute). It isn't ideal, but it stops the system from complaining.Handbreadth
I had also the issue that foreground notification was not removed, when my service was very short running. Adding 100ms delay before the call to stopForeground(true); stopSelf(); has helped. Your solution did not help. (Pixel 3, Android 11)Inseverable
I
16

I have a work around for this problem. I have verified this fix in my own app(300K+ DAU), which can reduce at least 95% of this kind of crash, but still cannot 100% avoid this problem.

This problem happens even when you ensure to call startForeground() just after service started as Google documented. It may be because the service creation and initialization process already cost more than 5 seconds in many scenarios, then no matter when and where you call startForeground() method, this crash is unavoidable.

My solution is to ensure that startForeground() will be executed within 5 seconds after startForegroundService() method, no matter how long your service need to be created and initialized. Here is the detailed solution.

  1. Do not use startForegroundService at the first place, use bindService() with auto_create flag. It will wait for the service initialization. Here is the code, my sample service is MusicService:

    final Context applicationContext = context.getApplicationContext();
    Intent intent = new Intent(context, MusicService.class);
    applicationContext.bindService(intent, new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder binder) {
            if (binder instanceof MusicBinder) {
                MusicBinder musicBinder = (MusicBinder) binder;
                MusicService service = musicBinder.getService();
                if (service != null) {
                    // start a command such as music play or pause.
                    service.startCommand(command);
                    // force the service to run in foreground here.
                    // the service is already initialized when bind and auto_create.
                    service.forceForeground();
                }
            }
            applicationContext.unbindService(this);
        }
    
        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
    }, Context.BIND_AUTO_CREATE);
    
  2. Then here is MusicBinder implementation:

    /**
     * Use weak reference to avoid binder service leak.
     */
     public class MusicBinder extends Binder {
    
         private WeakReference<MusicService> weakService;
    
         /**
          * Inject service instance to weak reference.
          */
         public void onBind(MusicService service) {
             this.weakService = new WeakReference<>(service);
         }
    
         public MusicService getService() {
             return weakService == null ? null : weakService.get();
         }
     }
    
  3. The most important part, MusicService implementation, forceForeground() method will ensure that startForeground() method is called just after service start:

    public class MusicService extends MediaBrowserServiceCompat {
    ...
        private final MusicBinder musicBind = new MusicBinder();
    ...
        @Override
        public IBinder onBind(Intent intent) {
            musicBind.onBind(this);
            return musicBind;
        }
    ...
        public void forceForeground() {
            // API lower than 26 do not need this work around.
            if (Build.VERSION.SDK_INT >= 26) {
                Notification notification = mNotificationHandler.createNotification(this);
                // call startForeground just after service start.
                startForeground(Constants.NOTIFICATION_ID, notification);
            }
        }
    }
    
  4. If you want to run the step 1 code snippet in a pending intent, such as if you want to start a foreground service in a widget (a click on widget button) without opening your app, you can wrap the code snippet in a broadcast receiver, and fire a broadcast event instead of start service command.

That is all. Hope it helps. Good luck.

Inexperienced answered 13/11, 2018 at 17:9 Comment(4)
this is still didn't help, I have 80K active users (200K downloads)Thaumatology
@Thaumatology As I mentioned, it can reduce at least 95% of this kind of crash, but still cannot 100% avoid this problem. I did not find a way to solve this exception 100%. Sorry.Inexperienced
How is this a solution if Context.startForeground() is necessary to start a service from the app's in background state?Inseverable
Although this solution is recommended by @Shane in his comment #55895136, it doesn't work perfectly. After calling startForeground() unbindService(), the service is sometimes destroyed by system, and immediately started again. When the service initializes a worker thread during startup, we get an Error java.lang.InternalError: Thread starting during runtime shutdown :-(Alterant
L
15

Just a heads up as I wasted way too many hours on this. I kept getting this exception even though I was calling startForeground(..) as the first thing in onCreate(..). In the end I found that the problem was caused by using NOTIFICATION_ID = 0. Using any other value seems to fix this.

Ligetti answered 16/5, 2018 at 20:1 Comment(4)
In my case I was using ID 1, thanks, issue got resolvedReceptive
I am using ID 101, still I receive this error sometimes.Sememe
@RavindraKushwaha Not really.Sememe
I have a random number for notificationId and I still get this. I get it more on android 13 than any other versionGarrison
I
12

You have to add a permission as bellow for android 9 device when use target sdk 28 or later or the exception will always happen:

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
Instructive answered 11/3, 2020 at 17:28 Comment(2)
My app has this permission yet is still experiencing the issue described in the question.Feria
@Feria it seems only an activity can startForgroundService, try to startService in background instead.Instructive
M
11

So many answers but none worked in my case. I have started a service like this.

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

And in my service in onStartCommand

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
   Notification.Builder builder = new Notification.Builder(this, ANDROID_CHANNEL_ID)
      .setContentTitle(getString(R.string.app_name))
      .setContentText("SmartTracker Running")
      .setAutoCancel(true);
   Notification notification = builder.build();
   startForeground(NOTIFICATION_ID, notification);
} else {
   NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
      .setContentTitle(getString(R.string.app_name))
      .setContentText("SmartTracker is Running...")
      .setPriority(NotificationCompat.PRIORITY_DEFAULT)
      .setAutoCancel(true);
   Notification notification = builder.build();
   startForeground(NOTIFICATION_ID, notification);
}

And don't forgot to set NOTIFICATION_ID non zero

private static final String ANDROID_CHANNEL_ID = "com.xxxx.Location.Channel";
private static final int NOTIFICATION_ID = 555;

So everything was perfect but still crashing on 8.1 so cause was as below.

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
   stopForeground(true);
} else {
   stopForeground(true);
}

I have called stop foreground with remove notification but once the notification is removed service becomes background and background service can not run in android O from the background. started after the push was received.

So magical word is

stopSelf();

So far so any reason, your service is crashing follow all the above steps and enjoy.

Microbiology answered 11/6, 2018 at 18:53 Comment(5)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { stopForeground(true); } else { stopForeground(true); } what is this if else for? both case you were doing the same, stopForeground(true);Moody
I believe you meant to put stopSelf(); inside of the else block instead of stopForeground(true);Gild
Yes, @JustinStanley use stopSelf();Microbiology
Shouldn't startForeground be called in onCreate?Anastrophe
I m using NotificationManager and not Notification class. How can i implement this?Likeminded
B
10

This error also occurs on Android 8+ when Service.startForeground(int id, Notification notification) is called while id is set to 0.

id int: The identifier for this notification as per NotificationManager.notify(int, Notification); must not be 0.

Bonbon answered 1/6, 2018 at 7:51 Comment(1)
Don't use higher numbers for the notification ID also. Try to use one digit IDs. https://mcmap.net/q/82870/-startforeground-does-not-show-my-notificationWilscam
A
9

Please don't call any StartForgroundServices inside onCreate() method, you have to call StartForground services in onStartCommand() after make the worker thread otherwise you will get ANR always , so please don't write complex login in main thread of onStartCommand();

public class Services extends Service {

    private static final String ANDROID_CHANNEL_ID = "com.xxxx.Location.Channel";
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            Notification.Builder builder = new Notification.Builder(this, ANDROID_CHANNEL_ID)
                    .setContentTitle(getString(R.string.app_name))
                    .setContentText("SmartTracker Running")
                    .setAutoCancel(true);
            Notification notification = builder.build();
            startForeground(1, notification);
            Log.e("home_button","home button");
        } else {
            NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
                    .setContentTitle(getString(R.string.app_name))
                    .setContentText("SmartTracker is Running...")
                    .setPriority(NotificationCompat.PRIORITY_DEFAULT)
                    .setAutoCancel(true);
            Notification notification = builder.build();
            startForeground(1, notification);
            Log.e("home_button_value","home_button_value");

        }
        return super.onStartCommand(intent, flags, startId);

    }
}

EDIT: Caution! startForeground function can't take 0 as first argument, it will raise an exception! this example contains wrong function call, change 0 to your own const which couldnt be 0 or be greater than Max(Int32)

Abramabramo answered 31/12, 2018 at 11:41 Comment(0)
M
8

Around 10 users is getting this error in crashlytics for our application.

enter image description here

As Kimi Chiu replied- The main cause of this problem is the service was stopped before it was promoted to the foreground. But the assertion didn't stop after the service get destroyed. You can try to reproduce this by adding StopService after calling startForegroundService-Kimi Chiu

So I tested this and was able to reproduce.

One solution I applied is, I am letting the service to stay for at least 5 seconds so that the service will promote to the foreground. And now I cannot reproduce the issue while testing.

private fun stopService() {

        lifecycleScope.launch {
            delay(5000L)
            
            try {
                stopForeground(true)
                isForeGroundService = false
                stopSelf()
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }
    }

Lets see if the issue is reproduce in our next build.

Update :)-> This time there was no issue related to Context.startForegroundService() did not then call Service.startForeground()

enter image description here

Before/After comparission->

Before->enter image description here enter image description here

After-> enter image description here

Marpet answered 25/6, 2022 at 13:19 Comment(2)
Nice solution. How freaking dumb that we have to do this and Google/AOSP cannot do something better for us.Cosmo
@Kunal Kalwar can you provide full example.Largely
A
7

I've been researching this issue and this is what I've discovered so far. This crash could happen if we have code similar to this:

MyForegroundService.java

public class MyForegroundService extends Service {
    @Override
    public void onCreate() {
        super.onCreate();
        startForeground(...);
    }
}

MainActivity.java

Intent serviceIntent = new Intent(this, MyForegroundService.class);
startForegroundService(serviceIntent);
...
stopService(serviceIntent);

The exception is thrown in the following block of the code:

ActiveServices.java

private final void bringDownServiceLocked(ServiceRecord r) {
    ...
    if (r.fgRequired) {
        Slog.w(TAG_SERVICE, "Bringing down service while still waiting for start foreground: "
                  + r);
        r.fgRequired = false;
        r.fgWaiting = false;
        mAm.mAppOpsService.finishOperation(AppOpsManager.getToken(mAm.mAppOpsService),
                    AppOpsManager.OP_START_FOREGROUND, r.appInfo.uid, r.packageName);
        mAm.mHandler.removeMessages(
                    ActivityManagerService.SERVICE_FOREGROUND_TIMEOUT_MSG, r);
        if (r.app != null) {
            Message msg = mAm.mHandler.obtainMessage(
                ActivityManagerService.SERVICE_FOREGROUND_CRASH_MSG);
            msg.obj = r.app;
            msg.getData().putCharSequence(
                ActivityManagerService.SERVICE_RECORD_KEY, r.toString());
            mAm.mHandler.sendMessage(msg);
         }
    }
    ...
}

This method is executed before onCreate() of MyForegroundService because Android schedules the creation of the service on the main thread handler but bringDownServiceLocked is called on a BinderThread, wich is a race condition. It means that MyForegroundService didn't have a chance to call startForeground which will cause the crash.

To fix this we have to make sure that bringDownServiceLocked is not called before onCreate() of MyForegroundService.

public class MyForegroundService extends Service {

    private static final String ACTION_STOP = "com.example.MyForegroundService.ACTION_STOP";

    private final BroadcastReceiver stopReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            context.removeStickyBroadcast(intent);
            stopForeground(true);
            stopSelf();
        }
    };

    @Override
    public void onCreate() {
        super.onCreate();
        startForeground(...);
        registerReceiver(
            stopReceiver, new IntentFilter(ACTION_STOP));
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        unregisterReceiver(stopReceiver);
    }

    public static void stop(Context context) {
        context.sendStickyBroadcast(new Intent(ACTION_STOP));
    }
}

By using sticky broadcasts we make sure that the broadcast doesn't get lost and stopReceiver receives the stop intent as soon as it has been registered in onCreate() of MyForegroundService. By this time we have already called startForeground(...). We also have to remove that sticky broadcast to prevent stopReceiver being notified next time.

Please note that the method sendStickyBroadcast is deprecated and I use it only as a temporary workaround to fix this issue.

Aleida answered 22/11, 2018 at 10:30 Comment(1)
You should call it instead of context.stopService(serviceIntent) when you want to stop the service.Aleida
R
5

Even after calling the startForeground in Service, It crashes on some devices if we call stopService just before onCreate is called. So, I fixed this issue by Starting the service with an additional flag:

Intent intent = new Intent(context, YourService.class);
intent.putExtra("request_stop", true);
context.startService(intent);

and added a check in onStartCommand to see if it was started to stop:

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    //call startForeground first
    if (intent != null) {
        boolean stopService = intent.getBooleanExtra("request_stop", false);
        if (stopService) {
            stopSelf();
        }
    }

    //Continue with the background task
    return START_STICKY;
}

P.S. If the service were not running, it would start the service first, which is an overhead.

Reassure answered 22/6, 2018 at 7:21 Comment(7)
you don't need to create a service to stop it, just use local broadcast manager ))Thaumatology
Its not a different Service. Its the same service which I want to stop!Reassure
I didn't say anything about different service, I mean why are you trying to stop while it's being started?Thaumatology
use broadcast locale messagesThaumatology
Dear user924, If I have to stop the service, There is no need to BroadCast too, I can call context.stopService() directly. Above answer is a trick to solve the issue happening on Samsung devices in case you stop the service just before its onStartCommand is actually called.Reassure
onCreate is called before onStartCommand i thought. are you not doing otherway around here?Uncaused
@Uncaused You are right. Idea to let the onCreate complete and stop the service from onStartCommand. So that OS thinks onCreate was called, Notification was shown, finally stopped and won't throw the exception.Reassure
C
5

From Google's docs on Android 12 behavior changes:

To provide a streamlined experience for short-running foreground services on Android 12, the system can delay the display of foreground service notifications by 10 seconds for certain foreground services. This change gives short-lived tasks a chance to complete before their notifications appear.

Solution: Call startForeground() in onCreate() for the Service which you use Context.startForegroundService()

Combustion answered 14/3, 2021 at 19:8 Comment(1)
the question is too old for Android 12Thaumatology
S
4

https://developer.android.com/reference/android/content/Context.html#startForegroundService(android.content.Intent)

Similar to startService(Intent), but with an implicit promise that the Service will call startForeground(int, android.app.Notification) once it begins running. The service is given an amount of time comparable to the ANR interval to do this, otherwise the system will automatically stop the service and declare the app ANR.

Unlike the ordinary startService(Intent), this method can be used at any time, regardless of whether the app hosting the service is in a foreground state.

make sure you call the Service.startForeground(int, android.app.Notification) on the onCreate() so you ensure it will be called..if you have any condition that may prevent you from doing that, then you'd better off using the normal Context.startService(Intent) and call the Service.startForeground(int, android.app.Notification) yourself.

It seems that the Context.startForegroundService() adds a watchdog to make sure you called the Service.startForeground(int, android.app.Notification) before it was destroyed...

Seniority answered 12/1, 2018 at 10:35 Comment(0)
T
4

I am facing same issue and after spending time found a solutons you can try below code. If your using Service then put this code in onCreate else your using Intent Service then put this code in onHandleIntent.

if (Build.VERSION.SDK_INT >= 26) {
        String CHANNEL_ID = "my_app";
        NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
                "MyApp", NotificationManager.IMPORTANCE_DEFAULT);
        ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);
        Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
                .setContentTitle("")
                .setContentText("").build();
        startForeground(1, notification);
    }
Tabithatablature answered 9/4, 2018 at 7:20 Comment(0)
S
4

Problem With Android O API 26

If you stop the service right away (so your service does not actually really runs (wording / comprehension) and you are way under the ANR interval, you still need to call startForeground before stopSelf

https://plus.google.com/116630648530850689477/posts/L2rn4T6SAJ5

Tried this Approach But it Still creates an error:-

if (Util.SDK_INT > 26) {
    mContext.startForegroundService(playIntent);
} else {
    mContext.startService(playIntent);
}

I Am Using this until the Error is Resolved

mContext.startService(playIntent);
Surfboard answered 27/6, 2018 at 9:35 Comment(2)
Should be Util.SDK_INT >= 26, not just biggerEllord
do you mean that startService will still work for all SDK versions?Uncaused
S
2

Updating Data in onStartCommand(...)

onBind(...)

onBind(...) is a better lifecycle event to initiate startForeground vs. onCreate(...) because onBind(...) passes in an Intent which may contain important data in the Bundle needed to initialize the Service. However, it is not necessary as onStartCommand(...) is called when the Service is created for the first time or called subsequent times after.

onStartCommand(...)

startForeground in onStartCommand(...) is important in order to update the Service once it has already been created.

When ContextCompat.startForegroundService(...) is called after a Service has been created onBind(...) and onCreate(...) are not called. Therefore, updated data can be passed into onStartCommand(...) via the Intent Bundle to update data in the Service.

Sample

I'm using this pattern to implement the PlayerNotificationManager in the Coinverse cryptocurrency news app.

Activity / Fragment.kt

context?.bindService(
        Intent(context, AudioService::class.java),
        serviceConnection, Context.BIND_AUTO_CREATE)
ContextCompat.startForegroundService(
        context!!,
        Intent(context, AudioService::class.java).apply {
            action = CONTENT_SELECTED_ACTION
            putExtra(CONTENT_SELECTED_KEY, contentToPlay.content.apply {
                audioUrl = uri.toString()
            })
        })

AudioService.kt

private var uri: Uri = Uri.parse("")

override fun onBind(intent: Intent?) =
        AudioServiceBinder().apply {
            player = ExoPlayerFactory.newSimpleInstance(
                    applicationContext,
                    AudioOnlyRenderersFactory(applicationContext),
                    DefaultTrackSelector())
        }

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
    intent?.let {
        when (intent.action) {
            CONTENT_SELECTED_ACTION -> it.getParcelableExtra<Content>(CONTENT_SELECTED_KEY).also { content ->
                val intentUri = Uri.parse(content.audioUrl)
                // Checks whether to update Uri passed in Intent Bundle.
                if (!intentUri.equals(uri)) {
                    uri = intentUri
                    player?.prepare(ProgressiveMediaSource.Factory(
                            DefaultDataSourceFactory(
                                    this,
                                    Util.getUserAgent(this, getString(app_name))))
                            .createMediaSource(uri))
                    player?.playWhenReady = true
                    // Calling 'startForeground' in 'buildNotification(...)'.          
                    buildNotification(intent.getParcelableExtra(CONTENT_SELECTED_KEY))
                }
            }
        }
    }
    return super.onStartCommand(intent, flags, startId)
}

// Calling 'startForeground' in 'onNotificationStarted(...)'.
private fun buildNotification(content: Content): Unit? {
    playerNotificationManager = PlayerNotificationManager.createWithNotificationChannel(
            this,
            content.title,
            app_name,
            if (!content.audioUrl.isNullOrEmpty()) 1 else -1,
            object : PlayerNotificationManager.MediaDescriptionAdapter {
                override fun createCurrentContentIntent(player: Player?) = ...
                override fun getCurrentContentText(player: Player?) = ...
                override fun getCurrentContentTitle(player: Player?) = ...
                override fun getCurrentLargeIcon(player: Player?,
                                                 callback: PlayerNotificationManager.BitmapCallback?) = ...
            },
            object : PlayerNotificationManager.NotificationListener {
                override fun onNotificationStarted(notificationId: Int, notification: Notification) {
                    startForeground(notificationId, notification)
                }
                override fun onNotificationCancelled(notificationId: Int) {
                    stopForeground(true)
                    stopSelf()
                }
            })
    return playerNotificationManager.setPlayer(player)
}
Schutt answered 8/6, 2019 at 22:54 Comment(0)
R
2

Ok, something I noticed on this that might help a few others too. This is strictly from testing to see if I could figure out how to fix the occurrences I am seeing. For simplicity sake, let's say I have a method that calls this from the presenter.

context.startForegroundService(new Intent(context, TaskQueueExecutorService.class));

try {
    Thread.sleep(10000);
} catch (InterruptedException e) {
    e.printStackTrace();
}       

This will crash with the same error. The Service will NOT start until the method is complete, therefore no onCreate() in the service.

So even if you update the UI off the main thread, IF you have anything that might hold up that method after it, it won't start on time and give you the dreaded Foreground Error. In my case we were loading some things onto a queue and each called startForegroundService, but some logic was involved with each in the background. So if the logic took too long to finish that method since they were called back to back, crash time. The old startService just ignored it and went on it's way and since we called it each time, the next round would finish up.

This left me wondering, if I called the service from a thread in the background, could it not be fully bound on the start and run immediately, so I started experimenting. Even though this does NOT start it immediately, it does not crash.

new Handler(Looper.getMainLooper()).post(new Runnable() {
        public void run() {
               context.startForegroundService(new Intent(context, 
           TaskQueueExecutorService.class));
               try {
                   Thread.sleep(10000);
               } catch (InterruptedException e) {
                  e.printStackTrace();
              }       
        }
});

I will not pretend to know why it does not crash although I suspect this forces it to wait until the main thread can handle it in a timely fashion. I know it's not ideal to tie it to the main thread, but since my usage calls it in the background, I'm not real concerned if it waits until it can complete rather than crash.

Rosefish answered 24/6, 2019 at 13:41 Comment(2)
you are starting your service from background or in activity?Shing
Background. The problem is that Android can't guarantee the service will start in the allotted time. One would think they would just throw an error to ignore it if you want, but alas, not Android. It has to be fatal. This helped in our app, but didn't fix it totally.Rosefish
J
2

I am adding some code in @humazed answer. So there in no initial notification. It might be a workaround but it works for me.

@Override
 public void onCreate() {
        super.onCreate();

        if (Build.VERSION.SDK_INT >= 26) {
            String CHANNEL_ID = "my_channel_01";
            NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
                    "Channel human readable title",
                    NotificationManager.IMPORTANCE_DEFAULT);

            ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);

            Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
                    .setContentTitle("")
                    .setContentText("")
                    .setColor(ContextCompat.getColor(this, R.color.transparentColor))
                    .setSmallIcon(ContextCompat.getColor(this, R.color.transparentColor)).build();

            startForeground(1, notification);
        }
}

I am adding transparentColor in small icon and color on notification. It will work.

Justiciable answered 4/8, 2019 at 19:16 Comment(0)
B
2

One issue might be Service class is not enabled in AndroidManifest file. Please check it as well.

<service
        android:name=".AudioRecorderService"
        android:enabled="true"
        android:exported="false"
        android:foregroundServiceType="microphone" />
Bernina answered 18/9, 2020 at 12:6 Comment(1)
this is what I needed inside Manifest to be able to continue Play Audio <service android:name=".common.audio.AudioPlayerService" android:foregroundServiceType="mediaPlayback" ....Haha
I
2

I had an issue in Pixel 3, Android 11 that when my service was running very short, then the foreground notification was not dismissed.

Adding 100ms delay before stopForeground() stopSelf() seems to help.

People write here that stopForeground() should be called before stopSelf(). I cannot confirm, but I guess it doesn't bother to do that.

public class AService extends Service {

@Override
public void onCreate() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        startForeground(
            getForegroundNotificationId(),
            channelManager.buildBackgroundInfoNotification(getNotificationTitle(), getNotificationText()),
            ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC);
    } else {
        startForeground(getForegroundNotificationId(),
            channelManager.buildBackgroundInfoNotification(getNotificationTitle(), getNotificationText())
        );
    }

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    startForeground();

    if (hasQueueMoreItems()) {
        startWorkerThreads();
    } else {
        stopForeground(true);
        stopSelf();
    }
    return START_STICKY;
}

private class WorkerRunnable implements Runnable {

    @Override
    public void run() {

        while (getItem() != null && !isLoopInterrupted) {
            doSomething(getItem())   
        }

        waitALittle();
        stopForeground(true);
        stopSelf();
    }

    private void waitALittle() {
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
}
Inseverable answered 8/10, 2020 at 12:6 Comment(0)
L
1

I just check the PendingIntent null or nor not before calling the context.startForegroundService(service_intent) function.

this works for me

PendingIntent pendingIntent=PendingIntent.getBroadcast(context,0,intent,PendingIntent.FLAG_NO_CREATE);

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && pendingIntent==null){
            context.startForegroundService(service_intent);
        }
        else
        {
            context.startService(service_intent);
        }
}
Lucid answered 6/11, 2018 at 4:14 Comment(1)
This actually just kicks the service to startService rather than startForegroundService if null. The if statement would have to be nested, otherwise the app would crash trying to use services on a later version at some point.Rosefish
Y
1

just call startForeground method immediately after Service or IntentService is Created. like this:

import android.app.Notification;
public class AuthenticationService extends Service {

    @Override
    public void onCreate() {
        super.onCreate();
        startForeground(1,new Notification());
    }
}
Yolande answered 23/2, 2019 at 10:39 Comment(1)
FATAL EXCEPTION: main android.app.RemoteServiceException: Bad notification for startForeground: java.lang.RuntimeException: invalid channel for service notification: Notification(channel=null pri=0 contentView=null vibrate=null sound=null defaults=0x0 flags=0x40 color=0x00000000 vis=PRIVATE) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1821) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loop(Looper.java:164) at android.app.ActivityThread.main(ActivityThread.java:6626)Titos
K
0

I have fixed the problem with starting the service with startService(intent) instead of Context.startForeground() and calling startForegound() immediately after super.OnCreate(). Additionally, if you starting service on boot, you can start Activity that starts service on the boot broadcast. Although it is not a permanent solution, it works.

Kisangani answered 7/1, 2019 at 13:6 Comment(1)
in some Xiaomi devices activities can't be started from the backgroundShing
T
0

I just sharing my review about this. I am not surely(100% telling) that above code is not working for me and other guys also but some times I got this issue. Suppose I run the app 10 time then might be got this issue 2 to 3 three time.

I have tried above all the answers but still not solve the issue. I have implemented above all the codes and tested in different api levels (API level 26, 28, 29) and difference mobile (Samsung, Xiaomi, MIUI, Vivo, Moto, One Plus, Huawei, etc ) and getting same below issue.

Context.startForegroundService() did not then call Service.startForeground();

I have read service on google developer web site, some other blog and some stack overflow question and got the idea that this issue will happen when we call startForgroundSerivce() method but at that time service was not started.

In my case I have stop the service and after immediately start service. Below is the hint.

....//some other code
...// API level and other device auto star service condition is already set
stopService();
startService();
.....//some other code

In this case service is not started due to processing speed and low memory in RAM but startForegroundService() method is called and fire the exception.

Work for me:

new Handler().postDelayed(()->ContextCompat.startForegroundService(activity, new Intent(activity, ChatService.class)), 500);

I have change code and set 500 milliseconds delay to call startService() method and issue is solved. This is not perfect solution because this way app's performance goes downgrade.

Note: This is only for Foreground and Background service only. Don't tested when using Bind service. I am sharing this because only this is the way I have solved this issue.

Tolbooth answered 18/8, 2020 at 14:22 Comment(1)
Tried this on my app (200k MAU) and still get this error. Mostly Samsung it seems for some reason (maybe statistical, but idk).Footrace
A
0

In my case, I was calling context.stopService(outside service) before the service would have the chance to call startForeground internally.

Albumin answered 27/7, 2022 at 14:19 Comment(0)
I
0

My 10+ year old app randomly started giving this error. I searched for hours and then realized that I was launching my app from Android Studio while the Layout Inspector was still on and running.

As soon as I turned it off everything was back to normal.

Hopefully this helps someone.

Indamine answered 25/9, 2023 at 21:31 Comment(0)
D
0

This bug is so annoying. In my case, the problem was that I started the service and immediately stopped it before the OS could show a notification to the user. By avoiding immediate service stop, you could resolve this issue. Also, you can check this by overriding the onDestroy method of your service.

Deipnosophist answered 22/1, 2024 at 23:48 Comment(0)
P
0

If you already called startForeground() in onCreate() for the service which you use Context.startForegroundService() but the issue persists. You can try this solution:

  • Replace the startForegroundService() with startService().
  • Call the startForeground() in onCreate for the service as usual.

The solution has been tested in multiple projects I'm responsible for and it works well. It takes me so many time to find it so I want to post it here and hope it also works for you.

The trick here is the service will be alive as a foreground service after you called startForeground() in the onCreate method of the service, regardless of whether you started it with startForegroundService() or startService(). But if you started it with startService(), the timer that listens for the startForeground() call will not be started.

Preoccupancy answered 14/3, 2024 at 9:51 Comment(0)
W
-1

Service

class TestService : Service() {

    override fun onCreate() {
        super.onCreate()
        Log.d(TAG, "onCreate")

        val nBuilder = NotificationCompat.Builder(this, "all")
            .setSmallIcon(R.drawable.ic_launcher_foreground)
            .setContentTitle("TestService")
            .setPriority(NotificationCompat.PRIORITY_DEFAULT)
        startForeground(1337, nBuilder.build())
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        val rtn = super.onStartCommand(intent, flags, startId)

        if (intent?.action == STOP_ACTION) {
            Log.d(TAG, "onStartCommand -> STOP")
            stopForeground(true)
            stopSelf()
        } else {
            Log.d(TAG, "onStartCommand -> START")
        }

        return rtn
    }

    override fun onDestroy() {
        Log.d(TAG, "onDestroy")
        super.onDestroy()
    }

    override fun onBind(intent: Intent?): IBinder? = null

    companion object {

        private val TAG = "TestService"
        private val STOP_ACTION = "ly.zen.test.TestService.ACTION_STOP"

        fun start(context: Context) {
            ContextCompat.startForegroundService(context, Intent(context, TestService::class.java))
        }

        fun stop(context: Context) {
            val intent = Intent(context, TestService::class.java)
            intent.action = STOP_ACTION
            ContextCompat.startForegroundService(context, intent)
        }

    }

}

Tester

val nChannel = NotificationChannel("all", "All", NotificationManager.IMPORTANCE_NONE)
val nManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
nManager.createNotificationChannel(nChannel)

start_test_service.setOnClickListener {
    TestService.start(this@MainActivity)
    TestService.stop(this@MainActivity)
}

Result

D/TestService: onCreate
D/TestService: onStartCommand -> START
D/TestService: onStartCommand -> STOP
D/TestService: onDestroy
Winfield answered 12/3, 2019 at 10:30 Comment(0)
M
-4

In my case the notification ID I was passing to the startForeground method was '0' because of which this error was coming.

startForeground(0, notification); //This is wrong.

startForeground(1, notification); //This is right.

You can use any integer other than 0.

Miniver answered 8/9, 2021 at 11:45 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.