Panoramica dei servizi

Un Service è un componente dell'applicazione che può eseguire operazioni a lunga esecuzione in background. Non fornisce un'interfaccia utente. Una volta avviato, un servizio potrebbe continuare a funzionare per un po' di tempo, anche dopo che l'utente è passato a un'altra applicazione. Inoltre, un componente può associarsi a un servizio per interagire con esso e persino eseguire comunicazioni interprocesso (IPC). Ad esempio, un servizio può gestire transazioni di rete, riprodurre musica, eseguire I/O di file o interagire con un fornitore di contenuti, il tutto in background.

Attenzione: un servizio viene eseguito nel thread principale del processo di hosting. Il servizio non crea il proprio thread e non viene eseguito in un processo separato, a meno che non specifichi diversamente. Per evitare errori di tipo App not responding (ANR), devi eseguire eventuali operazioni di blocco su un thread separato all'interno del servizio.

Tipi di servizi

Ecco i tre diversi tipi di servizi:

Primo piano

Un servizio in primo piano esegue un'operazione visibile all'utente. Ad esempio, un'app audio utilizzerebbe un servizio in primo piano per riprodurre una traccia audio. I servizi in primo piano devono mostrare una notifica. I servizi in primo piano continuano a funzionare anche quando l'utente non interagisce con l'app.

Quando utilizzi un servizio in primo piano, devi mostrare una notifica in modo che gli utenti siano consapevoli dell'esecuzione del servizio. Questa notifica non può essere chiusa a meno che il servizio non venga interrotto o rimosso dall'attività in primo piano.

Scopri di più su come configurare i servizi in primo piano nella tua app.

Nota: l'API WorkManager offre un modo flessibile per pianificare le attività ed è in grado di eseguire questi job come servizi in primo piano, se necessario. In molti casi, è preferibile utilizzare WorkManager rispetto all'utilizzo diretto dei servizi in primo piano.

Sfondo
Un servizio in background esegue un'operazione che non viene notata direttamente dall'utente. Ad esempio, se un'app utilizza un servizio per comprimere lo spazio di archiviazione, solitamente si tratta di un servizio in background.

Nota: se la tua app ha come target il livello API 26 o versioni successive, il sistema impone limitazioni all'esecuzione dei servizi in background quando l'app stessa non è in primo piano. Ad esempio, nella maggior parte dei casi non dovresti accedere alle informazioni sulla posizione in background. Al contrario, pianifica le attività utilizzando WorkManager.

Associato
Un servizio è collegato quando un componente dell'applicazione si associa a questo chiamando bindService(). Un servizio associato offre un'interfaccia client-server che consente ai componenti di interagire con il servizio, inviare richieste, ricevere risultati e persino farlo tra processi con la comunicazione tra processi (IPC). Un servizio associato viene eseguito solo se un altro componente dell'applicazione è associato. Più componenti possono associarsi al servizio contemporaneamente, ma quando tutti si scollegano, il servizio viene distrutto.

Sebbene in questa documentazione in genere i servizi avviati e associati siano trattati separatamente, il tuo servizio può funzionare in entrambi i modi: può essere avviato (per essere eseguito a tempo indeterminato) e anche consentire l'associazione. Devi solo implementare un paio di metodi di callback: onStartCommand() per consentire ai componenti di avviarlo e onBind() per consentire il binding.

Indipendentemente dal fatto che il servizio sia avviato, associato o entrambi, qualsiasi componente dell'applicazione può utilizzare il servizio (anche da un'applicazione separata) nello stesso modo in cui qualsiasi componente può utilizzare un'attività, avviandola con un Intent. Tuttavia, puoi dichiarare il servizio come privato nel file manifest e bloccare l'accesso da altre applicazioni. Scopri di più nella sezione relativa alla dichiarazione del servizio nel manifest.

Scelta tra un servizio e un thread

