フォアグラウンド サービスを起動する

アプリからフォアグラウンド サービスを起動する手順は 2 つあります。まず、context.startForegroundService() を呼び出してサービスを開始する必要があります。次に、サービスが ServiceCompat.startForeground() を呼び出して、フォアグラウンド サービスに昇格するようにします。

前提条件

アプリがターゲットとする API レベルに応じて、アプリがフォアグラウンド サービスを起動できるタイミングに制限があります。

  • Android 12(API レベル 31)以降をターゲットとするアプリでは、アプリがバックグラウンドにあるときにフォアグラウンド サービスを開始することはできません(いくつかの例外を除く)。詳細と、このルールの例外については、バックグラウンドからフォアグラウンド サービスを開始する場合の制限をご覧ください。

  • Android 14(API レベル 34)以降をターゲットとするアプリは、フォアグラウンド サービス タイプに適した権限をリクエストする必要があります。アプリがサービスをフォアグラウンドに昇格させようとすると、システムは適切な権限をチェックし、アプリに権限がない場合は SecurityException をスローします。たとえば、location タイプのフォアグラウンド サービスを起動しようとすると、アプリに ACCESS_COARSE_LOCATION 権限または ACCESS_FINE_LOCATION 権限がすでに付与されているかどうかがチェックされます。フォアグラウンド サービス タイプのドキュメントには、各フォアグラウンド サービス タイプに必要な前提条件が記載されています。

サービスを起動する

フォアグラウンド サービスを起動するには、まず通常の(フォアグラウンド以外の)サービスとして起動する必要があります。

Kotlin

val intent = Intent(...) // Build the intent for the service
context.startForegroundService(intent)

Java

Context context = getApplicationContext();
Intent intent = new Intent(...); // Build the intent for the service
context.startForegroundService(intent);

コードに関する主なポイント

  • このコード スニペットはサービスを起動します。ただし、サービスはまだフォアグラウンドで実行されていません。サービス自体で、ServiceCompat.startForeground() を呼び出してサービスをフォアグラウンド サービスに昇格させる必要があります。

サービスをフォアグラウンドに昇格させる

サービスが実行されたら、ServiceCompat.startForeground() を呼び出して、サービスをフォアグラウンドで実行するようリクエストする必要があります。通常、このメソッドはサービスの onStartCommand() メソッドで呼び出します。

ServiceCompat.startForeground() は次のパラメータを取ります。

startForeground() に渡すフォアグラウンド サービスのタイプ。特定のユースケースに応じて、マニフェストで宣言されたタイプ。サービスタイプをさらに追加する必要がある場合は、startForeground() を再度呼び出すことができます。

たとえば、フィットネス アプリが、常に location 情報を必要とするランニング トラッカー サービスを実行しているが、メディアの再生が必要になる場合とそうでない場合があります。マニフェストで locationmediaPlayback の両方を宣言する必要があります。ユーザーがランニングを開始し、位置情報の追跡のみを希望する場合、アプリは startForeground() を呼び出して ACCESS_FINE_LOCATION 権限のみを渡す必要があります。次に、ユーザーが音声の再生を開始する場合は、startForeground() を再度呼び出して、すべてのフォアグラウンド サービス タイプのビット単位の組み合わせ(この場合は ACCESS_FINE_LOCATION|FOREGROUND_SERVICE_MEDIA_PLAYBACK)を渡します。

次の例は、カメラ サービスがフォアグラウンド サービスに昇格するために使用するコードを示しています。

Kotlin

class MyCameraService: Service() {

  private fun startForeground() {
    // Before starting the service as foreground check that the app has the
    // appropriate runtime permissions. In this case, verify that the user has
    // granted the CAMERA permission.
    val cameraPermission =
            PermissionChecker.checkSelfPermission(this, Manifest.permission.CAMERA)
    if (cameraPermission != PermissionChecker.PERMISSION_GRANTED) {
        // Without camera permissions the service cannot run in the foreground
        // Consider informing user or updating your app UI if visible.
        stopSelf()
        return
    }

    try {
        val notification = NotificationCompat.Builder(this, "CHANNEL_ID")
            // Create the notification to display while the service is running
            .build()
        ServiceCompat.startForeground(
            /* service = */ this,
            /* id = */ 100, // Cannot be 0
            /* notification = */ notification,
            /* foregroundServiceType = */
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA
            } else {
                0
            },
        )
    } catch (e: Exception) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
                && e is ForegroundServiceStartNotAllowedException) {
            // App not in a valid state to start foreground service
            // (e.g. started from bg)
        }
        // ...
    }
  }
}

Java

public class MyCameraService extends Service {

    private void startForeground() {
        // Before starting the service as foreground check that the app has the
        // appropriate runtime permissions. In this case, verify that the user
        // has granted the CAMERA permission.
        int cameraPermission =
            ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA);
        if (cameraPermission == PackageManager.PERMISSION_DENIED) {
            // Without camera permissions the service cannot run in the
            // foreground. Consider informing user or updating your app UI if
            // visible.
            stopSelf();
            return;
        }

        try {
            Notification notification =
                new NotificationCompat.Builder(this, "CHANNEL_ID")
                    // Create the notification to display while the service
                    // is running
                    .build();
            int type = 0;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
                type = ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA;
            }
            ServiceCompat.startForeground(
                    /* service = */ this,
                    /* id = */ 100, // Cannot be 0
                    /* notification = */ notification,
                    /* foregroundServiceType = */ type
            );
        } catch (Exception e) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S &&
                    e instanceof ForegroundServiceStartNotAllowedException
            ) {
                // App not in a valid state to start foreground service
                // (e.g started from bg)
            }
            // ...
        }
    }

    //...
}

コードに関する主なポイント

  • アプリは、CAMERA 権限が必要であることをマニフェストですでに宣言しています。ただし、アプリは実行時にユーザーがその権限を付与したことを確認する必要があります。アプリに正しい権限が実際にない場合は、問題をユーザーに知らせるようにする必要があります。
  • フォアグラウンド サービスの種類は、Android プラットフォームのバージョンによって異なります。このコードは、実行されている Android のバージョンを確認し、適切な権限をリクエストします。
  • コードは、許可されていない状況でフォアグラウンド サービスを開始しようとしている場合(アプリがバックグラウンドにあるときにサービスをフォアグラウンドに昇格させようとしている場合など)に備えて、ForegroundServiceStartNotAllowedException を確認します。