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 quest'ultimo 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 un proprio thread e non viene eseguito in un processo separato, se non diversamente specificato. Dovresti 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. Per i servizi in primo piano deve essere visualizzata 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 siano consapevoli 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à e, se necessario, è in grado di eseguire questi job come servizi in primo piano. In molti casi, è preferibile utilizzare WorkManager che utilizzare direttamente i servizi in primo piano.
- Premessa
- Un servizio in background esegue un'operazione non immediatamente 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 versioni successive, 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.
- Associato
- Un servizio è associato quando un componente dell'applicazione si associa al servizio 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 nei processi con comunicazione tra processi (IPC). Un servizio associato viene eseguito solo finché è associato un altro componente dell'applicazione. Al servizio possono essere associati più componenti contemporaneamente, ma quando vengono svincolati tutti, il servizio viene eliminato.
Anche se questa documentazione illustra in genere i servizi avviati e associati separatamente, il tuo servizio può funzionare in entrambi i modi: può essere avviato (per essere eseguito a tempo indeterminato) e consente anche l'associazione. Occorre 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 un 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.
Scegliere 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 all'esterno del thread principale, ma solo mentre l'utente interagisce con l'applicazione, dovrai creare un nuovo thread nel contesto di un altro componente dell'applicazione. Ad esempio, se vuoi ascoltare un po' di musica, ma solo mentre l'attività è in esecuzione, potresti creare un thread in onCreate()
, iniziare a eseguirlo in onStart()
e interromperlo in onStop()
.
Valuta anche l'utilizzo di pool di thread ed esecutori del pacchetto java.util.concurrent
o delle coroutine di Kotlin anziché della tradizionale classe 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 viene comunque eseguito per impostazione predefinita nel thread principale dell'applicazione, quindi dovrai 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 Service
o utilizzare una delle relative sottoclassi esistenti. Nella tua implementazione, devi sostituire 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 dovresti
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 impostazione, è tua responsabilità interrompere il servizio al termine del suo lavoro chiamandostopSelf()
ostopService()
. 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 utilizzino per comunicare con il servizio restituendo unIBinder
. Devi sempre implementare questo metodo; tuttavia, se non vuoi consentire l'associazione, devi restituire un valore null. onCreate()
- Il sistema richiama questo metodo per eseguire procedure di configurazione una tantum quando il servizio viene
creato all'inizio (prima di chiamare
onStartCommand()
oonBind()
). 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 deve implementarla per eseguire la pulizia di 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 continuerà a essere eseguito finché non si arresta con stopSelf()
o un altro componente lo arresta 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 è quasi esaurita e deve recuperare le risorse di sistema per l'attività incentrata sull'utente. Se il servizio è associato a un'attività focalizzata sull'utente, è meno probabile che venga interrotto. Se il servizio viene eseguito in primo piano, raramente viene interrotto.
Se il servizio è avviato ed è a lunga esecuzione, nel tempo il sistema abbassa la sua posizione nell'elenco delle attività in background e il servizio diventa altamente suscettibile all'arresto: se il servizio viene avviato, devi progettarlo per gestire agevolmente 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 eliminare un servizio, consulta il documento Processi e thread.
Nelle sezioni seguenti scoprirai come creare i metodi dei servizi startService()
e bindService()
, nonché 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 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>
Per ulteriori informazioni sulla dichiarazione del servizio nel manifest, consulta la documentazione di riferimento sull'elemento <service>
.
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 attributo obbligatorio, in quanto specifica il nome della classe del servizio. Dopo aver pubblicato l'applicazione, lascia questo nome invariato per evitare il rischio di danneggiare il codice dovuto alla dipendenza da intent espliciti di avviare o associare il 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 Service
e non dichiarare filtri per intent per i tuoi servizi. L'utilizzo di un intent implicito per avviare un servizio rappresenta un pericolo per la sicurezza, perché non puoi essere certo del servizio che 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
. Questo impedisce ad altre app di avviare
il servizio, anche se utilizzi 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 che 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 l'ha avviato viene eliminato. Di conseguenza, il servizio dovrebbe arrestarsi automaticamente al termine del job chiamando stopSelf()
oppure un altro componente può arrestarlo chiamando stopService()
.
Un componente dell'applicazione, ad esempio un'attività, può avviare il servizio chiamando startService()
e passando un Intent
che specifichi il servizio e includa eventuali 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 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 e viene distrutto.
Attenzione: un servizio viene eseguito nello stesso processo dell'applicazione in cui è dichiarato e nel thread principale dell'applicazione per impostazione predefinita. Se il servizio esegue operazioni intensive o di blocco mentre l'utente interagisce con un'attività della stessa applicazione, il servizio rallenta le prestazioni dell'attività. 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 può 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 worker per gestire tutte le richieste di avvio, una alla volta. L'utilizzo di questo corso non è consigliato per le nuove app, poiché non funzionerà bene a partire da Android 8 Oreo, a causa dell'introduzione dei limiti di esecuzione in background.
Inoltre, è stato ritirato 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 per la maggior parte dei casi d'uso ti consigliamo di utilizzare WorkManager. Consulta la guida all'elaborazione in background su Android per vedere 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 presentarsi 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 serie, 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 nel caso in cui venga interrotto. 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 attesa da consegnare. Questa è l'opzione più sicura per evitare di eseguire il servizio quando non è necessario e quando l'applicazione può semplicemente riavviare i job non completati. START_STICKY
- Se il sistema termina il servizio dopo il ritorno di
onStartCommand()
, ricrealo e chiamaonStartCommand()
, ma non consegnare di nuovo l'ultimo intent. Il sistema chiama inveceonStartCommand()
con un intent nullo, a meno che non ci siano intent in attesa per avviare il servizio. In questo caso, questi intenti vengono sfruttati. Questo è adatto per media player (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()
, ricrealo e chiamaonStartCommand()
con l'ultimo intent fornito al servizio. Gli eventuali intent in sospeso vengono inviati a loro volta. È adatto per i servizi che eseguono 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 segnala 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 esplicito con startService()
, come mostrato qui:
Kotlin
startService(Intent(this, HelloService::class.java))
Java
startService(new Intent(this, HelloService.class));
Il metodo startService()
restituisce 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 fornisce 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 restituisca un risultato, il client che avvia il servizio può creare un PendingIntent
per una trasmissione (con getBroadcast()
) e consegnarlo 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 o 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 automaticamente chiamando stopSelf()
, oppure un altro
componente può arrestarlo chiamando stopService()
.
Dopo che è stato richiesto l'arresto con stopSelf()
o stopService()
, il sistema elimina il servizio non appena possibile.
Se il servizio gestisce più richieste a onStartCommand()
contemporaneamente, non devi interrompere il servizio quando hai terminato l'elaborazione di una richiesta di avvio, perché potresti aver ricevuto una nuova richiesta di avvio (interrompersi alla fine della prima richiesta comporta la terminazione della 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 (la startId
consegnata a onStartCommand()
) a cui corrisponde la tua 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 batteria, assicurati che l'applicazione interrompa i propri servizi quando ha finito di funzionare.
Se necessario, altri componenti possono interrompere il servizio chiamando stopService()
. Anche se abiliti l'associazione per il servizio, devi sempre arrestarlo autonomamente se riceve una chiamata a onStartCommand()
.
Per saperne di più sul ciclo di vita di un servizio, vedi la sezione seguente sulla gestione del ciclo di vita di un servizio.
Creazione di un servizio associato
Un servizio associato consente ai componenti dell'applicazione di associarsi chiamando bindService()
per creare una connessione di lunga data.
Generalmente non consente ai componenti di avviarlo chiamando startService()
.
Crea un servizio associato quando vuoi interagire con il servizio dalle attività e da altri componenti dell'applicazione o per esporre alcune delle funzionalità della tua applicazione ad altre applicazioni attraverso la comunicazione tra processi (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 sul servizio. Il servizio risiede solo per gestire il componente dell'applicazione
associato, quindi se non esistono componenti associati al servizio, il sistema lo elimina.
Non è necessario arrestare un servizio associato nello stesso modo in cui devi arrestare un servizio associato quando 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 che il client riceve IBinder
, può iniziare a interagire con il servizio tramite questa interfaccia.
Più client possono essere associati contemporaneamente al servizio. Quando un client ha terminato di interagire con il servizio, chiama unbindService()
per svincolarlo.
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 sui servizi associati viene visualizzata in un documento separato sui servizi associati.
Invio di notifiche all'utente
Quando un servizio è in esecuzione, può informare l'utente degli eventi utilizzando le notifiche della barra di stato o le 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à).
Di solito, una notifica della barra di stato è la tecnica migliore da utilizzare quando le operazioni in background, ad esempio il download di un file, sono state completate e l'utente può ora 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 fare attenzione a come viene creato ed eliminato il servizio perché può essere eseguito in background all'insaputa dell'utente.
Il ciclo di vita del servizio, da quando viene creato 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 chiamandostopSelf()
. Un altro componente può anche interrompere il servizio chiamandostopService()
. 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'interfacciaIBinder
. Il client può chiudere la connessione chiamandounbindService()
. Più client possono essere associati allo stesso servizio e, quando tutti vengono svincolati, il sistema elimina il servizio. Il servizio non deve arrestarsi automaticamente.
Questi due percorsi non sono completamente separati. Puoi eseguire l'associazione a un servizio già
avviato con startService()
. Ad esempio, puoi
avviare un servizio di musica in background chiamando startService()
con un Intent
che identifica la musica da riprodurre. In seguito,
se in un secondo momento l'utente vuole esercitare un certo controllo sul player o ottenere informazioni sul
brano corrente, un'attività può essere associata al servizio chiamando bindService()
. In questi casi, stopService()
o stopSelf()
non interrompe effettivamente il servizio fino a quando tutti i client non si svincolano.
Implementazione dei callback del ciclo di vita
Come per 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. Lo scheletro di questo servizio descrive 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 funonCreate
() { // The service is being created } override funonStartCommand
(intent: Intent?, flags: Int, startId: Int): Int { // The service is starting, due to a call to startService() return startMode } override funonBind
(intent: Intent): IBinder? { // A client is binding to the service with bindService() return binder } override funonUnbind
(intent: Intent): Boolean { // All clients have unbound with unbindService() return allowRebind } override funonRebind
(intent: Intent) { // A client is binding to the service with bindService(), // after onUnbind() has already been called } override funonDestroy
() { // 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 voidonCreate
() { // The service is being created } @Override public intonStartCommand
(Intent intent, int flags, int startId) { // The service is starting, due to a call tostartService()
return startMode; } @Override public IBinderonBind
(Intent intent) { // A client is binding to the service withbindService()
return binder; } @Override public booleanonUnbind
(Intent intent) { // All clients have unbound withunbindService()
return allowRebind; } @Override public voidonRebind
(Intent intent) { // A client is binding to the service withbindService()
, // after onUnbind() has already been called } @Override public voidonDestroy
() { // 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.
La figura 2 illustra i tipici metodi di callback di 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 viene avviato, può potenzialmente consentire ai client di essere associato.
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 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 restituitoonDestroy()
. Come un'attività, un servizio esegue la configurazione iniziale inonCreate()
e rilascia tutte le risorse rimanenti inonDestroy()
. Ad esempio, un servizio di riproduzione musicale può creare il thread in cui viene riprodotta la musica inonCreate()
e poi interromperlo inonDestroy()
.Nota: i metodi
onCreate()
eonDestroy()
vengono richiesti per tutti i servizi, indipendentemente dal fatto che siano stati creati dastartService()
obindService()
. - La durata attiva di un servizio inizia con una chiamata a
onStartCommand()
oonBind()
. A ogni metodo viene assegnato ilIntent
che è stato trasmesso astartService()
obindService()
.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 il reso di
onStartCommand()
). Se il servizio è associato, la durata attiva termina quando viene restituitoonUnbind()
.
Nota: anche se un servizio avviato viene interrotto da una chiamata al servizio stopSelf()
o stopService()
, non esiste un callback corrispondente per il servizio (non esiste 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 onRebind()
nella sezione sulla gestione del ciclo di vita di un servizio associato.