Iniciar um serviço em primeiro plano

Há duas etapas para iniciar um serviço em primeiro plano no app. Primeiro, você deve iniciar o serviço chamando context.startForegroundService(). Em seguida, faça com que o serviço chame ServiceCompat.startForeground() para se promover a um serviço em primeiro plano.

Pré-requisitos

Dependendo do nível da API a que o app é direcionado, há algumas restrições sobre quando um app pode iniciar um serviço em primeiro plano.

Iniciar um serviço

Para iniciar um serviço em primeiro plano, primeiro inicie-o como um serviço comum (não em primeiro plano):

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);

Pontos principais sobre o código

  • O snippet de código inicia um serviço. No entanto, o serviço ainda não está em execução em primeiro plano. Dentro do próprio serviço, é necessário chamar ServiceCompat.startForeground() para promover o serviço a um serviço em primeiro plano.

Promover um serviço para o primeiro plano

Depois que um serviço estiver em execução, chame ServiceCompat.startForeground() para solicitar que o serviço seja executado em primeiro plano. Normalmente, você chamaria esse método no método onStartCommand() do serviço.

ServiceCompat.startForeground() usa os seguintes parâmetros:

Os tipos de serviço em primeiro plano transmitidos para startForeground() precisam ser declarados no manifesto, dependendo do caso de uso específico. Em seguida, se você precisar adicionar mais tipos de serviço, chame startForeground() novamente.

Por exemplo, suponha que um app de fitness execute um serviço de monitoramento de corrida que sempre precisa de informações de location, mas pode ou não precisar reproduzir mídia. Você precisaria declarar location e mediaPlayback no manifesto. Se um usuário iniciar uma corrida e quiser apenas que a localização seja rastreada, o app precisará chamar startForeground() e transmitir apenas a permissão ACCESS_FINE_LOCATION. Em seguida, se o usuário quiser começar a reproduzir áudio, chame startForeground() novamente e transmita a combinação bit a bit de todos os tipos de serviço em primeiro plano (nesse caso, ACCESS_FINE_LOCATION|FOREGROUND_SERVICE_MEDIA_PLAYBACK).

O exemplo a seguir mostra o código que um serviço de câmera usaria para se promover a um serviço em primeiro plano:

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)
            }
            // ...
        }
    }

    //...
}

Pontos principais sobre o código

  • O app já declarou no manifesto que precisa da permissão CAMERA. No entanto, o app também precisa verificar no momento da execução para garantir que o usuário concedeu essa permissão. Se o app não tiver as permissões corretas, ele precisará informar o usuário sobre o problema.
  • Diferentes tipos de serviços em primeiro plano foram introduzidos com diferentes versões da plataforma Android. Esse código verifica qual versão do Android está em execução e solicita as permissões adequadas.
  • O código verifica ForegroundServiceStartNotAllowedException caso esteja tentando iniciar um serviço em primeiro plano em uma situação não permitida (por exemplo, se estiver tentando promover o serviço para o primeiro plano enquanto o app está em segundo plano).