383

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?

MathMax
  • 571
  • 7
  • 22
NiceGuy
  • 3,866
  • 2
  • 9
  • 10
  • IOW, please provide a [mcve]. That would include the entire Java stack trace and the code that is triggering the crash. – CommonsWare Jun 08 '17 at 10:55
  • 6
    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. – Alex Apr 29 '18 at 08:35
  • 3
    FYI https://issuetracker.google.com/issues/76112072 – v1k Jun 21 '18 at 05:12
  • we can check response from google team here https://issuetracker.google.com/issues/76112072#comment56 and https://issuetracker.google.com/issues/76112072#comment36 – Prags Apr 02 '19 at 09:09
  • 2
    I have the same problem. I fix this issue. I shared my implementation in this topic https://stackoverflow.com/questions/55894636/android-9-pie-context-startforegroundservice-did-not-then-call-service-star/56338570#56338570 – Beyazid May 28 '19 at 10:20
  • Check out here! [Context.startForegroundService() did not then call Service.startForeground()](https://stackoverflow.com/a/58528446/9636618) – Sepehr Oct 24 '19 at 08:52
  • The main reason this occurs is because there is an exception before you call startForeground(), check your code again – Duna Sep 02 '20 at 06:57
  • Moreover if the service get killed by the system onCreate will never called if started in onDestroy by itself. Move startForeground to onStartCommand – Duna Sep 02 '20 at 07:44
  • 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) – thilina Kj Mar 02 '21 at 10:21
  • 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. – sapeg Nov 10 '21 at 15:43
  • I finally found the solution and its working for all the devices https://stackoverflow.com/a/72754189/12228079 – Kunal Kalwar Jul 01 '22 at 11:53

34 Answers34

147

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)

CopsOnRoad
  • 237,138
  • 77
  • 654
  • 440
