33

First of all, I looked at these;

enter image description here

I have a streaming application used by almost a million people. I'm using foreground service for the player. I didn't implement MediaSession yet. I have a 99.95% crash-free sessions. So this app works on all versions but I started getting crash reports(ANR) with android 9. This crash occurs only Samsung phones, especially s9, s9+, s10, s10+, note9 models.

I tried these,

  • Calling startForeground() method in onCreate()
  • Calling Service.startForeground() before Context.stopService()
  • Other stackoverflow answers to similar questions

I read some comments from developers of Google, they said it's just Intended Behavior. I wonder is it occurred by Samsung's system or Android OS. Does anyone have an opinion about this? How can I fix this?

Beyazid
  • 1,795
  • 1
  • 15
  • 28
  • 1
    Are there any paths where startForeground may not be called? Or paths where the main thread is stalled/sleeping/doing too much work and cannot process the start of the service? – Gabe Sechan Apr 28 '19 at 21:20
  • 1
    It's a radio/music application, it just plays the stream URL. I didn't get a crash on my phone. Some users said the crash occurs when listening to music in the background – Beyazid Apr 28 '19 at 21:26
  • 2
    I would not be surprised if there is a chance that this will occur with a perfectly-written app, as there is no guarantee that your code will get called in time. While the particular bits of code in [this answer](https://stackoverflow.com/a/53286232/115145) are a mess, the workaround would seem to have promise: get the service going via binding, move it to the foreground, *then* use `startForegroundService()`, and unbind sometime later. – CommonsWare Apr 28 '19 at 21:28
  • 1
    Right. You have a short window after starting a foreground service via startForegroundService to call startForeground I the service. For some reason, that isn't happening quickly enough on those devices – Gabe Sechan Apr 28 '19 at 21:29
  • 1
    If this is localised to only Samsung devices it would suggest they have a bug with their customised version(s) of AOSP for Android 9 - not with your code. Any workarounds would be pure guess work until this can be faithfully reproduced. – Mark Apr 28 '19 at 23:29
  • Thank you for your responses @CommonsWare, GabeSechan, Mark Keen. These approaches actually didn't work for me. I was waiting for more usage percentage for writing in here. I fixed this problem and I am not getting this crash any more. I will share my implementation as an answer – Beyazid May 09 '19 at 19:48
  • Hello Beyazid, if you have any tip for another desperate dev, it will be really welcome. Samsung devices only on my side as well. Tried many methods, none seems to work, crazy ... thanks for the possible tip. – Menion Asamm May 27 '19 at 18:37
  • Hi @MenionAsamm, I agree, this a really annoying problem. I shared my implementation. – Beyazid May 28 '19 at 10:27
  • I've also observed this on a Huawei P20 Lite (with Android 9) – Alix Jan 23 '20 at 15:50
  • Hi guys, thanks for your questions and opinions. But I changed my company and I don't work for this project or similar projects for a year. I shared the solution that was worked for me, but I don't remember the approach exactly. Probably, it's not the best approach but it was worked for the project. Best. – Beyazid May 23 '20 at 19:02

8 Answers8

20

After too much struggle with this crash finally, I fixed this exception completely and find the solution.

Make sure that have done this stuff in your service I list them as below : (some of this stuff are repetitious as mentioned in another answer I just write them again).

1- Call

startForeground()

in both onCreate and onStartCommand.(it's ok to call startForeground() many times)

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

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    if (intent == null) {
        return START_NOT_STICKY;
    }
    final int command = intent.getIntExtra(MAIN_SERVICE_COMMAND_KEY, -1);

    if (command == MAIN_SERVICE_START_COMMAND) {
        startCommand();
        return START_STICKY;
    }
    return START_NOT_STICKY;
}

 private void startCommand() {
    createNotificationAndStartForeground();
    runningStatus.set(STARTED);
}

2- Stop your service using

context.stopService()

, there is no need to call stopForeground() or stopSelf().

  try {
        context.stopService(
                new Intent(
                        context,
                        NavigationService.class
                )
        );
    } catch (Exception ex) {
        Crashlytics.logException(ex);
        LogManager.e("Service manager can't stop service ", ex);
    }

3- Start your service using

ContextCompat.startForegroundService()

it will handle different API versions.

   ContextCompat.startForegroundService(
            context,
            NavigationService.getStartIntent(context)
    );

4- If your service has actions ( need pending Intents ) handle your pending intents with a Broadcast receiver rather than your current service( it will call your service on Create() and can be dangerous, or use PendingIntent.FLAG_NO_CREATE) , it's a good practice to have a specific Broadcast receiver for handling your service notification actions, I mean create all your pending intents using PendingIntent.getBroadcast().

    private PendingIntent getStopActionPendingIntent() {
    final Intent stopNotificationIntent = getBroadCastIntent();

    stopNotificationIntent.setAction(BROADCAST_STOP_SERVICE);

    return getPendingIntent(stopNotificationIntent);
}

private PendingIntent getPendingIntent(final Intent intent) {
    return PendingIntent.getBroadcast(
            this,
            0,
            intent,
            0
    );
}

new NotificationCompat.Builder(this, CHANNEL_ID)
            .addAction(
                    new NotificationCompat.Action(
                            R.drawable.notification,
                            getString(R.string.switch_off),
                            getStopActionPendingIntent()
                    )
            )

5- Always before stop your service make sure that your service is created and started (I Create a global class that has my service state)

  if (navigationServiceStatus == STARTED) {
            serviceManager.stopNavigationService();
        }

6- Set your notificationId to a long number such as 121412.

7- Using NotificationCompat.Builder will handle different API versions you just need to create notification channel for Build versions >= Build.VERSION_CODES.O.(This one is not a solution just make your code more readable)

8- Add

<uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> 

permission to your manifest. (this one is mentioned in android docs) Android Foreground Service

hope it helps :))

