startForegroundService() не вызывал startForeground(), но это

У меня есть Context.startForegroundService() did not then call Service.startForeground() в моей службе Android, но я не могу понять, почему это происходит.

Мое приложение предназначено для потоковой передачи мультимедиа, и эта ошибка возникает только тогда, когда вы приостанавливаете уведомление переднего плана (превращая его в регулярное уведомление), а затем удаляете уведомление, которое предназначено для остановки моего обслуживания.

Вот единственный метод, startForeground stopForeground методы startForegroundService, startForeground и stopForeground:

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();
        }
    }

И вот здесь устанавливается цель удаления моего уведомления:

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

Этот метод configureServiceState(long action) вызывается только из моих MediaSession вызовов MediaSession: onPlay, onPause и onStop... очевидно, что действие является предполагаемым действием, которое необходимо выполнить.

Ошибка при выполнении onStop из моего пользовательского интерфейса или при вызове onPause за которым следует onStop из пользовательского интерфейса (зеркальное отображение действия, необходимого для очистки уведомления), только из уведомления.

Все, что я могу найти об этой ошибке, заключается в том, что она якобы возникает, когда вы вызываете startForegroundService но не вызываете startForeground течение 5 секунд... однако startForeground вызывается сразу же после startForegroundService только startForegroundService.

Кроме того, onPlaybackStateChange переходит к моей активности "Now Playing" в методе onStop, который запускает эту onStop для запуска finish() чтобы эта служба не перезапускалась таким образом.

Что мне здесь не хватает?

Дополнительные детали:

  • Услуга не будет частично перезапущена моей деятельностью "сейчас играть", так как выполнение кода никогда не достигает каких-либо своих методов.

  • Выполнение кода также никогда не заново вводит configureServiceState до возникновения ошибки

  • Если я добавлю точку останова в последней возможной точке (MediaButtonReceiver.handleIntent(mMediaSession, intent); в onStartCommand моей службы), приостановка выполнения здесь и попытка отладки заставит отладчик отключиться вскоре после паузы

  • Попытка другого канала уведомлений для приоритетного или обычного уведомления не имеет значения ни

  • Выключение приостановленного уведомления из экрана блокировки не вызывает ошибки, это происходит только в том случае, если телефон разблокирован; независимо от того, действительно ли мое приложение открыто

Полное исключение:

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)

На моем тестовом устройстве работает Android 8.0, минимальный API проекта - 21, а целевой API - 27. Device API - 26.

Ответ 1

Вам необходимо использовать NotificationChannel для Android O API 26 и выше, иначе вы получите ошибку, с которой вы столкнулись. См. Следующее выражение в документах Android - Создание и управление каналами уведомлений:

Начиная с Android 8.0 (уровень API 26), все уведомления должны быть назначены каналу.

Вот выдержка (возьмите из нее то, что вам нужно) из метода, который я использую для создания уведомлений в средствах массовой информации. Вы можете видеть, что есть проверка для устройств Android O с определенным методом обработки этого случая:

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())
}

В onStop() вам нужно вызвать stopSelf() в службе. Затем, когда вы onDestroy() метод onDestroy() вам нужно очистить несколько вещей (применительно к вашему делу) следующим образом:

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

Я не включил детали для некоторых из вышеперечисленных методов, но имена методов должны быть самокомментирующими - сообщите мне, могу ли я включить более подробную информацию. Некоторые из них могут быть излишними, но в вашем случае это может решить проблему.

Я уверен, что это решит вашу проблему. Если это не так, у меня есть еще несколько идей.

Ответ 2

Просто потратил слишком много времени на это. Я не уверен, что это то, что вы испытываете, но в моем случае я продолжал получать это исключение, потому что мой NOTIFICATION_ID был 0... Использование любого другого значения, похоже, исправить это.-_-

Ответ 3

У меня была такая же проблема. Проблема заключалась в использовании PendingIntent.getForegroundService() в элементах уведомлений, чтобы обновить медиа-сервис. Использование только PendingIntent.getService решило проблему для меня.