zhuzhumouse
  • 1,505
  • 1
  • 8
  • 4
  • 35
    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. – Kimi Chiu Oct 03 '17 at 15:04
  • 45
    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. – Kurovsky Mar 20 '18 at 15:48
  • I called Context.startForegroundService() method in broadcast receiver. so how to handle in this situation? because onCreate() is not available in broadcast receiver – Anand Savjani Jul 12 '18 at 14:49
  • If I call startForeground in onCreate(), then even startService() will start it in foreground. what's the purpose of startForegroundService()? – M. Usman Khan Jan 06 '19 at 05:19
  • 17
    @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` ... – android developer Mar 31 '19 at 12:00
  • 1
    We can check response from google team here https://issuetracker.google.com/issues/76112072#comment56 – Prags Apr 02 '19 at 09:08
  • 10
    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. – Duna Nov 02 '20 at 14:47
  • 1
    Some phone like VIVO, you will still crash even if you are call stopService() after startForeground(), on Android 8.1. see: [my logcat](https://tva1.sinaimg.cn/large/008eGmZEly1gpa6uh2nv3j30ix02n74w.jpg) – thirtyyuan Apr 06 '21 at 10:14
  • 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 users – user924 May 06 '21 at 18:44
  • Guys see my answer its working for me https://stackoverflow.com/a/72754189/12228079 – Kunal Kalwar Jun 30 '22 at 07:07
125

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);
        }
}
humazed
  • 74,687
  • 32
  • 99
  • 138
  • 75
    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. – Kimi Chiu Oct 18 '17 at 06:08
  • 6
    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). – Tyler Dec 13 '17 at 19:06
  • 4
    This creates a local notification in Oreo saying "APP_NAME is running. Tap to close or see info". How to stop showing that notification? – Artist404 Dec 14 '17 at 12:20
  • you can't if you want to run Foreground service this is a new restriction introduced in android o. – humazed Dec 14 '17 at 12:46
  • @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 helps – Artist404 Jan 12 '18 at 11:10
  • 6
    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. – Kimi Chiu Jan 12 '18 at 11:16
  • 24
    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 Jan 12 '18 at 11:20
  • Do we need to make these changes in case of JobIntentService? Or it is handling it internally? – Amrut Apr 05 '18 at 08:56
  • no, the job classes are created to handle the background work in android Oreo. so you don't need to show notification. – humazed Apr 05 '18 at 14:12
  • 2
    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. http://choruscode.blogspot.com/2018/05/handling-exceptions-thrown-from.html – Kurovsky May 05 '18 at 18:17
  • Don't forget to add `setSmallIcon` method to the chain of builder methods, otherwise the default notification "App is running" will be shown – Vasily Kabunov Oct 02 '18 at 04:36
  • 1
    That was a quick solution. Thanks ^^ – Atif Waqar Aug 25 '20 at 02:02
  • 1
    you are so smart!!! we all do it... but boy your users will still face crash from time to time – user924 May 06 '21 at 18:51
  • Guys see my answer its working for me https://stackoverflow.com/a/72754189/12228079 – Kunal Kalwar Jun 30 '22 at 07:08
84

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

Prags
  • 2,457
  • 2
  • 21
  • 38
ZhouX
  • 1,866
  • 18
  • 22
  • 18
    Why both onCreate and onStartCommand? Can you just put it in onStartCommand? – rcell Aug 06 '18 at 21:10
  • stopSelf must not be called before startForeground => or directly after, because it seems to cancel the "startForeground" when you do. – Frank Feb 04 '19 at 12:46
  • 4
    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. – Lxu May 23 '19 at 14:08
  • 2
    I'm using 0 as the notification ID of the initial call for `startForeground`. Changing it to 1 fixes the issue for me (hopefully) – mr5 Jan 09 '20 at 07:05
  • So what is the solution? – IgorGanapolsky Apr 10 '20 at 19:02
  • 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. – Malachiasz Oct 08 '20 at 11:27
  • 4
    `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... – user924 May 06 '21 at 18:40
  • @user924 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. – Beloo Oct 28 '21 at 15:32
  • 1
    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. – DIRTY DAVE Nov 10 '21 at 10:12
  • `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? – Hzzkygcs Jan 29 '22 at 19:39
  • Guys see my answer its working for me https://stackoverflow.com/a/72754189/12228079 – Kunal Kalwar Jun 30 '22 at 07:08
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.

Oleg Gryb
  • 5,122
  • 1
  • 28
  • 40
  • 4
    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 info](https://stackoverflow.com/questions/53248528/intentservice-startforeground-vs-jobintentservice) – CopsOnRoad Oct 05 '19 at 08:23
  • @CopsOnRoad 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. – Oleg Gryb Oct 05 '19 at 11:16
  • 1
    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. – CopsOnRoad Oct 05 '19 at 11:31
  • 1
    @CopsOnRoad 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. – Oleg Gryb Oct 05 '19 at 11:49
  • 1
    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`. – CopsOnRoad Oct 05 '19 at 11:52
  • 2
    Probably I will when time permits: I'll need to de-couple it from custom-specific code and de-compile some proprietary Samsung libs – Oleg Gryb Oct 05 '19 at 12:03
  • No worries, take your time. Good day! – CopsOnRoad Oct 05 '19 at 12:05
  • 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. – Emil Jan 17 '20 at 00:07
  • @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. – Oleg Gryb Jan 17 '20 at 04:42
  • @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 foregroundservice – Emil Jan 18 '20 at 12:01
  • 1
    @batmaci - JobService is used by SAP internally. See Update 2 in OP for details. – Oleg Gryb Jan 18 '20 at 17:11
  • quick quesiton: SAAgentV2.requestAgent - where do you call this? within mainactivity oncreate? using SAAgent it wasnt required as it it was registered automatically. – Emil Jan 25 '20 at 15:47
  • @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. – Emil Jan 27 '20 at 09:24
  • 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. – fsljfke Aug 30 '20 at 20:36
  • @OlegGryb Does `JobService` work all the time?. For example will it work for background video recording with screen turned of for 24 hours? – user924 Jan 21 '21 at 09:58
  • 2
    So this is wrong to call `JobService` as good replacement, cause it can't run all the time, only sticky foreground service can do it – user924 Jan 21 '21 at 15:31
  • Guys see my answer its working for me https://stackoverflow.com/a/72754189/12228079 – Kunal Kalwar Jun 30 '22 at 07:09
48

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.

Prags
  • 2,457
  • 2
  • 21
  • 38
swooby
  • 3,005
  • 2
  • 36
  • 43
  • 7
    Best update I have seen is https://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 https://github.com/paulpv/ForegroundServiceAPI26/tree/bound/app/src/main/java/com/github/paulpv/foregroundserviceapi26 – swooby Jun 22 '18 at 21:37
  • 1
    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 ':) – Oleg Gryb Mar 30 '20 at 20:28
  • 1
    @swooby your solution helped me greatly. – lasec0203 Apr 08 '21 at 03:43
  • 1
    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% – user924 May 06 '21 at 20:06
  • 1
    Guys see my answer its working for me https://stackoverflow.com/a/72754189/12228079 – Kunal Kalwar Jun 30 '22 at 07:09
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.

