Android 9 (Pie), Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord

Beyazid picture Beyazid · Apr 28, 2019 · Viewed 14.2k times · Source

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?

Answer

Sepehr picture Sepehr · Oct 23, 2019

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 :))