Panoramica dei servizi

Un Service è un componente dell'applicazione in grado di eseguire operazioni a lunga esecuzione in background. Non fornisce un'interfaccia utente. Una volta avviato, un servizio potrebbe continuare a essere eseguito 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 una comunicazione tra processi (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 proprio processo di hosting; il servizio non crea il proprio thread e non viene eseguito in un processo separato, se non diversamente specificato. Devi eseguire eventuali operazioni di blocco su un thread separato all'interno del servizio per evitare errori L'applicazione non risponde (ANR).

Tipi di servizi

Ecco i tre diversi tipi di servizi:

Primo piano

Un servizio in primo piano esegue un'operazione evidente per l'utente. Ad esempio, un'app audio utilizza 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 essere eseguiti anche quando l'utente non interagisce con l'app.

Quando utilizzi un servizio in primo piano, devi visualizzare una notifica in modo che gli utenti sappiano attivamente che il servizio è in esecuzione. Questa notifica non può essere ignorata, a meno che il servizio non venga interrotto o rimosso dal 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'uso diretto dei servizi in primo piano.

Premessa
Un servizio in background esegue un'operazione non direttamente rilevata dall'utente. Ad esempio, se un'app ha utilizzato un servizio per compattare lo spazio di archiviazione, di solito si tratta di un servizio in background.

Nota: se la tua app ha come target il livello API 26 o successivo, il sistema impone limitazioni sull'esecuzione dei servizi in background quando l'app non è in primo piano. Nella maggior parte dei casi, ad esempio, non dovresti accedere alle informazioni sulla posizione in background. Puoi invece pianificare le attività utilizzando WorkManager.

Associata
Un servizio è associato quando un componente dell'applicazione viene associato 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 attraverso processi con comunicazione tra processi (IPC). Un servizio associato viene eseguito solo finché è associato un altro componente dell'applicazione. L'associazione di più componenti al servizio in una sola volta, ma quando vengono svincolati tutti, il servizio viene eliminato.

Anche se questa documentazione in genere illustra separatamente i servizi avviati e associati, il servizio può funzionare in entrambi i modi: può essere avviato (per l'esecuzione a tempo indeterminato) e consente anche l'associazione. Devi semplicemente implementare un paio di metodi di callback: onStartCommand() per consentire ai componenti di avviarlo e onBind() per consentire l'associazione.

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à, iniziando con Intent. Tuttavia, puoi dichiarare il servizio come privato nel file manifest e bloccare l'accesso da altre applicazioni. Questo argomento viene discusso in modo più approfondito nella sezione relativa alla dichiarazione del servizio nel file 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 dovresti 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 l'applicazione, devi creare un nuovo thread nel contesto di un altro componente dell'applicazione. Ad esempio, se vuoi riprodurre musica, ma solo mentre l'attività è in esecuzione, potresti creare un thread in onCreate(), avviarlo in onStart() e interromperlo in onStop(). Valuta anche l'utilizzo di pool di thread ed esecutori del pacchetto java.util.concurrent o delle coroutine Kotlin anziché della classe tradizionale Thread. Consulta il documento Threading su Android per ulteriori informazioni sullo spostamento dell'esecuzione in thread in background.

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

Nozioni di base

Per creare un servizio, devi creare una sottoclasse di Service o utilizzare una delle sottoclassi esistenti. Nella tua implementazione, devi eseguire l'override di alcuni metodi di callback che gestiscono gli aspetti chiave del ciclo di vita del servizio e fornire un meccanismo che consenta ai componenti di associarsi al servizio, se opportuno. Di seguito sono riportati i metodi di callback più importanti di cui devi eseguire l'override:

onStartCommand()
Il sistema richiama questo metodo chiamando startService() quando un altro componente (ad esempio un'attività) richiede l'avvio del servizio. Quando questo metodo viene eseguito, il servizio viene avviato e può essere eseguito in background a tempo indeterminato. Se implementi questa funzionalità, è tua responsabilità interrompere il servizio una volta completato il suo lavoro chiamando stopSelf() o stopService(). Se vuoi solo fornire l'associazione, non è necessario implementare questo metodo.
onBind()
Il sistema richiama questo metodo chiamando bindService() quando un altro componente vuole associarsi al servizio (ad esempio per eseguire RPC). Nell'implementazione di questo metodo, devi fornire un'interfaccia che i client utilizzano per comunicare con il servizio restituendo un IBinder. Devi sempre implementare questo metodo; tuttavia, se non vuoi consentire l'associazione, devi restituire null.
onCreate()
Il sistema richiama questo metodo per eseguire procedure di configurazione una tantum quando il servizio viene creato all'inizio (prima che chiami onStartCommand() o onBind()). Se il servizio è già in esecuzione, questo metodo non viene chiamato.
onDestroy()
Il sistema richiama questo metodo quando il servizio non è più in uso ed è in fase di eliminazione. Il tuo servizio dovrebbe implementarla per ripulire qualsiasi risorsa come thread, ascoltatori registrati o ricevitori. Questa è l'ultima chiamata ricevuta dal servizio.

Se un componente avvia il servizio chiamando startService() (il che comporta una chiamata a onStartCommand()), il servizio continua a essere eseguito finché non si arresta automaticamente 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 se il componente è associato. Dopo l'annullamento dell'associazione del servizio da tutti i client, il sistema lo elimina.

Il sistema Android interrompe un servizio solo quando la memoria è insufficiente e deve recuperare le risorse di sistema per l'attività incentrata sull'utente. Se il servizio è associato a un'attività incentrata sull'utente, è meno probabile che venga arrestata; se il servizio viene dichiarato eseguito in primo piano, viene raramente interrotto. Se il servizio è avviato ed è a lunga esecuzione, nel tempo il sistema ne abbassa la posizione nell'elenco delle attività in background e il servizio diventa altamente suscettibile all'interruzione: se il servizio viene avviato, devi progettarlo per gestire agevolmente i riavvii da parte del sistema. Se il sistema termina il tuo servizio, lo riavvia non appena le risorse diventano disponibili, ma ciò dipende anche dal valore restituito da onStartCommand(). Per saperne di più su quando il sistema potrebbe eliminare un servizio, consulta il documento Processes and 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, proprio come fai per le attività e altri componenti.

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

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

Consulta il riferimento all'elemento <service> per ulteriori informazioni sulla dichiarazione del servizio nel 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 il servizio deve essere eseguito. L'attributo android:name è l'unico attributo obbligatorio, in quanto specifica il nome della classe del servizio. Dopo aver pubblicato l'applicazione, non modificare il nome per evitare il rischio di violare il codice dovuto alla dipendenza da intent espliciti per l'avvio o l'associazione del servizio (leggi il post del blog, Cose che non possono cambiare).

Attenzione: per assicurarti che la tua app sia sicura, usa sempre un intent esplicito quando avvii un Service e non dichiarare i filtri per intent per i tuoi servizi. L'utilizzo di un intent implicito per avviare un servizio rappresenta un rischio per la sicurezza perché non puoi avere la certezza che il servizio risponda all'intent 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 implicito.

Puoi assicurarti che il servizio sia disponibile solo per la tua app includendo l'attributo android:exported e impostandolo su false. Ciò impedisce ad altre app di avviare il servizio, anche quando si utilizza un intent esplicito.

Nota: gli utenti possono vedere quali servizi sono in esecuzione sul loro dispositivo. Se vedono 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 è un servizio che viene avviato da un altro componente chiamando startService(), 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 eliminato. Di conseguenza, il servizio dovrebbe arrestarsi automaticamente quando il job viene completato chiamando stopSelf() oppure un altro componente può arrestarlo chiamando stopService().

Un componente dell'applicazione come un'attività può avviare il servizio chiamando startService() e passando un Intent che specifichi il servizio e includa tutti i dati che il servizio può 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 companion e fornirgli i dati da salvare passando un intent a startService(). Il servizio riceve l'intent in onStartCommand(), si connette a internet ed esegue la transazione del database. Al termine della transazione, il servizio si arresta da solo e viene eliminato.

Attenzione: un servizio viene eseguito nello stesso processo dell'applicazione in cui è stato dichiarato e nel thread principale dell'applicazione per impostazione predefinita. Se il servizio esegue operazioni intense o di blocco mentre l'utente interagisce con un'attività della stessa applicazione, le prestazioni dell'attività rallentano. Per evitare di influire sulle prestazioni dell'applicazione, avvia un nuovo thread all'interno del servizio.

La classe Service è la classe base per tutti i servizi. Quando estendi questa classe, è importante creare un nuovo thread in cui il servizio possa completare tutto il suo lavoro. Il servizio utilizza per impostazione predefinita 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, in quanto non funzionerà bene a partire da Android 8 Oreo, a causa dell'introduzione dei limiti di esecuzione in background. Inoltre, è deprecata a partire da Android 11. Puoi utilizzare JobIntentService come sostituzione di IntentService, compatibile con le versioni più recenti di Android.

Le seguenti sezioni descrivono come implementare il tuo servizio personalizzato, ma ti consigliamo vivamente di utilizzare WorkManager nella 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 entrata. Ecco come potrebbe apparire 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 come un IntentService ed elabora tutte le richieste in modo seriale, una dopo l'altra. Puoi modificare il codice per eseguire il lavoro su un pool di thread, ad esempio se vuoi eseguire più richieste contemporaneamente.

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

START_NOT_STICKY
Se il sistema termina il servizio dopo il ritorno di onStartCommand(), non ricrearlo a meno che non ci siano intent in sospeso da consegnare. Questa è l'opzione più sicura per evitare di eseguire il servizio quando non è necessario e quando l'applicazione può semplicemente riavviare qualsiasi job non completato.
START_STICKY
Se il sistema termina il servizio dopo il ritorno di onStartCommand(), ricrealo e chiama onStartCommand(), ma non inviare nuovamente l'ultimo intent. Al contrario, il sistema chiama onStartCommand() con un intent nullo, a meno che non siano presenti intent in sospeso per avviare il servizio. In questo caso, questi intenti vengono realizzati. Questa opzione è adatta per lettori multimediali (o servizi simili) che non eseguono comandi, ma che 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(), ricrealo e chiama onStartCommand() con l'ultimo intent fornito al servizio. Gli intent in sospeso vengono inviati a turno. Questa opzione è adatta ai servizi che eseguono attivamente un job che deve essere ripristinato immediatamente, come 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 un altro componente dell'applicazione passando un valore 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 successivo, il sistema impone limitazioni all'utilizzo o alla creazione di servizi in background, a meno che l'app 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 segnala al sistema che il servizio si promuoverà in primo piano. Una volta creato, il servizio deve chiamare il metodo startForeground() entro cinque secondi.

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

Kotlin

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

Java

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

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

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

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

Interruzione di un servizio

Un servizio avviato deve gestire il proprio ciclo di vita. In altre parole, il sistema non arresta né elimina il servizio a meno che non debba recuperare la memoria di sistema e il servizio continui a essere eseguito dopo la restituzione di onStartCommand(). Il servizio deve arrestarsi chiamando stopSelf() oppure un altro componente può arrestarlo chiamando stopService().

Una volta richiesto l'arresto con stopSelf() o stopService(), il sistema elimina il servizio non appena possibile.

Se il tuo servizio gestisce più richieste a onStartCommand() contemporaneamente, non devi interrompere il servizio quando hai terminato l'elaborazione di una richiesta di avvio, poiché potresti aver ricevuto una nuova richiesta di avvio (se interrompi il servizio alla fine della prima richiesta, viene terminata la seconda). Per evitare questo problema, puoi utilizzare stopSelf(int) per assicurarti che la tua richiesta di interruzione del servizio sia sempre basata sulla richiesta di avvio più recente. In altre parole, quando chiami stopSelf(int), passi l'ID della richiesta di avvio (il startId consegnato a onStartCommand()) a cui corrisponde la richiesta di interruzione. Quindi, se il servizio riceve una nuova richiesta di avvio prima che tu possa chiamare stopSelf(int), l'ID non corrisponde e il servizio non viene interrotto.

Attenzione: per evitare di sprecare risorse di sistema e consumare energia a batteria, assicurati che l'applicazione interrompa i servizi una volta completata l'operazione. Se necessario, altri componenti possono interrompere il servizio chiamando stopService(). Anche se abiliti l'associazione per il servizio, devi sempre arrestare il servizio autonomamente se riceve una chiamata a onStartCommand().

Per saperne di più sul ciclo di vita di un servizio, vedi la sezione Gestione del ciclo di vita di un servizio più avanti in questo articolo.

Creazione di un servizio associato

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

Crea un servizio associato quando vuoi interagire con il servizio da attività e altri componenti dell'applicazione o esporre alcune delle funzionalità della tua applicazione ad altre applicazioni tramite IPC (Inter-Process Communication).

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 sul servizio. Il servizio serve solo per gestire il componente dell'applicazione a cui è associato. Di conseguenza, quando non esistono componenti associati al servizio, il sistema lo elimina. Non è necessario arrestare un servizio associato come deve essere 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. L'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 che il client riceve IBinder, può iniziare a interagire con il servizio tramite questa interfaccia.

L'associazione di più client al servizio può essere eseguita contemporaneamente. Quando un client ha terminato di interagire con il servizio, chiama unbindService() per lo svincolo. Se non esistono client associati al servizio, il sistema elimina il servizio.

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

Invio di notifiche all'utente

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

Una notifica della snackbar è un messaggio che appare sulla superficie della finestra corrente solo per un momento prima di scomparire. Una notifica nella barra di stato fornisce un'icona nella barra di stato con un messaggio, che l'utente può selezionare per eseguire un'azione, ad esempio avviare un'attività.

Generalmente, la notifica nella barra di stato è la tecnica migliore da utilizzare quando è stato completato un lavoro in background, ad esempio il download di un file, su cui l'utente ora può intervenire. Quando l'utente seleziona la notifica dalla visualizzazione espansa, la notifica può avviare un'attività (ad esempio, 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 particolare attenzione al modo in cui il servizio viene creato ed eliminato, perché un servizio può essere eseguito in background a insaputa dell'utente.

Il ciclo di vita del servizio, dalla creazione a quando viene eliminato, 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 arrestarsi chiamando stopSelf(). Un altro componente può arrestare il servizio anche 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 essere associati allo stesso servizio e, quando vengono svincolati, il sistema elimina il servizio. Il servizio non deve arrestarsi automaticamente.

Questi due percorsi non sono completamente separati. Puoi associare 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 seguito, possibilmente quando l'utente vuole esercitare un certo controllo sul player o ricevere informazioni sul brano corrente, un'attività può essere associata al servizio chiamando bindService(). In questi casi, stopService() o stopSelf() non interrompe effettivamente il servizio finché tutti i client non si sono svincolati.

Implementazione dei callback del ciclo di vita

Come un'attività, un servizio ha metodi di callback del ciclo di vita che puoi implementare per monitorare le modifiche allo stato del servizio ed eseguire le operazioni nei momenti appropriati. Il seguente servizio di bozza illustra 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 delle 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 il diagramma a destra mostra il ciclo di vita quando il servizio viene creato con bindService().

La figura 2 illustra i tipici metodi di callback per un servizio. Anche se la figura separa i servizi creati da startService() da quelli creati da bindService(), tieni presente che qualsiasi servizio, a prescindere da come è stato avviato, può potenzialmente consentire l'associazione dei client al servizio. Un servizio avviato inizialmente con onStartCommand() (da un client che chiama startService()) può comunque ricevere una chiamata a onBind() (quando un client chiama bindService()).

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

  • L'intera durata di un servizio si verifica 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 musicale può creare il thread in cui viene riprodotta la musica in onCreate() e poi interromperlo in onDestroy().

    Nota: i metodi onCreate() e onDestroy() vengono richiesti per tutti i servizi, indipendentemente dal fatto che vengano creati da startService() o bindService().

  • La durata attiva di un servizio inizia con una chiamata al numero onStartCommand() o onBind(). A ogni metodo viene assegnato il valore Intent che è stato trasmesso a startService() o bindService().

    Se il servizio viene avviato, la durata attiva termina nello stesso momento in cui termina l'intero ciclo di vita (il servizio è ancora attivo anche dopo la restituzione 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 callback corrispondente per il servizio (non è presente un callback onStop()). A meno che il servizio non sia associato a un client, il sistema lo elimina quando il servizio viene interrotto: onDestroy() è l'unico callback ricevuto.

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