startForegroundService() did not call startForeground(), but it did

transiti0nary :

I'm having the Context.startForegroundService() did not then call Service.startForeground() in my Android service, but I cannot figure out why it is happening.

My application is for media streaming, and this error only occurs when you pause from the foreground notification (turning it into a regular notification), and you then swipe the notification away, which is intended to stop my service.

Here is the only method where the startForegroundService, startForeground and stopForeground methods are called:

private void configureServiceState(long action) {
        if (action == PlaybackStateCompat.ACTION_PLAY) {
            if (!mServiceInStartedState) {
                ContextCompat.startForegroundService(
                        StreamingService.this,
                        new Intent(
                                StreamingService.this,
                                StreamingService.class));
                mServiceInStartedState = true;
            } startForeground(NOTIFICATION_ID,
                    buildNotification(PlaybackStateCompat.ACTION_PAUSE));
        } else if (action == PlaybackStateCompat.ACTION_PAUSE) {
            stopForeground(false);

            NotificationManager mNotificationManager
                    = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

            assert mNotificationManager != null;
            mNotificationManager
                    .notify(NOTIFICATION_ID,
                            buildNotification(PlaybackStateCompat.ACTION_PLAY));
        } else if (action == PlaybackStateCompat.ACTION_STOP) {
            mServiceInStartedState = false;
            stopForeground(true);
            stopSelf();
        }
    }

And here is where the delete intent of my notification is set:

.setDeleteIntent(
                        MediaButtonReceiver.buildMediaButtonPendingIntent(
                                this, PlaybackStateCompat.ACTION_STOP));

This configureServiceState(long action) method is called only from my MediaSession callbacks: onPlay, onPause and onStop... obviously with the action being the intended action to be performed.

The error doesn't occur when performing onStop from my UI, or when calling onPause followed by onStop from the UI (mirroring the action required to clear the notification), only from the notification.

All I can find about this error is that it supposedly occurs when you call startForegroundService but don't call startForeground within 5 seconds... however the startForeground is invoked immediately after the only time startForegroundService is invoked.

Additionally, an onPlaybackStateChange is going to my 'Now Playing' activity in the onStop method, which triggers that activity to run finish() so that the service is not being restarted that way.

What am I missing here?

Additional details:

  • The service is not being partially restarted by my 'now playing' activity as code execution never reaches any of its methods.

  • The code execution also never seems to re-enter configureServiceState before the error is produced

  • If I add a breakpoint at the last possible point (MediaButtonReceiver.handleIntent(mMediaSession, intent); in onStartCommand of my service), pausing execution here and trying to debug causes the debugger to disconnect shortly after pausing

  • Trying a different notification channel for the foreground vs regular notification doesn't make a difference either

  • Swiping the paused notification away from the lock screen does not cause an error, it only occurs if the phone is unlocked; regardless of whether my app is actually open

Full exception:

android.app.RemoteServiceException: Context.startForegroundService() did not then call Service.startForeground()
                      at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1870)
                      at android.os.Handler.dispatchMessage(Handler.java:105)
                      at android.os.Looper.loop(Looper.java:164)
                      at android.app.ActivityThread.main(ActivityThread.java:6809)
                      at java.lang.reflect.Method.invoke(Native Method)
                      at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240)
                      at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)

My testing device is running Android 8.0, the project min API is 21 and target API is 27. Device API is 26.

YodaScholtz :

You need to use a NotificationChannel for Android O API 26 and above, otherwise you'll get the error you have experienced. See the following statement from the Android docs - Create and Manage Notification Channels:

Starting in Android 8.0 (API level 26), all notifications must be assigned to a channel.

Here is an excerpt (take from it what you need) from a method I use for building our media notifications. You can see there is a check for Android O devices with a specific method to handle that case:

