web-dev-qa-db-ja.com

「Context.startForegroundService()がService.startForeground()を呼び出さなかった」エラーでバインドされたサービスがクラッシュする

私は現在、オーディオ再生アプリに取り組んでおり、開始されたバインドされたサービスを使用してバックグラウンドで音楽を再生しています。次のコードを使用して、サービスを開始してバインドします。

val intent = Intent(context, MusicService::class.Java)
context.startService(intent)
context.bindService(intent, serviceConnection, 0)

再生時にフォアグラウンドに昇格し、音楽が一時停止すると降格します。

// onAudioPlay
service.startForeground(NOTIFY_ID, notification)

// onAudioPause
service.stopForeground(false)

これまでのサービス作業は問題ありません。ただし、一時停止状態のユーザーが通知をスワイプ(削除)すると、このエラーが発生して数秒後にサービスがクラッシュします。

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.myplayer, PID: 4380
    Android.app.RemoteServiceException: Context.startForegroundService() did not then call Service.startForeground()
        Android.app.ActivityThread$H.handleMessage(ActivityThread.Java:1775)
        Android.os.Handler.dispatchMessage(Handler.Java:105)
        Android.os.Looper.loop(Looper.Java:164)
        Android.app.ActivityThread.main(ActivityThread.Java:6541)
        Java.lang.reflect.Method.invoke(Native Method)
      com.Android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.Java:240)
        com.Android.internal.os.ZygoteInit.main(ZygoteInit.Java:767)

これはOreoでのみ発生し、Oreoのバックグラウンドの制限についてはすでに読んでいます。しかし、次の点は私を悩ませます。

  • このサービスはバインドされたサービスです(制限の影響を受けません)
  • Context.startForegroundService()を使用してサービスを開始することはありません(使用したくない)
  • サービスは、フォアグラウンドから降格されてもクラッシュせず、通知を削除するときに発生します。

サービスがクラッシュするのはなぜですか?私は何を間違えていますか?ここで起こっていることを誰かが教えてくれてとても感謝しています。

21
UdeshUK

deshUk's answer -を展開すると、MediaButtonReceiver(保留中のインテントを作成するか、メディアボタンインテントを受信する)を使用すると、MediaButtonReceiver.onReceiveがOreoでそのようにサービスを開始するため、startForegroundServiceでサービスが呼び出されます。これはどこにも記録されていません。

12
Andy S

ServiceクラスのonCreate()メソッド内でこのメソッドを呼び出す必要があります:->

private void startServiceOreoCondition(){
        if (Build.VERSION.SDK_INT >= 26) {


            String CHANNEL_ID = "my_service";
            String CHANNEL_NAME = "My Background Service";

            NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
                    CHANNEL_NAME,NotificationManager.IMPORTANCE_NONE);
            ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).createNotificationChannel(channel);

            Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
                    .setCategory(Notification.CATEGORY_SERVICE).setSmallIcon(R.drawable.ic_tgc_icon).setPriority(PRIORITY_MIN).build();

            startForeground(101, notification);
        }
    }
8
Ramkailash

問題が見つかりました。私の通知で私は設定しました

setDeleteIntent(MediaButtonReceiver.buildMediaButtonPendingIntent(service, PlaybackStateCompat.ACTION_STOP))

したがって、ユーザーが通知を削除すると、一時停止時に以前にstopForeground()を呼び出したため、startForegroundService()が呼び出されます。

克服するために、カスタム保留インテントを使用し、MediaButtonReveiver's PendingIntentの代わりにonStartCommand()で処理しました

6
UdeshUK

ドキュメントから 8.0動作の変更

システムは、アプリがバックグラウンドにあるときでも、アプリがContext.startForegroundService()を呼び出すことを許可します。ただし、アプリは、サービスの作成後5秒以内にそのサービスのstartForeground()メソッドを呼び出す必要があります。

したがって、サービスのonCreate()内でstartForegroundを呼び出す必要があります。

2
lakshman.pasala

パーティーに遅れましたが、前述のとおり、それはMediaButtonReceiverです。このコードがあります:

    @Override
    public void onReceive(Context context, Intent intent) {
        ...
        startForegroundService(context, intent);
        ...
    }

バインドされたサービスでレシーバーを使用しているため、この行を除き、MediaButtonReceiverとまったく同じコードを持つクラスを作成しました。私はそれを次のものに置き換えました:

        context.startService(intent);
1
UpsideDownTree