Un servizio è semplicemente un componente che può essere eseguito in background, anche quando l'utente non interagisce con la tua applicazione, quindi devi creare un servizio solo se è ciò di cui hai bisogno.

Se devi eseguire operazioni al di fuori del thread principale, ma solo mentre l'utente interagisce con la tua applicazione, devi creare un nuovo thread nel contesto di un altro componente dell'applicazione. Ad esempio, se vuoi riprodurre della musica, ma solo mentre la tua attività è in esecuzione, puoi creare un thread in onCreate(), avviarlo in onStart() e interromperlo in onStop(). Valuta anche la possibilità di utilizzare pool di thread ed executor del pacchetto java.util.concurrent o le coroutine Kotlin anziché la tradizionale classe Thread. Per ulteriori informazioni su come spostare l'esecuzione nei thread in background, consulta il documento Threading su Android.

Ricorda che se utilizzi un servizio, questo viene comunque eseguito nel thread principale dell'applicazione per impostazione predefinita, quindi devi comunque creare un nuovo thread all'interno del servizio se esegue operazioni intensive o bloccanti.

Nozioni di base

Per creare un servizio, devi creare una sottoclasse di Service o utilizzare una delle sue sottoclassi esistenti. Nell'implementazione, devi sostituire alcuni metodi di callback che gestiscono aspetti chiave del ciclo di vita del servizio e forniscono un meccanismo che consenta ai componenti di eseguire il binding al servizio, se opportuno. Questi sono i metodi di callback più importanti che devi override:

onStartCommand()
Il sistema invoca questo metodo chiamando startService() quando un altro componente (ad esempio un'attività) richiede l'avvio del servizio. Quando viene eseguito questo metodo, il servizio viene avviato e può essere eseguito in background a tempo indeterminato. Se implementi questa funzionalità, è tua responsabilità interrompere il servizio al termine del lavoro chiamando stopSelf() o stopService(). Se vuoi solo fornire il binding, non è necessario implementare questo metodo.
onBind()
Il sistema invoca questo metodo chiamando bindService() quando un altro componente vuole eseguire il binding con il servizio (ad esempio per eseguire l'RPC). Nell'implementazione di questo metodo, devi fornire un'interfaccia che i client utilizzino per comunicare con il servizio restituendo un IBinder. Devi sempre implementare questo metodo. Tuttavia, se non vuoi consentire il binding, devi restituire null.
onCreate()
Il sistema richiama questo metodo per eseguire procedure di configurazione una tantum quando il servizio viene creato inizialmente (prima di chiamare onStartCommand() o onBind()). Se il servizio è già in esecuzione, questo metodo non viene chiamato.
onDestroy()
Il sistema invoca questo metodo quando il servizio non viene più utilizzato e viene distrutto. Il servizio deve implementare questa funzionalità per ripulire eventuali risorse come thread, ascoltatori registrati o ricevitori. Questa è l'ultima chiamata ricevuta dal servizio.

Se un componente avvia il servizio chiamando startService() (che comporta una chiamata a onStartCommand()), il servizio continua a funzionare finché non si arresta autonomamente con stopSelf() o un altro componente lo interrompe chiamando stopService().

Se un componente chiama bindService() per creare il servizio e onStartCommand() non viene chiamato, il servizio viene eseguito solo finché il componente è associato. Una volta scollegato da tutti i client, il sistema lo distrugge.

Il sistema Android interrompe un servizio solo quando la memoria è in esaurimento e deve recuperare le risorse di sistema per l'attività in primo piano. Se il servizio è associato a un'attività che ha il focus dell'utente, è meno probabile che venga interrotto; se il servizio è dichiarato in esecuzione in primo piano, viene interrotto raramente. Se il servizio è avviato ed è di lunga durata, il sistema ne abbassa la posizione nel corso del tempo nell'elenco delle attività in background e il servizio diventa molto vulnerabile al suo interruzione. Se il servizio è avviato, devi progettarlo in modo da gestire in modo corretto i riavvii da parte del sistema. Se il sistema termina il servizio, lo riavvia non appena le risorse diventano disponibili, ma questo dipende anche dal valore restituito da onStartCommand(). Per ulteriori informazioni su quando il sistema potrebbe distruggere un servizio, consulta la documentazione su Processi e threading.

Nelle sezioni seguenti scoprirai come creare i metodi di servizio startService() e bindService() e come utilizzarli da altri componenti dell'applicazione.

Dichiarazione di un servizio nel manifest

Devi dichiarare tutti i servizi nel file manifest della tua applicazione, come fai per le attività e gli altri componenti.

Per dichiarare il servizio, aggiungi un elemento <service> come elemento secondario dell'elemento <application>. Ecco un esempio:

<manifest ... >
  ...
  <application ... >
      <service android:name=".ExampleService" />
      ...
  </application>
</manifest>

Consulta la documentazione di riferimento dell'elemento <service> per ulteriori informazioni sulla dichiarazione del servizio nel file manifest.

Esistono altri attributi che puoi includere nell'elemento <service> per definire proprietà come le autorizzazioni necessarie per avviare il servizio e il processo in cui deve essere eseguito. L'attributo android:name è l'unico obbligatorio: specifica il nome della classe del servizio. Dopo aver pubblicato l'applicazione, lascia invariato questo nome per evitare il rischio di interrompere il codice a causa della dipendenza da intent espliciti per avviare o associare il servizio (leggi il post del blog Elementi che non possono cambiare).

Attenzione: per garantire la sicurezza della tua app, utilizza sempre un'intent esplicita quando avvii un Service e non dichiarare filtri intent per i tuoi servizi. L'utilizzo di un'intenzione implicita per avviare un servizio rappresenta un rischio per la sicurezza perché non puoi essere certo del servizio che risponde all'intenzione e l'utente non può vedere quale servizio viene avviato. A partire da Android 5.0 (livello API 21), il sistema genera un'eccezione se chiami bindService() con un'intent implicita.

Puoi assicurarti che il tuo servizio sia disponibile solo per la tua app includendo l'attributo android:exported e impostandolo su false. In questo modo, le altre app non possono avviare il servizio, anche se viene utilizzato un intent esplicito.

Nota: gli utenti possono vedere quali servizi sono in esecuzione sul loro dispositivo. Se vede un servizio che non riconosce o non considera attendibile, può interromperlo. Per evitare che il servizio venga interrotto accidentalmente dagli utenti, devi aggiungere l'attributo android:description all'elemento <service> nel file manifest dell'app. Nella descrizione, fornisci una breve frase che spieghi cosa fa il servizio e quali vantaggi offre.

Creazione di un servizio avviato

Un servizio avviato è quello che viene avviato da un altro componente chiamando startService(), il che comporta una chiamata al metodo onStartCommand() del servizio.

Quando un servizio viene avviato, ha un ciclo di vita indipendente dal componente che lo ha avviato. Il servizio può essere eseguito in background a tempo indeterminato, anche se il componente che lo ha avviato viene distrutto. Di conseguenza, il servizio dovrebbe interrompersi al termine del suo compito chiamando stopSelf() oppure un altro componente può interromperlo chiamando stopService().

Un componente dell'applicazione, ad esempio un'attività, può avviare il servizio chiamando startService() e passando un Intent che specifica il servizio e include tutti i dati da utilizzare. Il servizio riceve questo Intent nel metodo onStartCommand().

Ad esempio, supponiamo che un'attività debba salvare alcuni dati in un database online. L'attività può avviare un servizio complementare e inviargli i dati da salvare passando un'intent a startService(). Il servizio riceve l'intent in onStartCommand(), si connette a internet ed esegue la transazione di database. Al termine della transazione, il servizio si arresta e viene distrutto.

Attenzione: per impostazione predefinita, un servizio viene eseguito nello stesso processo dell'applicazione in cui è dichiarato e nel thread principale di quell'applicazione. Se il servizio svolge operazioni intensive o di blocco mentre l'utente interagisce con un'attività della stessa applicazione, il servizio rallenta il rendimento dell'attività. Per evitare di influire sul rendimento dell'applicazione, avvia un nuovo thread all'interno del servizio.

La classe Service è la classe di base per tutti i servizi. Quando estendi questa classe, è importante creare un nuovo thread in cui il servizio possa completare tutto il proprio lavoro. Per impostazione predefinita, il servizio utilizza il thread principale dell'applicazione, il che può rallentare le prestazioni di qualsiasi attività in esecuzione nell'applicazione.

Il framework Android fornisce anche la sottoclasse IntentService di Service che utilizza un thread di lavoro per gestire tutte le richieste di avvio, una alla volta. L'utilizzo di questa classe è sconsigliato per le nuove app perché non funzionerà bene a partire da Android 8 Oreo, a causa dell'introduzione dei limiti di esecuzione in background. Inoltre, è deprecato a partire da Android 11. Puoi utilizzare JobIntentService come sostituzione di IntentService ed è compatibile con le versioni più recenti di Android.

Le sezioni seguenti descrivono come implementare il tuo servizio personalizzato, ma ti consigliamo vivamente di utilizzare WorkManager per la maggior parte dei casi d'uso. Consulta la guida all'elaborazione in background su Android per scoprire se esiste una soluzione adatta alle tue esigenze.

Estensione della classe Service

Puoi estendere la classe Service per gestire ogni intent in arrivo. Ecco come potrebbe essere un'implementazione di base:

Kotlin

class HelloService : Service() {

    private var serviceLooper: Looper? = null
    private var serviceHandler: ServiceHandler? = null

    // Handler that receives messages from the thread
    private inner class ServiceHandler(looper: Looper) : Handler(looper) {

        override fun handleMessage(msg: Message) {
            // Normally we would do some work here, like download a file.
            // For our sample, we just sleep for 5 seconds.
            try {
                Thread.sleep(5000)
            } catch (e: InterruptedException) {
                // Restore interrupt status.
                Thread.currentThread().interrupt()
            }

            // Stop the service using the startId, so that we don't stop
            // the service in the middle of handling another job
            stopSelf(msg.arg1)
        }
    }

    override fun onCreate() {
        // Start up the thread running the service.  Note that we create a
        // separate thread because the service normally runs in the process's
        // main thread, which we don't want to block.  We also make it
        // background priority so CPU-intensive work will not disrupt our UI.
        HandlerThread("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND).apply {
            start()

            // Get the HandlerThread's Looper and use it for our Handler
            serviceLooper = looper
            serviceHandler = ServiceHandler(looper)
        }
    }

    override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
        Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show()

        // For each start request, send a message to start a job and deliver the
        // start ID so we know which request we're stopping when we finish the job
        serviceHandler?.obtainMessage()?.also { msg ->
            msg.arg1 = startId
            serviceHandler?.sendMessage(msg)
        }

        // If we get killed, after returning from here, restart
        return START_STICKY
    }

    override fun onBind(intent: Intent): IBinder? {
        // We don't provide binding, so return null
        return null
    }

    override fun onDestroy() {
        Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show()
    }
}

Java

public class HelloService extends Service {
  private Looper serviceLooper;
  private ServiceHandler serviceHandler;

  // Handler that receives messages from the thread
  private final class ServiceHandler extends Handler {
      public ServiceHandler(Looper looper) {
          super(looper);
      }
      @Override
      public void handleMessage(Message msg) {
          // Normally we would do some work here, like download a file.
          // For our sample, we just sleep for 5 seconds.
          try {
              Thread.sleep(5000);
          } catch (InterruptedException e) {
              // Restore interrupt status.
              Thread.currentThread().interrupt();
          }
          // Stop the service using the startId, so that we don't stop
          // the service in the middle of handling another job
          stopSelf(msg.arg1);
      }
  }

  @Override
  public void onCreate() {
    // Start up the thread running the service. Note that we create a
    // separate thread because the service normally runs in the process's
    // main thread, which we don't want to block. We also make it
    // background priority so CPU-intensive work doesn't disrupt our UI.
    HandlerThread thread = new HandlerThread("ServiceStartArguments",
            Process.THREAD_PRIORITY_BACKGROUND);
    thread.start();

    // Get the HandlerThread's Looper and use it for our Handler
    serviceLooper = thread.getLooper();
    serviceHandler = new ServiceHandler(serviceLooper);
  }

  @Override
  public int onStartCommand(Intent intent, int flags, int startId) {
      Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();

      // For each start request, send a message to start a job and deliver the
      // start ID so we know which request we're stopping when we finish the job
      Message msg = serviceHandler.obtainMessage();
      msg.arg1 = startId;
      serviceHandler.sendMessage(msg);

      // If we get killed, after returning from here, restart
      return START_STICKY;
  }

  @Override
  public IBinder onBind(Intent intent) {
      // We don't provide binding, so return null
      return null;
  }

  @Override
  public void onDestroy() {
    Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
  }
}

Il codice di esempio gestisce tutte le chiamate in arrivo in onStartCommand() e pubblica il lavoro in un Handler in esecuzione in un thread in background. Funziona esattamente come un IntentService ed elabora tutte le richieste in sequenza, una dopo l'altra. Ad esempio, se vuoi eseguire più richieste contemporaneamente, puoi modificare il codice in modo da eseguire il lavoro in un pool di thread.

Tieni presente che il metodo onStartCommand() deve restituire un intero. L'intero è un valore che descrive in che modo il sistema deve continuare il servizio nel caso in cui lo uccida. Il valore restituito da onStartCommand() deve essere una delle seguenti costanti:

START_NOT_STICKY
Se il sistema interrompe il servizio dopo il ritorno di onStartCommand(), non ricrearlo a meno che non ci siano intenti in attesa di invio. Questa è l'opzione più sicura per evitare di eseguire il servizio quando non è necessario e quando l'applicazione può semplicemente riavviare i job incompiuti.
START_STICKY
Se il sistema termina il servizio dopo il ritorno di onStartCommand(), ricrea il servizio e chiama onStartCommand(), ma non inviare nuovamente l'ultimo intent. Il sistema chiama onStartCommand() con un intento nullo, a meno che non ci siano intenti in attesa per avviare il servizio. In questo caso, gli intent vengono pubblicati. È adatto per lettori multimediali (o servizi simili) che non eseguono comandi, ma sono in esecuzione a tempo indeterminato e in attesa di un job.
START_REDELIVER_INTENT
Se il sistema termina il servizio dopo il ritorno di onStartCommand(), ricrea il servizio e chiama onStartCommand() con l'ultimo intento inviato al servizio. Eventuali intent in attesa vengono pubblicati a loro volta. È adatto per i servizi che stanno esecutando attivamente un job che deve essere ripreso immediatamente, ad esempio il download di un file.

Per ulteriori dettagli su questi valori restituiti, consulta la documentazione di riferimento collegata per ogni costante.

Avvio di un servizio

Puoi avviare un servizio da un'attività o da un altro componente dell'applicazione passando un Intent a startService() o startForegroundService(). Il sistema Android chiama il metodo onStartCommand() del servizio e gli passa Intent, che specifica quale servizio avviare.

Nota: se la tua app ha come target il livello API 26 o versioni successive, il sistema impone limitazioni all'utilizzo o alla creazione di servizi in background, a meno che l'app stessa non sia in primo piano. Se un'app deve creare un servizio in primo piano, deve chiamare startForegroundService(). Questo metodo crea un servizio in background, ma il metodo indica al sistema che il servizio si promuoverà in primo piano. Una volta creato, il servizio deve chiamare il suo metodo startForeground() entro cinque secondi.

Ad esempio, un'attività può avviare il servizio di esempio nella sezione precedente (HelloService) utilizzando un'intent esplicita con startService(), come mostrato di seguito:

Kotlin

startService(Intent(this, HelloService::class.java))

Java

startService(new Intent(this, HelloService.class));

Il metodo startService() restituisce immediatamente il valore e il sistema Android chiama il metodo onStartCommand() del servizio. Se il servizio non è già in esecuzione, il sistema chiama prima onCreate() e poi chiama onStartCommand().

Se il servizio non fornisce anche il binding, l'intent fornito con startService() è l'unica modalità di comunicazione tra il componente dell'applicazione e il servizio. Tuttavia, se vuoi che il servizio invii un risultato, il client che avvia il servizio può creare un PendingIntent per una trasmissione (con getBroadcast()) e inviarlo al servizio nell'Intent che avvia il servizio. Il servizio può quindi utilizzare la trasmissione per fornire un risultato.

Più richieste di avvio del servizio generano più chiamate corrispondenti al onStartCommand() del servizio. Tuttavia, per interrompere il servizio è necessaria una sola richiesta (con stopSelf() o stopService()).

Interrompere un servizio

Un servizio avviato deve gestire il proprio ciclo di vita. In altre parole, il sistema non interrompe o distrugge il servizio, a meno che non debba recuperare la memoria di sistema e il servizio continui a funzionare dopo il ritorno di onStartCommand(). Il servizio deve interrompersi chiamando stopSelf() oppure un altro componente può interromperlo chiamando stopService().

Una volta richiesta l'interruzione con stopSelf() o stopService(), il sistema elimina il servizio il prima possibile.

Se il servizio gestisce più richieste a onStartCommand() contemporaneamente, non devi interromperlo al termine dell'elaborazione di una richiesta di avvio, in quanto potresti aver ricevuto una nuova richiesta di avvio (l'interruzione alla fine della prima richiesta terminerebbe la seconda). Per evitare questo problema, puoi utilizzare stopSelf(int) per assicurarti che la richiesta di interruzione del servizio si basi sempre sulla richiesta di avvio più recente. In altre parole, quando chiami stopSelf(int), passi l'ID della richiesta di inizio (il startId fornito a onStartCommand()) a cui corrisponde la richiesta di interruzione. Se il servizio riceve una nuova richiesta di avvio prima che tu possa chiamare stopSelf(int), l'ID non corrisponde e il servizio non si arresta.