private fun compileNotification(context: Context, action: NotificationCompat.Action, mediaSession: MediaSessionCompat, controller: MediaControllerCompat, mMetadata: MediaMetadataCompat, art: Bitmap?, mPlaybackState: PlaybackStateCompat) {

    val description = mMetadata.description

    // https://stackoverflow.com/questions/45395669/notifications-fail-to-display-in-android-oreo-api-26
    @TargetApi(26)
    if(Utils.hasO()) {
        val channelA = mNotificationManager?.getNotificationChannel(NotificationChannelID.MEDIA_SERVICE.name)

        if(channelA == null) {
            val channelB = NotificationChannel(NotificationChannelID.MEDIA_SERVICE.name,
                    "MediaService",
                    NotificationManager.IMPORTANCE_DEFAULT)
            channelB.setSound(null, null)

            mNotificationManager?.createNotificationChannel(channelB)
        }
    }

    val notificationBuilder = if(Utils.hasLollipop()) {
        NotificationCompat.Builder(context, NotificationChannelID.MEDIA_SERVICE.name)
    } else {
        NotificationCompat.Builder(context)
    }

    notificationBuilder
            .setStyle(android.support.v4.media.app.NotificationCompat.MediaStyle()
                    // Show actions 0,2,4 in compact view
                    .setShowActionsInCompactView(0,2,4)
                    .setMediaSession(mediaSession.sessionToken))
            .setSmallIcon(R.drawable.logo_icon)
            .setShowWhen(false)
            .setContentIntent(controller.sessionActivity)
            .setContentTitle(description.title)
            .setContentText(description.description)
            .setLargeIcon(art)
            .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
            .setOngoing(mPlaybackState.state == PlaybackStateCompat.STATE_PLAYING)
            .setOnlyAlertOnce(true)

            if(!Utils.hasLollipop()) {
                notificationBuilder
                        .setStyle(android.support.v4.media.app.NotificationCompat.MediaStyle()
                                // Show actions 0,2,4 in compact view
                                .setShowActionsInCompactView(0,2,4)
                                .setMediaSession(mediaSession.sessionToken)
                                .setShowCancelButton(true)
                                .setCancelButtonIntent(MediaButtonReceiver.buildMediaButtonPendingIntent(context,
                                        PlaybackStateCompat.ACTION_STOP)))
                        // Stop the service when the notification is swiped away
                        .setDeleteIntent(MediaButtonReceiver.buildMediaButtonPendingIntent(context,
                                PlaybackStateCompat.ACTION_STOP))
            }

    notificationBuilder.addAction(NotificationCompat.Action(
            R.drawable.exo_controls_previous,
            "Previous",
            MediaButtonReceiver.buildMediaButtonPendingIntent(context,
                    PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS)))
    notificationBuilder.addAction(NotificationCompat.Action(
            R.drawable.ic_replay_10_white_24dp,
            "Rewind",
            MediaButtonReceiver.buildMediaButtonPendingIntent(context,
                    PlaybackStateCompat.ACTION_REWIND)))

    notificationBuilder.addAction(action)

    notificationBuilder.addAction(NotificationCompat.Action(
            R.drawable.ic_forward_10_white_24dp,
            "Fast Foward",
            MediaButtonReceiver.buildMediaButtonPendingIntent(context,
                    PlaybackStateCompat.ACTION_FAST_FORWARD)))
    notificationBuilder.addAction(NotificationCompat.Action(
            R.drawable.exo_controls_next,
            "Next",
            MediaButtonReceiver.buildMediaButtonPendingIntent(context,
                    PlaybackStateCompat.ACTION_SKIP_TO_NEXT)))

    (context as MediaService).startForeground(NOTIFICATION_ID, notificationBuilder.build())
}

In your onStop() callback, you'll need to call stopSelf() within the service. Then, when your service onDestroy() method is called you need to clean up a few things (as applies to your case) as follows:

override fun onDestroy() {
    super.onDestroy()

    abandonAudioFocus()
    unregisterReceiver(mNoisyReceiver)

    //Deactivate session
    mSession.isActive = false
    mSession.release()

    NotificationManagerCompat.from(this).cancelAll()

    if(mWiFiLock?.isHeld == true) mWiFiLock?.release()

    stopForeground(true)
}

I haven't included detail for some of the methods above, but the method names should be self commenting - let me know if I can include more detail. Some of them might be overkill, but in your case might solve the issue.

I'm pretty sure this will solve your issue. If it doesn't I have a few more ideas.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=36923&siteId=1