Furkan Yurdakul
  • 2,801
  • 1
  • 15
  • 37
  • Thank you, just implemented your solution in an app with well over 5M installs, let's see. Hope for the best. – gregko Sep 04 '20 at 21:45
  • 5
    It works for me so far - almost a month later and no more crashes or ANRs related to startForegroundService() - Thanks! – gregko Oct 02 '20 at 00:23
  • a tricky solution, I will try it, have 80k active users in my app – user924 May 06 '21 at 18:33
  • but you have to unbind service in onStop of activity cycle – user924 May 06 '21 at 20:37
  • 1
    ` // 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 sentence – user924 May 06 '21 at 20:44
  • `myService.startForeground(myNotificationId, MyService.getNotification());` you can move it to `onCreate`, because `onServiceConnected` is called after `onCreate`!!! – user924 May 06 '21 at 20:53
  • this is still didn't help, I have 80K active users (200K downloads) – user924 May 09 '21 at 12:15
  • Fixed it for us, thanks. – ManxJason Feb 11 '22 at 11:31
  • Guys see my answer its working for me https://stackoverflow.com/a/72754189/12228079 – Kunal Kalwar Jun 30 '22 at 07:10
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
    }
}
General Grievance
  • 4,555
  • 31
  • 31
  • 45
Ahmad Arslan
  • 4,498
  • 8
  • 38
  • 59
  • 13
    Did you tried it in some app that has at least several thousands users? I tried, some users still have the crash issues. – Alex Apr 29 '18 at 08:37
  • No no I am using the exact code and I don't see the clients getting crash. – Ahmad Arslan Apr 30 '18 at 07:35
  • 1
    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. – Alex Apr 30 '18 at 09:11
  • only 2200 user :-/ but did not get any crash – Ahmad Arslan Apr 30 '18 at 10:08
  • @Alex strange I am getting the ANR on nexus and HTC devices :( do you have any solution ? – Ahmad Arslan May 30 '18 at 06:26
  • @Alex how did you fix this issue?? – Jeeva Jun 08 '18 at 06:08
  • I was using intent service right inside of this foreground service which creates error and sometimes crashes, I have removed it and worked perfectly well – Ahmad Arslan Jun 08 '18 at 09:34
  • 1
    @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 https://developer.android.com/topic/libraries/architecture/workmanager#java before August 2018 because https://android-developers.googleblog.com/2017/12/improving-app-security-and-performance.html – Alex Jun 13 '18 at 08:55
  • @Alex Thanks mate let me know if you have any better solutions – Jeeva Jun 13 '18 at 09:56
  • This causes a crash "Bad notification for startForeground: java.lang.RuntimeException: invalid channel for service notification". Also, Notification.Builder is deprecated as of API 26 – matdev May 24 '19 at 10:04
  • no need for version check manually, simply use `ContextCompat.startForegroundService()`, it will do that for you. – Darshan Jul 08 '19 at 08:02
  • why do make it foreground only for Oreo, nonsense – user924 May 06 '21 at 18:24
  • 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. – Vicente Domingos Nov 27 '21 at 20:46
  • 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. – Martin Zeitler Feb 28 '22 at 21:18
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();
}
Roy Solberg
  • 18,133
  • 12
  • 49
  • 76
  • But, according to docs, stopForeground `...does not stop the service from running (for that you use stopSelf() or related methods)` – Dmytro Rostopira Jul 08 '20 at 10:22
  • @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. – Roy Solberg Jul 08 '20 at 14:10
  • 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) – Malachiasz Oct 08 '20 at 11:58
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.

Hexise
  • 1,520
  • 15
  • 20
  • this is still didn't help, I have 80K active users (200K downloads) – user924 May 09 '21 at 12:14
  • @user924 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. – Hexise May 11 '21 at 19:47
  • How is this a solution if Context.startForeground() is necessary to start a service from the app's in background state? – Malachiasz Aug 24 '21 at 05:13
  • Although this solution is recommended by @CommonsWare in his comment https://stackoverflow.com/questions/55894636/android-9-pie-context-startforegroundservice-did-not-then-call-service-star#comment98445857_55894636, 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 :-( – zelig74 Sep 03 '21 at 14:06
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.

tobalr
  • 1,597
  • 15
  • 14
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" />
Shrdi
  • 359
  • 4
  • 13
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.

Aldan
  • 674
  • 9
  • 23
sandy
  • 3,311
  • 4
  • 36
  • 47
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.

almisoft
  • 2,153
  • 2
  • 25
  • 33
  • 3
    Don't use higher numbers for the notification ID also. Try to use one digit IDs. https://stackoverflow.com/a/12228555/2527204 – Marlon Oct 09 '18 at 09:57
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)

user924
  • 8,146
  • 7
  • 57
  • 139
Shalu Gupta
  • 145
  • 4
  • 11
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

Kunal Kalwar
  • 685
  • 6
  • 20
  • 4
    Nice solution. How freaking dumb that we have to do this and Google/AOSP cannot do something better for us. – swooby Aug 24 '22 at 01:55
6

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.

makovkastar
  • 5,000
  • 2
  • 30
  • 50
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.

Arsen Khachaturyan
  • 7,904
  • 4
  • 42
  • 42
Gaurav Singla
  • 1,405
  • 1
  • 17
  • 17
  • you don't need to create a service to stop it, just use local broadcast manager )) – user924 Oct 04 '20 at 11:42
  • Its not a different Service. Its the same service which I want to stop! – Gaurav Singla Oct 05 '20 at 06:49
  • I didn't say anything about different service, I mean why are you trying to stop while it's being started? – user924 Oct 05 '20 at 10:44
  • use broadcast locale messages – user924 Oct 05 '20 at 10:45
  • 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. – Gaurav Singla Oct 08 '20 at 08:22
  • onCreate is called before onStartCommand i thought. are you not doing otherway around here? – Emil May 29 '22 at 23:50
  • @Emil 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. – Gaurav Singla May 30 '22 at 12:04
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()

Duna
  • 1,564
  • 1
  • 16
  • 36
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...

Alécio Carvalho
  • 13,481
  • 5
  • 68
  • 74
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);
    }
Sambhaji Karad
  • 387
  • 7
  • 18
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);
Andy Cass
  • 398
  • 6
  • 16
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)
}
AdamHurwitz
  • 9,758
  • 10
  • 72
  • 134
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.

adiga
  • 34,372
  • 9
  • 61
  • 83
a54studio
  • 965
  • 11
  • 11
  • you are starting your service from background or in activity? – Mateen Chaudhry May 04 '20 at 09:41
  • 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. – a54studio May 04 '20 at 21:11
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.

touhid udoy
  • 4,005
  • 2
  • 18
  • 31
Khemraj
  • 109
  • 1
  • 9
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" />
Pankaj Kant Patel
  • 2,050
  • 21
  • 27
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();
        }
    }
}
}
Malachiasz
  • 7,126
  • 2
  • 35
  • 49
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);
        }
}
Farhana Naaz Ansari
  • 7,524
  • 26
  • 65
  • 105
SaimumIslam27
  • 971
  • 1
  • 8
  • 14
  • 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. – a54studio May 07 '20 at 20:20
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());
    }
}
Hossein Karami
  • 776
  • 10
  • 11
  • 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) – Gerry Mar 07 '19 at 18:07
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.

melianor
  • 170
  • 2
  • 10
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.

Farmer
  • 4,093
  • 3
  • 23
  • 47
  • 1
    Tried this on my app (200k MAU) and still get this error. Mostly Samsung it seems for some reason (maybe statistical, but idk). – MikkelT Feb 22 '21 at 13:29
0

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

Harshal Pudale
  • 178
  • 1
  • 2
  • 7
0

The message "Call startForeground() in onCreate() for the Service which you use Context.startForegroundService()" is a warning that you may see in your Android logcat when you start a foreground service using Context.startForegroundService(), but you don't call startForeground() in the onCreate() method of your service.

Starting from Android 8.0 (API level 26), foreground services are subject to new background execution limits, which means that a foreground service must display a notification to the user in order to run in the foreground for an extended period of time. If a foreground service does not call startForeground() within 5 seconds of being started, a ServiceConnectionLeaked exception will be thrown, and the service will be terminated.

The warning message is reminding you to call startForeground() in the onCreate() method of your service, before the 5-second time limit expires, to avoid this exception.

Here's an example of how to call startForeground() in the onCreate() method of your service:

public class MyForegroundService extends Service {
    private static final int NOTIFICATION_ID = 1;

    @Override
    public void onCreate() {
        super.onCreate();
        
        Notification notification = new NotificationCompat.Builder(this, "channel_id")
                .setContentTitle("My Foreground Service")
                .setContentText("Running...")
                .setSmallIcon(R.drawable.ic_notification)
                .build();

        startForeground(NOTIFICATION_ID, notification);
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // Do your background tasks here
        return START_STICKY;
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

In this example, we call startForeground() in the onCreate() method of our service, passing in a notification with a unique ID. This will ensure that our service can run in the foreground for an extended period of time, without being terminated by the system.

Note that you still need to call startForeground() again from the onStartCommand() method of your service, passing in the same notification, to keep the service running in the foreground after it has been started.

Mahmudul
  • 470
  • 1
  • 4
  • 12
  • This answer looks like it was generated by an AI (like ChatGPT), not by an actual human being. You should be aware that [posting AI-generated output is officially **BANNED** on Stack Overflow](https://meta.stackoverflow.com/q/421831). If this answer was indeed generated by an AI, then I strongly suggest you delete it before you get yourself into even bigger trouble: **WE TAKE PLAGIARISM SERIOUSLY HERE.** Please read: [Why posting GPT and ChatGPT generated answers is not currently allowed](https://stackoverflow.com/help/gpt-policy). – tchrist Jul 11 '23 at 13:45
-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
JeanK
  • 292
  • 2
  • 4
-3

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.