StartForegroundService () не вызывал startForeground (), но вызывал

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

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

Вот единственный метод, в котором вызываются методы 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: onPlay, onPause и onStop ... очевидно, что действие является предполагаемым действием, которое необходимо выполнить.

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

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

Кроме того, onPlaybackStateChange переходит к моему действию «Сейчас играет» в методе 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. API устройства - 26.

Я столкнулся с этой проблемой недавно при создании медиа-службы (хотя, вероятно, это не совсем связано). Добавьте свою версию Android и уровень API в свой вопрос, и я посмотрю, смогу ли я помочь.

YodaScholtz 17.05.2018 16:17

Спасибо. Я добавил это, а также еще одну вещь, которую обнаружил во время отладки.

transiti0nary 17.05.2018 17:26

@ transiti0nary if (action == PlaybackStateCompat.ACTION_PLAY) это если условие выполняется в течение 5 секунд? Я имею в виду, что эта conidtion true и ACTION_PLAY срабатывает в течение 5 секунд?

Sagar 18.05.2018 17:21

Через 5 секунд после запуска услуги? Я не уверен. Я также мог бы активировать его в onCreate, чтобы быть в безопасности, но есть ли функция для проверки наличия уведомления переднего плана, поэтому я все еще могу запустить его в этой функции, когда воспроизведение переходит от паузы к воспроизведению (когда onCreate не будет вызываться ?

transiti0nary 18.05.2018 17:34
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
11
4
6 073
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

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

Хотя здесь это не так, я тоже это испытал! Потребовалось много времени, чтобы понять это.

YodaScholtz 17.05.2018 21:37
Ответ принят как подходящий

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

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

Вот отрывок (возьмите из него то, что вам нужно) из метода, который я использую для создания наших медиа-уведомлений. Вы можете видеть, что есть проверка устройств 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(), вам нужно очистить несколько вещей (применительно к вашему случаю) следующим образом:

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

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

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

Спасибо, я попробую это позже сегодня, если смогу. Одна вещь, однако, моя функция создания уведомлений запускает эту функцию канала уведомлений, показанную здесь: developer.android.com/training/notify-user/channels делает то же самое? Еще я заметил, что мой конструктор не имеет дополнительного setStyle, если !Utils.hasLollipop(), и всегда использует этот конструктор NotificationCompat.Builder(context, NOTIFICATION_CHANNEL), независимо от hasLollipop. Может ли это быть связано с этим?

transiti0nary 18.05.2018 12:48

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

YodaScholtz 18.05.2018 13:10

@ transiti0nary Я добавил еще несколько подробностей о том, как я обрабатываю метод onStop() в моем медиа-сервисе. Пожалуйста, дайте мне знать, помогает ли это вообще.

YodaScholtz 22.05.2018 12:09

В вашем коде может быть проблема с вызовом stopForeground(true); перед stopSelf(); - stopSelf () не уничтожает службу немедленно.

YodaScholtz 22.05.2018 12:11

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

YodaScholtz 24.05.2018 11:50

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

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

transiti0nary 31.05.2018 17:42

У меня такая же проблема с Android 8, 9 и замените PendingIntent.getForegroundService () на PendingIntent.getService, решите ее

DzungPV 12.03.2019 13:14

Другие вопросы по теме