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.
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.
Just wasted way too many hours on this. I'm not sure that this is what you are experiencing, but in my case I kept getting this exception because my NOTIFICATION_ID
was 0
... Using any other value seems to fix this.-_-
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With