Iniciar um serviço em primeiro plano

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

Pré-requisitos

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

  • Apps direcionados ao Android 12 (nível 31 da API) ou mais recentes não podem iniciar um serviço em primeiro plano enquanto o app está em segundo plano, com algumas exceções específicas. Para mais informações e informações sobre as exceções a essa regra, consulte Restrições para iniciar um serviço em primeiro plano do segundo plano.

  • Os apps destinados ao Android 14 (nível 34 da API) ou mais recentes precisam solicitar as permissões adequadas para o tipo de serviço em primeiro plano. Quando o app tenta promover um serviço para o primeiro plano, o sistema verifica as permissões adequadas e gera SecurityException se o app estiver sem nenhuma. Por exemplo, se você tentar iniciar um serviço em primeiro plano do tipo location, o sistema vai verificar se o app já tem a permissão ACCESS_COARSE_LOCATION ou ACCESS_FINE_LOCATION. A documentação do tipo de serviço em primeiro plano lista os pré-requisitos necessários para cada tipo de serviço em primeiro plano.

Iniciar um serviço

Para iniciar um serviço em primeiro plano, primeiro você precisa iniciá-lo 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. No 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

Quando um serviço está em execução, é necessário chamar ServiceCompat.startForeground() para solicitar que ele 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:

  • O serviço.
  • Um número inteiro positivo que identifica exclusivamente a notificação do serviço na barra de status.
  • O próprio objeto Notification.
  • Os tipos de serviço em primeiro plano que identificam o trabalho realizado pelo serviço

Os tipos de serviço em primeiro plano que você transmite para startForeground() tipos 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 condicionamento físico execute um serviço de rastreamento de corrida que sempre precisa de informações location, mas pode ou não precisar reproduzir mídia. Você precisa declarar location e mediaPlayback no manifesto. Se um usuário iniciar uma corrida e quiser apenas que o local seja rastreado, 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 de bits de todos os tipos de serviço em primeiro plano (neste 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 precisa informar o problema ao usuário.
  • Diferentes tipos de serviços em primeiro plano foram introduzidos com diferentes versões da plataforma Android. Esse código verifica em qual versão do Android ele está executando e solicita as permissões adequadas.
  • O código verifica ForegroundServiceStartNotAllowedException caso ele esteja tentando iniciar um serviço em primeiro plano em uma situação que não é permitida (por exemplo, se ele estiver tentando promover o serviço para o primeiro plano enquanto o app está em segundo plano).