Attenzione: per evitare di sprecare risorse di sistema e di consumare la batteria, assicurati che l'applicazione interrompa i servizi al termine del lavoro. Se necessario, altri componenti possono interrompere il servizio chiamando stopService(). Anche se attivi il binding per il servizio, devi sempre interrompere il servizio se riceve una chiamata a onStartCommand().

Per ulteriori informazioni sul ciclo di vita di un servizio, consulta la sezione di seguito relativa alla gestione del ciclo di vita di un servizio.

Creazione di un servizio associato

Un servizio associato è un servizio che consente ai componenti dell'applicazione di associarsi chiamando bindService() per creare una connessione di lunga durata. In genere, non consente ai componenti di avviarlo chiamando startService().

Crea un servizio associato quando vuoi interagire con il servizio dalle attività e da altri componenti della tua applicazione o per esporre alcune funzionalità della tua applicazione a altre applicazioni tramite la comunicazione interprocesso (IPC).

Per creare un servizio associato, implementa il metodo di callback onBind() per restituire un IBinder che definisce l'interfaccia per la comunicazione con il servizio. Altri componenti dell'applicazione possono quindi chiamare bindService() per recuperare l'interfaccia e iniziare a chiamare i metodi del servizio. Il servizio esiste solo per servire il componente dell'applicazione associato, quindi quando non sono presenti componenti associati al servizio, il sistema lo distrugge. Non è necessario interrompere un servizio associato nello stesso modo in cui devi farlo quando il servizio viene avviato tramite onStartCommand().

