

startForegroundService()後にstartForeground()を呼ばないとクラッシュする
この記事は、構成と文章の整理にAIを使っています。
あくまで読みやすくするための補助で、内容そのものをAIにすべて任せて書いているわけではありませんが、ところどころ相談しながら整えています。
こんなクラッシュログが出た。
Fatal Exception: android.app.RemoteServiceException: Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{4a55f88 u0 com.example.app/.service.LocationForegroundService} at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2290) at android.os.Handler.dispatchMessage(Handler.java:106) at android.os.Looper.loop(Looper.java:264) at android.app.ActivityThread.main(ActivityThread.java:8315) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:632) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1049)何が起きたか
Context.startForegroundService()でサービスを起動したのに、サービス側がService.startForeground()を呼ばなかったことでクラッシュしている。位置情報を扱うフォアグラウンドサービス(LocationForegroundService)で発生した。
原因
Android 8.0(API 26)以降、アプリがバックグラウンド状態のときにバックグラウンドサービスを直接作ることができなくなった。そのためフォアグラウンドサービスを起動する場合はstartForegroundService()を使う必要がある。
このメソッドでサービスを起動すると、システム側は「これからフォアグラウンドサービスになる」と期待する。ただしそれを保証するのはアプリ側の責任で、サービスは起動後5秒以内にstartForeground()を呼んで通知を出さないといけない。5秒以内に呼ばれなかった場合、システムはサービスを停止してこのRemoteServiceExceptionを投げる。
呼び忘れ以外にも、onCreate()やonStartCommand()内でDBアクセスやネットワーク通信など時間のかかる処理をメインスレッドで実行していると、それだけで5秒を超えてクラッシュすることがあるらしい(実機での再現は未確認)。
対処法
onStartCommand()の先頭、もしくはonCreate()でできるだけ早くstartForeground()を呼ぶ。
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { val notification = NotificationCompat.Builder(this, CHANNEL_ID) .setContentTitle("位置情報を取得中") .setSmallIcon(R.drawable.ic_notification) .build()
startForeground(NOTIFICATION_ID, notification)
// ここから重い処理を始める
return START_STICKY}ポイントは、通知の生成自体を軽くしておくこと。通知の内容を組み立てるために外部データの取得やDBアクセスが必要な場合は、先に最低限の通知でstartForeground()を呼んでおいて、後からnotify()で内容を更新する方が安全。
まとめ
- Android 8.0以降は
startForegroundService()→5秒以内にstartForeground()が必須 - 呼び忘れだけでなく、メインスレッドのブロックでも同じ例外が起きうる
startForeground()はできるだけ早く、軽い処理で呼ぶのがコツ