Avviare un servizio in primo piano

Per avviare un servizio in primo piano dalla tua app sono necessari due passaggi. Innanzitutto, devi avviare il servizio chiamando context.startForegroundService(). Quindi, fai in modo che il servizio chiami ServiceCompat.startForeground() per promuoversi come servizio in primo piano.

Prerequisiti

A seconda del livello API a cui è destinata la tua app, esistono alcune limitazioni su quando un'app può avviare un servizio in primo piano.

  • Le app che hanno come target Android 12 (livello API 31) o versioni successive non possono avviare un servizio in primo piano mentre l'app è in background, con alcune eccezioni specifiche. Per ulteriori informazioni e sulle eccezioni a questa regola, consulta Limitazioni per l'avvio di un servizio in primo piano da un'app in background.

  • Le app che hanno come target Android 14 (livello API 34) o versioni successive devono richiedere le autorizzazioni appropriate per il tipo di servizio in primo piano. Quando l'app tenta di promuovere un servizio in primo piano, il sistema controlla le autorizzazioni appropriate e lancia SecurityException se all'app ne mancano. Ad esempio, se provi a avviare un servizio in primo piano di tipo location, il sistema verifica che la tua app disponga già dell'autorizzazione ACCESS_COARSE_LOCATION o ACCESS_FINE_LOCATION. La documentazione del tipo di servizio in primo piano elenca i prerequisiti obbligatori per ogni tipo di servizio in primo piano.

Avviare un servizio

Per avviare un servizio in primo piano, devi prima avviarlo come servizio normale (non in primo piano):

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

Punti chiave del codice

  • Lo snippet di codice avvia un servizio. Tuttavia, il servizio non è ancora in esecuzione in primo piano. All'interno del servizio stesso, devi chiamare ServiceCompat.startForeground() per promuovere il servizio in un servizio in primo piano.

Promuovere un servizio in primo piano

Una volta avviato un servizio, devi chiamare ServiceCompat.startForeground() per richiederne l'esecuzione in primo piano. Di solito, questo metodo viene chiamato nel metodo onStartCommand() del servizio.

ServiceCompat.startForeground() accetta i seguenti parametri:

  • Il servizio.
  • Un numero intero positivo che identifica in modo univoco la notifica del servizio nella barra di stato.
  • L'oggetto Notification stesso.
  • I tipi di servizi in primo piano che identificano il lavoro svolto dal servizio

I tipi di servizi in primo piano che passi ai startForeground() tipi dichiarati nel manifest, a seconda del caso d'uso specifico. Se hai bisogno di aggiungere altri tipi di servizi, puoi chiamare nuovamentestartForeground().

Ad esempio, supponiamo che un'app per il fitness esegua un servizio di monitoraggio della corsa che richiede sempre informazioni location, ma potrebbe o meno dover riprodurre contenuti multimediali. Devi dichiarare sia location che mediaPlayback nel file manifest. Se un utente avvia una corsa e vuole solo che la sua posizione venga monitorata, la tua app deve chiamare startForeground() e passare solo l'autorizzazione ACCESS_FINE_LOCATION. Poi, se l'utente vuole avviare la riproduzione audio, chiama di nuovo startForeground() e passa la combinazione bitwise di tutti i tipi di servizi in primo piano (in questo caso, ACCESS_FINE_LOCATION|FOREGROUND_SERVICE_MEDIA_PLAYBACK).

L'esempio seguente mostra il codice che un servizio della fotocamera utilizzerebbe per promuovere se stesso a un servizio in primo piano:

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

    //...
}

Punti chiave del codice

  • L'app ha già dichiarato nel file manifest di aver bisogno dell'autorizzazione CAMERA. Tuttavia, l'app deve anche verificare in fase di esecuzione che l'utente abbia concesso l'autorizzazione. Se l'app non dispone effettivamente delle autorizzazioni corrette, dovrebbe informare l'utente del problema.
  • Diversi tipi di servizi in primo piano sono stati introdotti con versioni diverse della piattaforma Android. Questo codice controlla su quale versione di Android è in esecuzione e richiede le autorizzazioni appropriate.
  • Il codice controlla la presenza di ForegroundServiceStartNotAllowedException nel caso in cui stia tentando di avviare un servizio in primo piano in una situazione non consentita (ad esempio, se sta tentando di promuovere il servizio in primo piano mentre l'app è in background).