Per creare un servizio associato, devi definire l'interfaccia che specifica in che modo un client può comunicare con il servizio. Questa interfaccia tra il servizio e un client deve essere un'implementazione di IBinder ed è ciò che il servizio deve restituire dal metodo di callback onBind(). Dopo aver ricevuto il token IBinder, il client può iniziare a interagire con il servizio tramite l'interfaccia.

Più client possono eseguire il binding al servizio contemporaneamente. Quando un client ha terminato di interagire con il servizio, chiama unbindService() per annullare l'associazione. Se non sono presenti client associati al servizio, il sistema lo elimina.

Esistono diversi modi per implementare un servizio associato e l'implementazione è più complicata rispetto a un servizio avviato. Per questi motivi, la discussione sui servizi associati viene visualizzata in un documento separato sui servizi associati.

Invio di notifiche all'utente

Quando un servizio è in esecuzione, può notificare gli eventi all'utente utilizzando le notifiche snackbar o le notifiche della barra di stato.

Una notifica nella barra di app è un messaggio che viene visualizzato sulla superficie della finestra corrente solo per un breve istante prima di scomparire. Una notifica nella barra di stato fornisce un'icona con un messaggio, che l'utente può selezionare per eseguire un'azione (ad esempio avviare un'attività).

In genere, una notifica nella barra di stato è la tecnica migliore da utilizzare quando un'operazione in background, come il download di un file, è stata completata e l'utente può ora intervenire. Quando l'utente seleziona la notifica dalla visualizzazione espansa, la notifica può avviare un'attività (ad esempio per visualizzare il file scaricato).

