web-dev-qa-db-ja.com

startForegroundService()はstartForeground()を呼び出しませんでしたが、呼び出しました

私のAndroidサービスにContext.startForegroundService() did not then call Service.startForeground()がありますが、なぜそれが起こっているのかわかりません。

私のアプリケーションはメディアストリーミング用であり、このエラーは、フォアグラウンド通知から一時停止(通常の通知に変更)してから通知をスワイプして、サービスを停止する場合にのみ発生します。

startForegroundServicestartForeground、および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コールバックからのみ呼び出されます:onPlayonPauseおよびonStop...明らかにアクションが実行される予定のアクション。

UIからonStopを実行するとき、またはUIからonPauseに続いてonStopを呼び出すとき(通知をクリアするために必要なアクションをミラーリングするとき)、エラーは発生しません。通知から。

このエラーについて私が見つけることができるのは、startForegroundServiceを呼び出したときに5秒以内にstartForegroundを呼び出さないと、おそらく発生するということです...しかし、startForegroundstartForegroundServiceのみが呼び出されます。

さらに、onPlaybackStateChangeonStopメソッドの「Now Playing」アクティビティに送られます。これにより、そのアクティビティがfinish()を実行し、サービスが再起動されなくなります仕方。

ここに何が欠けていますか?

追加の詳細:

  • コードの実行がメソッドに到達することはないため、サービスは「再生中」アクティビティによって部分的に再起動されません。

  • また、エラーが発生する前にコード実行がconfigureServiceStateに再入力することもありません。

  • 可能な最後のポイントにブレークポイントを追加すると(サービスのonStartCommandMediaButtonReceiver.handleIntent(mMediaSession, intent);)、ここで実行を一時停止してデバッグしようとすると、一時停止後すぐにデバッガが切断されます

  • フォアグラウンド通知と通常の通知で異なる通知チャネルを試しても違いはありません

  • ロック画面から一時停止した通知をスワイプしてもエラーは発生しません。電話がロック解除されている場合にのみ発生します。アプリが実際に開いているかどうかに関係なく

完全な例外:

_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です。

10
transiti0nary

Android O API 26以降ではNotificationChannelを使用する必要があります。そうしないと、経験したエラーが発生します。Android docs- 通知チャネルの作成と管理

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()メソッドが呼び出されると、次のように(ケースに応じて)いくつかのことをクリーンアップする必要があります。

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

上記のメソッドの一部については詳細を含めていませんが、メソッド名は自己コメントである必要があります。詳細を含めることができるかどうかを教えてください。それらのいくつかは過剰であるかもしれませんが、あなたの場合には問題を解決するかもしれません。

これで問題が解決されると確信しています。そうでない場合は、さらにいくつかのアイデアがあります。

5
YodaScholtz

これに何時間も無駄にしただけです。これがあなたが経験しているものかどうかはわかりませんが、私の場合、NOTIFICATION_ID0 ...他の値を使用すると、これが修正されるようです。-_-

5
tobalr

同じ問題が発生しました。問題は、メディアサービスを更新するためにクリックする通知項目でPendingIntent.getForegroundService()を使用していたことです。 PendingIntent.getServiceを使用するだけで問題は解決しました。

1
Pitt90