Sepehr
  • 960
  • 11
  • 17
6

I was waiting my crash report to share the solution. I didn't get any crash or ANR almost 20 days. I want to share my solution. It can help those who encounter this problem.

In onCreate() method

  • First of all, my app is a media application. I didn't implement the mediasession yet. I'm creating a notification channel in the top of onCreate(). Official doc
  • I'm calling Service.startForeground() method after Context.startForegroundService() method. In my prepareAndStartForeground() method.

    Note: I don't know why but ContextCompat.startForegroundService() doesn't work properly.

For this reason, I've added manually same function to my service class instead of calling ContextCompat.startForegroundService()

private fun startForegroundService(intent: Intent) {
    if (Build.VERSION.SDK_INT >= 26) {
        context.startForegroundService(intent)
    } else {
        // Pre-O behavior.
        context.startService(intent)
    }
}

prepareAndStartForeground() method

private fun prepareAndStartForeground() {
    try {
        val intent = Intent(ctx, MusicService::class.java)
        startForegroundService(intent)

        val n = mNotificationBuilder.build()
        // do sth
        startForeground(Define.NOTIFICATION_ID, n)
    } catch (e: Exception) {
        Log.e(TAG, "startForegroundNotification: " + e.message)
    }
}

It's my onCreate()

override fun onCreate() {
    super.onCreate()
    createNotificationChannel()
    prepareAndStartForeground()
}

My onStartCommand()

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
    if (intent == null) {
        return START_STICKY_COMPATIBILITY
    } else {
        //....
        //...
    }
    return START_STICKY
}

onRebind, onBind, onUnbind methods like these

internal var binder: IBinder? = null

override fun onRebind(intent: Intent) {
    stopForeground(true) // <- remove notification
}

override fun onBind(intent: Intent): IBinder? {
    stopForeground(true) // <- remove notification
    return binder
}

override fun onUnbind(intent: Intent): Boolean {
    prepareAndStartForeground() // <- show notification again
    return true
}

We need to clear something when onDestroy() calling

   override fun onDestroy() {
    super.onDestroy()
    releaseService()
   }