Gestione del ciclo di vita di un servizio

Il ciclo di vita di un servizio è molto più semplice di quello di un'attività. Tuttavia, è ancora più importante prestare molta attenzione a come viene creato e distrutto il servizio, perché un servizio può essere eseguito in background senza che l'utente lo sappia.

Il ciclo di vita del servizio, dalla creazione alla distruzione, può seguire uno di questi due percorsi:

  • Un servizio avviato

    Il servizio viene creato quando un altro componente chiama startService(). Il servizio viene quindi eseguito a tempo indeterminato e deve interrompersi chiamando stopSelf(). Anche un altro componente può interrompere il servizio chiamando stopService(). Quando il servizio viene interrotto, il sistema lo elimina.

  • Un servizio associato

    Il servizio viene creato quando un altro componente (un client) chiama bindService(). Il client comunica quindi con il servizio tramite un'interfaccia IBinder. Il client può chiudere la connessione chiamando unbindService(). Più client possono eseguire il binding allo stesso servizio e quando tutti vengono sganciati, il sistema distrugge il servizio. Il servizio non deve arrestarsi.

Questi due percorsi non sono completamente separati. Puoi eseguire il binding a un servizio già avviato con startService(). Ad esempio, puoi avviare un servizio di musica di sottofondo chiamando startService() con un Intent che identifica la musica da riprodurre. In un secondo momento, possibly quando l'utente vuole esercitare un certo controllo sul player o ottenere informazioni sul brano in riproduzione, un'attività può associarsi al servizio chiamando bindService(). In questi casi, stopService() o stopSelf() non interrompe effettivamente il servizio finché non viene eseguito lo scollegamento di tutti i client.

