Androidのフォアグラウンドサービス起動、5秒以内にstartForeground()を呼ばないとANRになる

  • Android
  • Kotlin

この記事は、構成と文章の整理にAIを使っています。
あくまで読みやすくするための補助で、内容そのものをAIにすべて任せて書いているわけではありませんが、ところどころ相談しながら整えています。

Android 8.0(API 26)以降、バックグラウンドのアプリはサービスを直接起動できなくなって、startForegroundService()を使うことになった。このとき、サービス側で5秒以内にstartForeground()を呼ばないとシステムがサービスを止めてANR扱いにする、という制限がある。

何が起きるか

流れはこう。

  1. 呼び出し元がstartForegroundService()を呼ぶ
  2. システムがサービスを作成する
  3. サービスのonCreate()またはonStartCommand()内で5秒以内にServiceCompat.startForeground(...)を呼ぶ
  4. 呼ばなかった場合、ForegroundServiceDidNotStartInTimeExceptionでクラッシュする
sequenceDiagram
    participant Caller
    participant System
    participant Service
    Caller->>System: startForegroundService()
    System->>Service: onCreate() / onStartCommand()
    Note over Service: ここから5秒以内
    Service->>System: startForeground()
    alt 5秒以内に呼んだ
        System-->>Service: フォアグラウンド化成功
    else 5秒超過
        System-->>Service: ANR (ForegroundServiceDidNotStartInTimeException)
    end

onCreate()で重い初期化処理(DBオープン、ネットワーク接続、大きめのオブジェクト生成など)をやってからstartForeground()を呼んでいると、この5秒に間に合わなくて落ちる。対策は単純で、startForeground()を真っ先に呼んでから、重い処理は後回しにする。

Android 12以降の変更点

Android 12以降は、短時間で終わるフォアグラウンドサービスについてはシステムが通知の表示を10秒待つようになった。setForegroundServiceBehavior()にFOREGROUND_SERVICE_IMMEDIATEを渡すとこの挙動をオプトアウトできる。ただしこれはあくまで「通知がユーザーに見えるタイミング」の話で、startForeground()自体を5秒以内に呼ぶという制約がなくなったわけではない。

WorkManager経由の場合も同じ

WorkManagerでsetForeground() / setForegroundAsync()を使ってフォアグラウンドサービスを立てる場合も同様に、速やかに呼び出す必要がある。2つのフォアグラウンドワーカーのライフサイクルが重なる(片方がシャットダウン中にもう片方が起動しようとする)とクラッシュするバグがあったらしく、WorkManager 2.10.5でこの問題への対応が入っているとのこと(この修正内容自体は実機未確認)。

ハマりどころ

  • API 34以降はforegroundServiceTypeの宣言が必須。宣言していないとそもそも起動条件を満たさない
  • API 34以降はサービスタイプごとに個別の権限(FOREGROUND_SERVICE_CAMERAなど)も必要
  • Android 12以降はバックグラウンド状態のアプリからのフォアグラウンドサービス起動自体が制限されていて、locationやremoteMessagingなど一部タイプを除いて例外的な許可が必要になる

5秒制限そのものはシンプルだけど、周辺のバージョン差分(通知表示タイミング、サービスタイプの権限)が絡むと切り分けが面倒になる。