private fun releaseService() {
    stopMedia()
    stopTimer()
    // sth like these
    player = null
    mContext = null
    afChangeListener = null
    mAudioBecomingNoisy = null
    handler = null
    mNotificationBuilder = null
    mNotificationManager = null
    mInstance = null
}

I hope this solution works properly for you.

Beyazid
  • 1,795
  • 1
  • 15
  • 28
  • Thanks for sharing your implementation. Seems you have a connection based on the binding. Maybe this may cause the main difference why otherwise identical solution works for you. Thanks for sharing! – Menion Asamm May 28 '19 at 21:10
  • Hi, any specific reason for "I don't know why but ContextCompat.startForegroundService() doesn't work properly." – ingsaurabh May 31 '19 at 05:18
  • 3
    Hi, can you please specify how do you create this service? because service onCreate method is called after calling: ContextCompat.startForegroundService(context, intent), and here you call: startForegroundService on your service onCreate. – CookieMonster Jun 03 '19 at 08:11
  • @ingsaurabh Actually, I don't know, because I didn't see any difference in logs or get any crash. I can only basically say that ContextCompat.startForegroundService() doesn't work for my code :) – Beyazid Jun 13 '19 at 17:41
  • @CookieMonster startForegroundService is a method that calling context.startForegroundService. You can see same calling inside of ContextCompat.startForegroundService(context, intent) – Beyazid Jun 13 '19 at 17:46
  • 6
    @Beyazid thank you for your answer but i'm not sure i understand. Is the onCreate method in your example is the onCreate of the MusicService? because if it is, i looks like you call startForegroundService twice. once form a different place in the app (in order to "trigger" the service onCreate) and second time in the service onCreate. – CookieMonster Jun 16 '19 at 08:30
  • Hi, @Beyazid, Is this solved and never shows again in your crash report? thanks – thecr0w Nov 12 '19 at 09:27
  • 2
    @Beyazid I am also confused. please comment below where the "onCreate()" method has placed. calling service in onCreate Method of Service doesn't make sense for me – aguagu Dec 02 '19 at 07:29
  • @Beyazid can you please answer comment of CookieMonster – Giru Bhai Mar 20 '20 at 12:09
  • `ContextCompat.startForegroundService` works properly – IgorGanapolsky Apr 10 '20 at 19:54
  • The answer is not clear. Your onCreate method is from which class, are you calling startForegroundService from the onCreate of the same service? – Gaurav Singla May 20 '20 at 15:36
  • Hi guys, thanks for your questions and opinions. But I changed my company and I don't work for this project or similar projects for a year. These code lines were worked for me, but I don't remember the approach exactly. Probably, it's not the best approach but it was worked for the project. Best. – Beyazid May 23 '20 at 18:58
  • Then this should not be the accepted best answer, since the author writes "but I don't remember the approach exactly. Probably, it's not the best approach. " It is very difficult to follow the logic in this code example. – Eyvind Almqvist Jan 14 '22 at 07:25
  • @mobile-visuals Nobody claims to be the best answer. I asked the question and also responded to it. It was the solution for me. I accepted it, thus it's the accepted answer :) – Beyazid Jan 14 '22 at 08:38
  • sorry makes no sense. Service is only starting after startForegroundService is called. In another words, you dont hit the OnCreate and OnStartCommand if you dont call StartForegroundService. this is the basic flow chart to understand it better https://medium.com/geekculture/context-startforegroundservice-did-not-then-call-service-startforeground-solved-7640d5ba394a – Emil May 29 '22 at 23:04
  • @Emil thanks for your effort, but you're probably more than three years late. the timing also makes no sense, sorry :D – Beyazid May 30 '22 at 22:09
4

Update on 06 January 2023

I was facing this issue in my Music Player Application

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

After a lot of research, I found some solutions. Maybe this will help others

Solution 1:

if your app targets API level 26 or higher, the system imposes restrictions on running background services when the app itself isn't in the foreground. If an app needs to create a foreground service, the app should call startForegroundService() . The system allows apps to call Context.startForegroundService() even while the app is in the background. However, the app must call that service's startForeground() method with five seconds after the service is created.

Note: call startForeground() in onCreate() for the service for which you use Context.startForegroundService()