Implementazione dei callback del ciclo di vita

Come un'attività, un servizio dispone di metodi di callback del ciclo di vita che puoi implementare per monitorare le modifiche dello stato del servizio ed eseguire operazioni nei momenti opportuni. Il seguente servizio di scheletro mostra ciascuno dei metodi del ciclo di vita:

Kotlin

class ExampleService : Service() {
    private var startMode: Int = 0             // indicates how to behave if the service is killed
    private var binder: IBinder? = null        // interface for clients that bind
    private var allowRebind: Boolean = false   // indicates whether onRebind should be used

    override fun onCreate() {
        // The service is being created
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        // The service is starting, due to a call to startService()
        return startMode
    }

    override fun onBind(intent: Intent): IBinder? {
        // A client is binding to the service with bindService()
        return binder
    }

    override fun onUnbind(intent: Intent): Boolean {
        // All clients have unbound with unbindService()
        return allowRebind
    }

    override fun onRebind(intent: Intent) {
        // A client is binding to the service with bindService(),
        // after onUnbind() has already been called
    }

    override fun onDestroy() {
        // The service is no longer used and is being destroyed
    }
}

Java

public class ExampleService extends Service {
    int startMode;       // indicates how to behave if the service is killed
    IBinder binder;      // interface for clients that bind
    boolean allowRebind; // indicates whether onRebind should be used

