Un Service
è un componente dell’applicazione che può eseguire operazioni di lunga durata in background. Non fornisce un’interfaccia utente. All’inizio, un servizio potrebbe continuare a funzionare per qualche tempo, anche dopo che l’utente passa a un’altra applicazione. Inoltre, un componente può legarsi a un servizio per interagire con esso e persino eseguire una comunicazione interprocesso (IPC). Per esempio, un servizio può gestire transazioni di rete, riprodurre musica, eseguire I/O su file o interagire con un fornitore di contenuti, tutto in background.
Attenzione: Un servizio viene eseguito nel thread principale del suo processo di hosting; il servizio non crea un proprio thread e non viene eseguito in un processo separato a meno che non venga specificato altrimenti. Dovresti eseguire qualsiasi operazione bloccante su un thread separato all’interno del servizio per evitare errori di ApplicationNot Responding (ANR).
- Tipi di servizi
- Scegliere tra un servizio e un thread
- Le basi
- Dichiarare un servizio nel manifest
- Creazione di un servizio avviato
- Estendere la classe Service
- Kotlin
- Java
- Avviare un servizio
- Kotlin
- Java
- Arresto di un servizio
- Creazione di un servizio vincolato
- Inviare notifiche all’utente
- Gestire il ciclo di vita di un servizio
- Implementazione dei callback del ciclo di vita
- Kotlin
- Java
Tipi di servizi
Questi sono i tre diversi tipi di servizi:
In primo piano
Un servizio in primo piano esegue qualche operazione che è visibile all’utente. Per esempio, un’applicazione audio userebbe un servizio in primo piano per riprodurre una traccia audio. I servizi in primo piano devono visualizzare una notifica. I servizi in primo piano continuano a funzionare anche quando l’utente non sta interagendo con l’app.
Quando usi un servizio in primo piano, devi mostrare una notifica in modo che gli utenti siano attivamente consapevoli che il servizio è in esecuzione. Questa notifica non può essere rimossa a meno che il servizio non venga fermato o rimosso dal foreground.
Impara di più su come configurare i servizi in foreground nella tua app.
Nota: L’API WorkManager offre un modo flessibile di programmare i compiti ed è in grado di eseguire questi lavori come servizi in foreground, se necessario. In molti casi, usare WorkManager è preferibile all’uso diretto dei servizi in primo piano.
Background Un servizio in background esegue un’operazione che non viene notata direttamente dall’utente. Per esempio, se un’applicazione usasse un servizio per compattare il suo storage, questo sarebbe di solito un servizio in background.
Nota: Se la tua app si rivolge al livello API 26 o superiore, il sistema impone delle restrizioni sull’esecuzione di servizi in background quando l’app stessa non è in primo piano. Nella maggior parte delle situazioni, per esempio, non dovresti accedere alle informazioni sulla posizione in background. Invece, pianifica le attività usando WorkManager.
Vincolato Un servizio è vincolato quando un componente dell’applicazione si lega ad esso chiamandobindService()
. Un servizio vincolato offre un’interfaccia client-server che permette ai componenti di interagire con il servizio, inviare richieste, ricevere risultati, e anche farlo attraverso processi con comunicazione interprocesso (IPC). Un servizio vincolato funziona solo finché un altro componente dell’applicazione è vincolato ad esso. Più componenti possono legarsi al servizio contemporaneamente, ma quando tutti si scollegano, il servizio viene distrutto.
Anche se questa documentazione generalmente discute separatamente i servizi avviati e vincolati, il vostro servizio può funzionare in entrambi i modi: può essere avviato (per essere eseguito indefinitamente) e anche consentire il collegamento. È semplicemente una questione di implementare un paio di metodi di callback: onStartCommand()
per permettere ai componenti di avviarlo e onBind()
per permettere il binding.
A prescindere dal fatto che il servizio sia avviato, vincolato o entrambi, qualsiasi componente dell’applicazione può usare il servizio (anche da un’applicazione separata) nello stesso modo in cui qualsiasi componente può usare un’attività, avviandola con un Intent
. Tuttavia, è possibile dichiarare il servizio come privato nel file manifest e bloccare l’accesso da parte di altre applicazioni. Questo è discusso maggiormente nella sezione relativa alla dichiarazione del servizio nel manifest.
Scegliere tra un servizio e un thread
Un servizio è semplicemente un componente che può essere eseguito in background, anche quando l’utente non sta interagendo con la tua applicazione, quindi dovresti creare un servizio solo se questo è ciò di cui hai bisogno.
Se dovete eseguire il lavoro al di fuori del vostro thread principale, ma solo mentre l’utente sta interagendo con la vostra applicazione, dovreste invece creare un nuovo thread nel contesto di un altro componente dell’applicazione. Per esempio, se volete riprodurre della musica, ma solo mentre la vostra attività è in esecuzione, potreste creare un thread in onCreate()
, iniziare l’esecuzione in onStart()
e fermarlo in onStop()
.Considerate anche l’utilizzo di pool di thread ed esecutori dal pacchetto java.util.concurrent
o coroutine Kotlin invece della tradizionale classeThread
. Vedi il documentoThreading su Android per maggiori informazioni su come spostare l’esecuzione su thread in background.
Ricorda che se usi un servizio, viene ancora eseguito nel thread principale della tua applicazione per default, quindi dovresti comunque creare un nuovo thread all’interno del servizio se esegue operazioni intensive o bloccanti.
Le basi
Per creare un servizio, devi creare una sottoclasse di Service
o usare una delle sue sottoclassi esistenti. Nella vostra implementazione, dovete sovrascrivere alcuni metodi di callback che gestiscono aspetti chiave del ciclo di vita del servizio e fornire un meccanismo che permette ai componenti di collegarsi al servizio, se appropriato. Questi sono i metodi di callback più importanti che dovreste sovrascrivere:
onStartCommand()
Il sistema invoca questo metodo chiamandostartService()
quando un altro componente (come un’attività) richiede che il servizio venga avviato.Quando questo metodo viene eseguito, il servizio viene avviato e può essere eseguito in background indefinitamente. Se lo implementate, è vostra responsabilità fermare il servizio quando il suo lavoro è completato chiamandostopSelf()
ostopService()
. Se volete solo fornire il binding, non avete bisogno di implementare questo metodo.onBind()
Il sistema invoca questo metodo chiamandobindService()
quando un altro componente vuole legarsi al servizio (ad esempio per eseguire RPC).Nella vostra implementazione di questo metodo, dovete fornire un’interfaccia che i client usano per comunicare con il servizio restituendo unIBinder
. Dovete sempre implementare questo metodo; tuttavia, se non volete permettere il binding, dovreste restituire null.onCreate()
Il sistema invoca questo metodo per eseguire procedure di configurazione una tantum quando il servizio viene creato inizialmente (prima di chiamareonStartCommand()
oonBind()
). Se il servizio è già in esecuzione, questo metodo non viene richiamato.onDestroy()
Il sistema invoca questo metodo quando il servizio non è più utilizzato e viene distrutto.Il tuo servizio dovrebbe implementarlo per pulire qualsiasi risorsa come thread, ascoltatori registrati o ricevitori. Questa è l’ultima chiamata che il servizio riceve.
Se un componente avvia il servizio chiamando startService()
(che risulta in una chiamata a onStartCommand()
), il servizio continua a funzionare finché non si ferma da solo con stopSelf()
o un altro componente lo ferma chiamando stopService()
.
Se un componente chiamabindService()
per creare il servizio e onStartCommand()
non viene chiamato, il servizio funziona solo finché il componente è legato ad esso. Dopo che il servizio è slegato da tutti i suoi clienti, il sistema lo distrugge.
Il sistema Android ferma un servizio solo quando la memoria è bassa e deve recuperare le risorse di sistema per l’attività che ha il focus dell’utente. Se il servizio è legato a un’attività che ha il focus dell’utente, è meno probabile che venga ucciso; se il servizio è dichiarato per essere eseguito in primo piano, raramente viene ucciso.Se il servizio viene avviato ed è di lunga durata, il sistema abbassa la sua posizione nell’elenco delle attività in background nel tempo, e il servizio diventa altamente suscettibile all’uccisione; se il vostro servizio viene avviato, dovete progettarlo per gestire con grazia i riavvii da parte del sistema. Se il sistema uccide il vostro servizio, lo riavvia non appena le risorse diventano disponibili, ma questo dipende anche dal valore che restituite da onStartCommand()
. Per maggiori informazioni su quando il sistema potrebbe distruggere un servizio, consultate il documento Processes and Threading.
Nelle sezioni seguenti, vedrete come potete creare i metodi di serviziostartService()
ebindService()
, e come utilizzarli da altri componenti dell’applicazione.
Dichiarare un servizio nel manifest
Devi dichiarare tutti i servizi nel file smanifest della tua applicazione, proprio come fai per le attività e gli altri componenti.
Per dichiarare il tuo servizio, aggiungi un elemento <service>
come figlio dell’elemento <application>
. Ecco un esempio:
<manifest ... > ... <application ... > <service android:name=".ExampleService" /> ... </application></manifest>
Vedi il riferimento all’elemento <service>
per maggiori informazioni su come dichiarare il tuo servizio nel manifest.
Ci sono altri attributi che puoi includere nell’elemento <service>
per definire proprietà come i permessi richiesti per avviare il servizio e il processo in cui il servizio dovrebbe essere eseguito. L’attributo android:name
è l’unico attributo richiesto: specifica il nome della classe del servizio. Dopo aver pubblicato la vostra applicazione, lasciate questo nome invariato per evitare il rischio di breakingcode dovuto alla dipendenza da intenti espliciti per avviare o vincolare il servizio (leggete il post del blog, ThingsThat Cannot Change).
Caution: Per garantire che la tua app sia sicura, usa sempre un intento esplicito quando avvii un Service
e non dichiarare filtri di intenti per i tuoi servizi. Usare un intento implicito per avviare un servizio è un rischio per la sicurezza perché non si può essere certi del servizio che risponde all’intento, e l’utente non può vedere quale servizio si avvia. A partire da Android 5.0 (livello API 21), il sistema lancia un’eccezione se chiamatebindService()
con un intento implicito.
Potete assicurarvi che il vostro servizio sia disponibile solo per la vostra app includendo l’attributo android:exported
e impostandolo a false
. Questo impedisce effettivamente alle altre applicazioni di avviare il tuo servizio, anche quando si utilizza un intento esplicito.
Nota: Gli utenti possono vedere quali servizi sono in esecuzione sul loro dispositivo. Se vedono un servizio che non riconoscono o di cui si fidano, possono fermare il servizio. Per evitare che il tuo servizio venga fermato accidentalmente dagli utenti, devi aggiungere l’attributo android:description
all’elemento <service>
nel manifesto della tua app. Nella descrizione, fornisci una breve frase che spieghi cosa fa il servizio e quali benefici fornisce.
Creazione di un servizio avviato
Un servizio avviato è quello che un altro componente avvia chiamando startService()
, che risulta in una chiamata al metodoonStartCommand()
del servizio.
Quando un servizio è avviato, ha un ciclo di vita che è indipendente dal componente che lo ha avviato. Il servizio può funzionare in background indefinitamente, anche se il componente che lo ha avviato viene distrutto. Come tale, il servizio dovrebbe fermarsi quando il suo lavoro è completo chiamando stopSelf()
, o un altro componente può fermarlo chiamando stopService()
.
Un componente dell’applicazione come un’attività può avviare il servizio chiamando startService()
e passando un Intent
che specifica il servizio e include qualsiasi dato che il servizio deve utilizzare. Il servizio riceve questo Intent
nel metodo onStartCommand()
.
Per esempio, supponiamo che un’attività abbia bisogno di salvare alcuni dati in un database online. L’attività può avviare un servizio companion e consegnargli i dati da salvare passando un intento a startService()
. Il servizio riceve l’intento in onStartCommand()
, si connette a Internet ed esegue la transazione sul database. Quando la transazione è completa, il servizio si ferma e viene distrutto.
Attenzione: Un servizio viene eseguito nello stesso processo dell’applicazione in cui è dichiarato e nel thread principale di tale applicazione per impostazione predefinita. Se il servizio esegue operazioni intensive o bloccanti 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, avviate un nuovo thread all’interno del servizio.
La classe Service
è la classe base per tutti i servizi. Quando estendete questa classe, è importante creare un nuovo thread in cui il servizio possa completare tutto il suo lavoro; il servizio usa di default il thread principale della vostra applicazione, il che può rallentare le prestazioni di qualsiasi attività che la vostra applicazione sta eseguendo.
Il framework Android fornisce anche la sottoclasse IntentService
di Service
che usa un thread lavoratore per gestire tutte le richieste di avvio, una alla volta. L’uso di questa classe è sconsigliato per le nuove applicazioni in quanto 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. È possibile utilizzare JobIntentService come sostituto di IntentService
che è compatibile con le versioni più recenti di Android.
Le sezioni seguenti descrivono come è possibile implementare il proprio servizio personalizzato, tuttavia si dovrebbe considerare fortemente di utilizzare WorkManager invece per la maggior parte dei casi d’uso. Consulta la guida all’elaborazione in background su Android per vedere se c’è una soluzione adatta alle tue esigenze.
Estendere la classe Service
Puoi estendere la classe Service
per gestire ogni intento in arrivo. 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 entrata in onStartCommand()
e invia il lavoro a un Handler
in esecuzione su un thread in background. Funziona proprio come un IntentService
ed elabora tutte le richieste in serie, una dopo l’altra.Potreste cambiare il codice per eseguire il lavoro su un pool di thread, per esempio, se volete eseguire più richieste simultaneamente.
Nota che il metodo onStartCommand()
deve restituire un intero. L’intero è un valore che descrive come il sistema dovrebbe continuare il servizio nel caso in cui il sistema lo uccida. Il valore di ritorno di onStartCommand()
deve essere una delle seguenti costanti:
START_NOT_STICKY
Se il sistema uccide il servizio dopo il ritorno dionStartCommand()
, non ricreare il servizio a meno che non ci siano dei messaggi in sospeso da consegnare. Questa è l’opzione più sicura per evitare di eseguire il servizio quando non è necessario e quando la vostra applicazione può semplicemente riavviare qualsiasi lavoro non terminato.START_STICKY
Se il sistema uccide il servizio dopo il ritorno dionStartCommand()
, ricreate il servizio e chiamateonStartCommand()
, ma non riconsegnate l’ultimo intento; invece, il sistema chiamaonStartCommand()
con un intento nullo a meno che non ci siano intenti in sospeso per avviare il servizio. In questo caso, quegli intenti vengono consegnati. Questo è adatto per i lettori multimediali (o servizi simili) che non stanno eseguendo comandi ma sono in esecuzione indefinitamente e in attesa di un lavoro.START_REDELIVER_INTENT
Se il sistema uccide il servizio dopo il ritorno dionStartCommand()
, ricreate il servizio e chiamateonStartCommand()
con l’ultimo intento che è stato consegnato al servizio. Qualsiasi intento in sospeso viene consegnato a sua volta. Questo è adatto ai servizi che stanno eseguendo attivamente un lavoro che dovrebbe essere immediatamente ripreso, come il download di un file.
Per maggiori dettagli su questi valori di ritorno, vedi la documentazione di riferimento collegata per ogni costante.
Avviare un servizio
Puoi avviare un servizio da un’attività o un altro componente dell’applicazione passando un Intent
a startService()
o startForegroundService()
. Il sistema Android chiama il metodo onStartCommand()
del servizio e gli passa il Intent
, che specifica quale servizio avviare.
Nota: se la tua applicazione si rivolge al livello API 26 o superiore, il sistema impone restrizioni sull’uso o la creazione di servizi in background a meno che l’applicazione stessa sia in primo piano. Se un’app ha bisogno di creare un servizio in primo piano, l’app dovrebbe 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 che il servizio è stato creato, il servizio deve chiamare il suo metodo startForeground()
entro cinque secondi.
Per esempio, un’attività può avviare il servizio di esempio della sezione precedente (HelloService
) usando un intento esplicito con startService()
, come mostrato qui:
Kotlin
Intent(this, HelloService::class.java).also { intent -> startService(intent)}
Java
Intent intent = new Intent(this, HelloService.class);startService(intent);
Il metodo startService()
ritorna immediatamente, 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’intento che viene consegnato con startService()
è l’unico modo di comunicazione tra il componente dell’applicazione e il servizio. Tuttavia, se si desidera che il servizio invii un risultato indietro, il client che avvia il servizio può creare un PendingIntent
per una trasmissione (con getBroadcast()
) e consegnarlo al servizio nel Intent
che avvia il servizio. Il servizio può quindi utilizzare il broadcast per consegnare un risultato.
Richieste multiple per avviare il servizio risultano in chiamate multiple corrispondenti alonStartCommand()
del servizio. Tuttavia, una sola richiesta di arresto del servizio (con stopSelf()
o stopService()
) è necessaria per fermarlo.
Arresto di un servizio
Un servizio avviato deve gestire il proprio ciclo di vita. Cioè, il sistema non si ferma o distrugge il servizio a meno che non debba recuperare la memoria del sistema e il servizio continua a funzionare dopo il ritorno di onStartCommand()
. Il servizio deve fermarsi da solo chiamando stopSelf()
, o un altro componente può fermarlo chiamando stopService()
.
Una volta richiesto di fermarsi con stopSelf()
o stopService()
, il sistema distrugge il servizio appena possibile.
Se il vostro servizio gestisce più richieste a onStartCommand()
contemporaneamente, non dovreste fermare il servizio quando avete finito di elaborare una richiesta di avvio, perché potreste aver ricevuto una nuova richiesta di avvio (fermarsi alla fine della prima richiesta terminerebbe la seconda). Per evitare questo problema, potete usare stopSelf(int)
per assicurarvi che la vostra richiesta di arresto del servizio sia sempre basata sulla richiesta di avvio più recente. Cioè, quando chiamate stopSelf(int)
, passate l’ID della richiesta di avvio (la startId
consegnata a onStartCommand()
) alla quale corrisponde la vostra richiesta di arresto. 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 si ferma.
Attenzione: Per evitare di sprecare risorse di sistema e consumare energia della batteria, assicurati che la tua applicazione fermi i suoi servizi quando ha finito di lavorare; se necessario, altri componenti possono fermare il servizio chiamando stopService()
. Anche se abiliti il binding per il servizio, devi sempre fermare tu stesso il servizio se riceve una chiamata a onStartCommand()
.
Per maggiori informazioni sul ciclo di vita di un servizio, vedi la sezione sottostante sulla Gestione del ciclo di vita di un servizio.
Creazione di un servizio vincolato
Un servizio vincolato è un servizio che permette ai componenti dell’applicazione di legarsi ad esso chiamando bindService()
per creare una connessione duratura.Generalmente non permette ai componenti di avviarlo chiamando startService()
.
Crea un servizio vincolato quando vuoi interagire con il servizio da attività e altri componenti della tua applicazione o per esporre alcune delle funzionalità della tua applicazione ad altre applicazioni attraverso la comunicazione interprocesso (IPC).
Per creare un servizio vincolato, implementa il metodo onBind()
callback per restituire un IBinder
che definisce l’interfaccia per la comunicazione con il servizio. Altri componenti dell’applicazione possono quindi chiamarebindService()
per recuperare l’interfaccia e iniziare a chiamare metodi sul servizio. Il servizio vive solo per servire il componente applicativo che è legato ad esso, quindi quando non ci sono componenti legati al servizio, il sistema lo distrugge.Non è necessario fermare un servizio legato nello stesso modo in cui è necessario quando il servizio viene avviato attraverso onStartCommand()
.
Per creare un servizio legato, è necessario definire l’interfaccia che specifica come 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 il IBinder
, può iniziare a interagire con il servizio attraverso quell’interfaccia.
Clienti multipli possono legarsi al servizio contemporaneamente. Quando un client ha finito di interagire con il servizio, chiama unbindService()
per slegarsi.Quando non ci sono più client legati al servizio, il sistema distrugge il servizio.
Ci sono più modi per implementare un servizio legato, e l’implementazione è più complicata di un servizio avviato. Per queste ragioni, la discussione sul servizio vincolato appare in un documento separato sui servizi vincolati.
Inviare notifiche all’utente
Quando un servizio è in esecuzione, può notificare all’utente degli eventi usando le notifiche Toast o le notifiche della barra di stato.
Una notifica toast è un messaggio che appare sulla superficie della finestra corrente solo per un momento prima di scomparire. Una notifica della barra di stato fornisce un’icona nella barra di stato con un messaggio, che l’utente può selezionare per intraprendere un’azione (come iniziare un’attività).
Di solito, una notifica della barra di stato è la tecnica migliore da usare quando un lavoro in background come il download di un file è stato completato, e l’utente può ora agire. Quando l’utente seleziona la notifica dalla vista espansa, la notifica può avviare un’attività (come visualizzare il file scaricato).
Vedi le guide per sviluppatori Toast Notifications o Status Bar Notifications per maggiori informazioni.
Gestire il 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 il servizio viene creato e distrutto, perché un servizio può essere eseguito in background senza che l’utente ne sia consapevole.
Il ciclo di vita di un servizio, da quando viene creato a quando viene distrutto, può seguire uno di questi due percorsi:
- Un servizio avviato
Il servizio viene creato quando un altro componente chiama
startService()
. Il servizio viene poi eseguito indefinitamente e deve fermarsi da solo chiamandostopSelf()
. Un altro componente può anche fermare il servizio chiamandostopService()
. Quando il servizio viene fermato, il sistema lo distrugge. - Un servizio legato
Il servizio viene creato quando un altro componente (un client) chiama
bindService()
. Il client comunica quindi con il servizio attraverso un’interfacciaIBinder
. Il client può chiudere la connessione chiamandounbindService()
. Più client possono legarsi allo stesso servizio e quando tutti si scollegano, il sistema distrugge il servizio. Il servizio non ha bisogno di fermarsi da solo.
Questi due percorsi non sono completamente separati. Potete legarvi a un servizio che è già avviato con startService()
. Per esempio, potete avviare un servizio di musica di sottofondo chiamando startService()
con un Intent
che identifica la musica da riprodurre. Più tardi, possibilmente quando l’utente vuole esercitare qualche controllo sul lettore o ottenere informazioni sulla canzone corrente, un’attività può legarsi al servizio chiamando bindService()
. In casi come questo, stopService()
o stopSelf()
non fermano effettivamente il servizio finché tutti i client non si scollegano.
Implementazione dei callback del ciclo di vita
Come un’attività, un servizio ha metodi di callback del ciclo di vita che è possibile implementare per monitorare i cambiamenti di stato del servizio ed eseguire il lavoro al momento opportuno. Il seguente skeletonservice dimostra 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 () { // The service is being created } override fun (intent: Intent?, flags: Int, startId: Int): Int { // The service is starting, due to a call to startService() return startMode } override fun (intent: Intent): IBinder? { // A client is binding to the service with bindService() return binder } override fun (intent: Intent): Boolean { // All clients have unbound with unbindService() return allowRebind } override fun (intent: Intent) { // A client is binding to the service with bindService(), // after onUnbind() has already been called } override fun () { // 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 () { // The service is being created } @Override public int (Intent intent, int flags, int startId) { // The service is starting, due to a call to return startMode; } @Override public IBinder (Intent intent) { // A client is binding to the service with return binder; } @Override public boolean (Intent intent) { // All clients have unbound with return allowRebind; } @Override public void (Intent intent) { // A client is binding to the service with , // after onUnbind() has already been called } @Override public void () { // 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()
e 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()
, si tenga presente che qualsiasi servizio, non importa come sia stato avviato, può potenzialmente permettere ai client di legarsi ad esso. Un servizio che è stato inizialmente avviato con onStartCommand()
(da un client che chiama startService()
) può ancora ricevere una chiamata a onBind()
(quando un client chiama bindService()
).
Con l’implementazione di questi metodi, è possibile monitorare questi due cicli annidati del ciclo di vita del servizio:
- L’intera vita di un servizio avviene tra il momento in cui
onCreate()
viene chiamato e il momento in cuionDestroy()
ritorna. Come un’attività, un servizio fa la sua configurazione iniziale inonCreate()
e rilascia tutte le risorse rimanenti inonDestroy()
. Per esempio, un servizio di riproduzione musicale può creare il thread dove la musica viene riprodotta inonCreate()
, e poi può fermare il thread inonDestroy()
.Nota: I metodi
onCreate()
eonDestroy()
sono chiamati per tutti i servizi, che siano creati dastartService()
obindService()
. - La vita attiva di un servizio inizia con una chiamata a
onStartCommand()
oonBind()
.Ogni metodo riceve ilIntent
che è stato passato astartService()
obindService()
.Se il servizio è avviato, la vita attiva finisce nello stesso momento in cui finisce l’intera vita (il servizio è ancora attivo anche dopo che
onStartCommand()
ritorna). Se il servizio è legato, la durata attiva termina quandoonUnbind()
ritorna.
Nota: Anche se un servizio avviato viene fermato da una chiamata a stopSelf()
o stopService()
, non c’è un callback corrispondente per il servizio (non c’è un callback onStop()
). A meno che il servizio non sia legato ad un client, il sistema lo distrugge quando il servizio viene fermato-onDestroy()
è l’unico callback ricevuto.
Per maggiori informazioni sulla creazione di un servizio che fornisce il binding, vedere il documento Bound Services, che include maggiori informazioni sul metodo onRebind()
callback nella sezione sulla Gestione del ciclo di vita di un servizio legato.