Why this issue is happening because android framework can't guarantee your service get started within 5 seconds but on the other hand framework does have a strict limit on foreground notification must be fired within 5 seconds, without checking if the framework has tried to start the service startForeground() a notification must be in both onCreate() and onStartCommand() because if your service is already created and your activity is trying to start it again, onCreate() won't be called. notification ID must not be 0 otherwise same crash will happen even if it's not the same reason. stopSelf must not be call before startForeground.

Background Service Limitations:

The app service has five seconds to call startForeground(), the app does not call startForegroudn() within the time limit, the system stops the service and declares the app to be ANR.

Possibilities:

  1. Either your foreground service get destroyed/finished before call the startForeground() method.
  2. or if the foreground service is already instantiated and it's getting call again, then onCreate() method will not be call, instead onStartCommand() will be called. Then move your logic to onStartCommand() to call startForeground() method.
  3. Your notification id in startForeground() must not be 0, otherwise it will also cause the crash.

Solution 2:

  1. Call startForeground() in both onCreate() and onStartCommand() (it's ok to call startForeground() many times)

  2. Stop your service using context.stopService() there is no need to call stopForeground() or stopSelf()

  3. Start your service using ContextCompat.startForegroundService(). it will handle different APIs.

  4. if your service has actions (need pending intent ) handle your pending intents with a broadcast receiver rather than your current service(it will call your service in onCreate() and can be dangerous, or use PendingIntent.FLAG_NO_CREATE), it's good). practice to has a specific broadcast receiver for handling your service notification actions, I mean create all your pending intent using PendingIntent.getBroadcast().

  5. some of the crash reports are from issues related to BOOT_COMPLETED handling in Oreo

  6. basically once you do startForegroundService(), you have to do startForeground(). And if you cannot do startForeground() in your Service, then you better do that type of checking in your Broadcast Receiver, etc. instead - so that you only start Service when are sure will do startForeground() there

  7. Add Handler() like this:

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

Solution 3:

what to use instead of foreground services?

there are JobScheduler and JobService now, which are a better alternative for foreground services. It's a better option, because of that: While a job is running, the system holds a wakelock on behalf of your app. For this reason, you do not need to take any action to guarantee that the device stays awake for the duration of the job.

It means that you don't need to care about handling wakelocks anymore and that's why it's not different from foreground services. From implementation point of view JobScheduler is not your service, it's a system's one, presumably it will handle the queue right, and Google will never terminate its own child.

Samsung has switched from startForegroundService to JobScheduler and JobService in their Samsung Accessory Protocol (SAP). It's very helpful when devices like smartwatches need to talk to hosts like phones, where the job does need to interact with a user through an app's main thread. Since the jobs are posted by the scheduler to the main thread, it becomes possible. You should remember though that the job is running on the main thread and offload all heavy stuff to other threads and async tasks.

Solution 4:

The issue is to call Service.startForeground(id, notification) from the service itself, right? Android Framework unfortunately does not guarantee to call Service.startForeground(id, notification) within Service.onCreate() in 5 seconds but throws the exception anyway, so I've come up with this way. Bind the service to a context with a binder from the service before calling Context.startForegroundService()

If the bind is successful, call Context.startForegroundService() from the service connection and immediately call Service.startForeground() inside the service connection.

IMPORTANT NOTE: Call the Context.bindService() method inside a try-catch because in some occasions the call can throw an exception, in which case you need to rely on calling Context.startForegroundService() directly and hope it will not fail. An example can be a broadcast receiver context, however getting application context does not throw an exception in that case, but using the context directly does.

Solution 5:

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

Information provided by google team

This is not a framework bug; it's intentional. If the app starts a service instance with startForegroundService(), it must transition that service instance to the foreground state and show the notification. If the service instance is stopped before startForeground() is called on it, that promise is unfulfilled: this is a bug in the app.

publishing a Service that other apps can start directly is fundamentally unsafe. You can mitigate that a bit by treating all start actions of that service as requiring startForeground(), though obviously that may not be what you had in mind.

Solution 6:

I was able to get rid of all crashes. What I did was to remove the call to stopSelf(). (I was thinking about delaying the stop until I was pretty sure the notification was shown, but I don't want the user to see the notification if it isn't necessary.) When the service has been idle for a minute or the system destroys it normally without throwing any exceptions.

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

Solution 7:

A common crash related to usage of foreground services is :

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

To get rid of this use this strategy :

Instead of starting the service as a foreground service, start the service as a background service, bind to it, then when you have the service instance available in your activity/UI component, you can directly call a method inside the service which calls Service.startForeground() and adds the notification.

This might sound like a hack but think about how music apps like Spotify start their service. This is a similar approach. The results are outstanding. Using this approach the issue count reduced to 0 from a significant unrevealable number.

Yaqoob Bhatti
  • 1,271
  • 3
  • 14
  • 30
3

The Android Service component is a bit tricky to get working properly, especially on later Android versions where the OS adds additional restrictions. As mentioned in other answers, when starting your Service, use ContextCompat.startForegroundService(). Next, in Service.onStartCommand(), call startForeground() immediately. Store the Notification you want to show as a member field and use that unless it is null. Example:

private var notification:Notification? = null
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
    if (notification == null) {
        notification = createDefaultNotification()
    }
    startForeground(NOTIFICATION_ID, notification)

    // Do any additional setup and work herre

    return START_STICKY
}