    @Override
    public void onCreate() {
        // The service is being created
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // The service is starting, due to a call to startService()
        return startMode;
    }
    @Override
    public IBinder onBind(Intent intent) {
        // A client is binding to the service with bindService()
        return binder;
    }
    @Override
    public boolean onUnbind(Intent intent) {
        // All clients have unbound with unbindService()
        return allowRebind;
    }
    @Override
    public void onRebind(Intent intent) {
        // A client is binding to the service with bindService(),
        // after onUnbind() has already been called
    }
    @Override
    public void onDestroy() {
        // The service is no longer used and is being destroyed
    }
}

Nota: a differenza dei metodi di callback del ciclo di vita dell'attività, non è necessario chiamare l'implementazione della superclasse di questi metodi di callback.

Figura 2. Il ciclo di vita del servizio. Il diagramma a sinistra mostra il ciclo di vita quando il servizio viene creato con startService(), mentre quello a destra mostra il ciclo di vita quando il servizio viene creato con bindService().

La Figura 2 illustra i metodi di callback tipici per un servizio. Sebbene la figura separi i servizi creati da startService() da quelli creati da bindService(), tieni presente che qualsiasi servizio, indipendentemente da come viene avviato, può potenzialmente consentire ai client di associarsi. Un servizio avviato inizialmente con onStartCommand() (da un cliente che chiama startService()) può comunque ricevere una chiamata a onBind() (quando un cliente chiama bindService()).

Implementando questi metodi, puoi monitorare questi due cicli nidificati del ciclo di vita del servizio:

  • L'intero ciclo di vita di un servizio avviene tra il momento in cui viene chiamato onCreate() e il momento in cui viene restituito onDestroy(). Come un'attività, un servizio esegue la configurazione iniziale in onCreate() e rilascia tutte le risorse rimanenti in onDestroy(). Ad esempio, un servizio di riproduzione di musica può creare il thread in cui viene riprodotta la musica in onCreate() e poi interromperlo in onDestroy().

    Nota: i metodi onCreate() e onDestroy() vengono chiamati per tutti i servizi, che siano creati da startService() o bindService().

  • Il ciclo di vita attivo di un servizio inizia con una chiamata a onStartCommand() o onBind(). A ogni metodo viene passato il valore Intent passato a startService() o bindService().

    Se il servizio è avviato, il lifetime attivo termina contemporaneamente all'intero lifetime (il servizio è ancora attivo anche dopo il ritorno di onStartCommand()). Se il servizio è associato, la durata attiva termina quando viene restituito onUnbind().

Nota: anche se un servizio avviato viene interrotto da una chiamata a stopSelf() o stopService(), non esiste un rispettivo callback per il servizio (non esiste un callback onStop()). A meno che il servizio non sia associato a un client, il sistema lo distrugge quando il servizio viene interrotto. onDestroy() è l'unico callback ricevuto.

Per ulteriori informazioni sulla creazione di un servizio che fornisce il binding, consulta il documento Servizi associati, che include ulteriori informazioni sul metodo di callback onRebind() nella sezione Gestire il ciclo di vita di un servizio associato.