Foreground Serviceの要件と通知設計:落ちない実装の作り方

スポンサーリンク

導入:Foreground Serviceは「最後の砦」

Androidでバックグラウンド処理を実装していると、最終的に行き着くのが Foreground Service です。

「これなら確実に落ちないはず」
「とりあえず Foreground にすればいい」

そう思って実装した結果、起動しない・即死する・ストア審査に引っかかるという事故は今でも頻発します。

Foreground Service は強力ですが、使い方を間違えると最も壊れやすい仕組みでもあります。

この記事では、Foreground Service の要件と通知設計を整理し、「落ちない実装」をどう作るかを解説します。

先に結論です。

  • Foreground Service は「常駐用」ではない
  • 通知設計が実装の半分を占める
  • 要件違反は即クラッシュ or 即制限対象

Foreground Serviceとは何か

Foreground Service は、ユーザーに「今この処理が動いている」と明示した上で実行される Serviceです。

通常のバックグラウンド Service と違い、

  • 常に通知を表示する義務がある
  • ユーザーに可視であることが前提
  • OSから「重要な処理」として扱われる

という強い制約と引き換えに、実行継続が保証される仕組みです。

まず理解すべき大前提

Foreground Service は、「バックグラウンド制限の抜け道」ではありません

  • 常駐用途は禁止
  • 目的が不明確な実行は禁止
  • ユーザーに説明できない処理は禁止

ここを勘違いすると、設計段階で詰みます。

Foreground Service の要件

最低限、次のルールを守る必要があります。

  • 起動後、短時間以内に startForeground() を呼ぶ
  • 常に通知を表示し続ける
  • 処理の内容がユーザーに説明可能である

よくある死亡パターン

  • startForeground を呼ぶ前に重い処理をしている
  • 通知作成に失敗して例外が出る
  • 通知チャンネル未作成でクラッシュ

これらは即クラッシュ案件です。

基本実装の形

Service クラス


class SampleForegroundService : Service() {

    override fun onCreate() {
        super.onCreate()
        startInForeground()
    }

    private fun startInForeground() {
        val notification = createNotification()
        startForeground(1, notification)
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        // 実処理
        return START_NOT_STICKY
    }

    override fun onBind(intent: Intent?): IBinder? = null
}

通知の作成


private fun createNotification(): Notification {
    val channelId = "foreground_service_channel"

    val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        val channel = NotificationChannel(
            channelId,
            "Foreground Service",
            NotificationManager.IMPORTANCE_LOW
        )
        manager.createNotificationChannel(channel)
    }

    return NotificationCompat.Builder(this, channelId)
        .setContentTitle("処理実行中")
        .setContentText("バックグラウンド処理を実行しています")
        .setSmallIcon(R.drawable.ic_notification)
        .build()
}

通知が作れなかった瞬間に、このServiceは死にます。

通知設計がすべてを決める

Foreground Service の実装で一番重要なのは、
Service本体ではなく通知設計です。

通知に求められる条件

  • 何の処理か分かる
  • ユーザーが納得できる
  • 消せない理由が説明できる

「とりあえず表示しておく通知」は、ほぼ確実にNG判定されます。

用途別・正しい使いどころ

使ってよい例

  • 音楽再生
  • ナビゲーション
  • 通話・録音
  • ファイル転送の進捗表示

やってはいけない例

  • 定期同期の常駐化
  • ユーザーに見えない裏処理
  • WorkManager の代わり

WorkManager との役割分担

「落ちないバックグラウンド処理」という意味では、
WorkManager の方が圧倒的に安全です。

Foreground Service は、
「ユーザーに見せる必要があるリアルタイム処理」専用と考えるのが正解です。

Androidバージョン差分の罠

  • 起動制限の強化
  • 通知権限の影響
  • バックグラウンド起動制限

「昔動いていたコード」がそのまま通る保証はありません。

テストで必ず確認すべきポイント

  • 起動直後に落ちないか
  • 通知が必ず表示されるか
  • 通知を消せないか
  • 画面回転・プロセスキル後の挙動

よくあるアンチパターン

  • とりあえず Foreground にする
  • 通知をダミー扱いする
  • 常駐前提で設計する

まとめ

  • Foreground Service は最終手段
  • 通知設計が実装の本体
  • ユーザーに説明できない処理には使わない

迷ったら:まず WorkManager で設計できないか考える。
それが一番事故らない判断です。

コメント

タイトルとURLをコピーしました