Always return START_STICKY in your Service. Anything else is probably the wrong thing, especially if you're doing a audio player of any kind. In fact, if you're doing an audio player, you shouldn't implement your own Service but use MediaBrowserServiceCompat (from AndroidX) instead.

I also recommend the blog posts I wrote on this: https://hellsoft.se/how-to-service-on-android-part-3-1e24113152cd

Erik Hellman
  • 529
  • 4
  • 11
  • 1
    *Always return START_STICKY in your Service. Anything else is probably the wrong thing* that's rather presumptuous, care to explain? – Tim Nov 30 '20 at 18:09
  • @Tim Please read the post I linked to. In that series I explain why `START_STICKY` is what you should use today. There are of course exceptions, but for 99% of the cases where you need a `Service`, this is the right way to do it. – Erik Hellman Dec 02 '20 at 07:39
  • why you suggest using ContextCompat instead of Context? does it make any difference? – Emil May 29 '22 at 23:08
  • 1
    @Emil `ContextCompat` is part of the AndroidX libraries and contains enhancements and bugfixes and gets regular updates. The platform `Context` is only updated when the Android OS is updated. Always prefer the AndroidX wrappers when available. – Erik Hellman May 31 '22 at 07:05
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. This was running with Android 11.

@Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        
        //Create notification here
        
        startForeground(5, builder.build()); //Change the ID to something other than 0

        return START_NOT_STICKY;
    }
DIRTY DAVE
  • 2,523
  • 2
  • 20
  • 83
0

After having same issue with same phones, I have made few changes and the crashes were gone. I am not sure what made the trick, but I am guessing that calling startForeground in both onCreate and onStartCommand. I am not sure why that is needed if service is already started and everything was called properly in onCreate.

Other changes: - Changing serviceId to some low number (1-10) - Calling startFororegroundService less often through a singleton synchronous class (this was implemented before with the crashes to prevent stopping service before it started with a callback from onStartCommand, but now it also filters calls if service already started). - using START_REDELIVER_INTENT (shouldn't affect anything)

The issue has been happening on the mentioned phones just for some users, so I suspect it is related to some new update from Samsung and will eventually be fixed

  • you need to startForeground onStartCommand for the case of calling Context.startForegroundService(), when a Service is already created. – Malachiasz Aug 24 '21 at 05:42
0

I have almost eliminated the problem with startForeground() in MediaSessionCompat.Callback methods like onPlay(), onPause().

Mateusz Kaflowski
  • 2,221
  • 1
  • 29
  • 35
0

According to this error message this says as when you call Context.startForegroundService() then you must issue notification using Service.startForeground() method. This is what i understand.

user1090751
  • 336
  • 1
  • 11