In questo tutorial esploreremo il Servizio
componente e la sua superclasse, il IntentService
. Imparerai quando e come utilizzare questo componente per creare grandi soluzioni di concorrenza per operazioni di background di lunga durata. Daremo anche uno sguardo veloce a IPC (Inter Process Communication), per imparare come comunicare con i servizi in esecuzione su diversi processi.
Per seguire questo tutorial avrai bisogno di conoscere la concorrenza su Android. Se non ne sai molto, potresti prima leggere alcuni dei nostri altri articoli sull'argomento.
Il Servizio
componente è una parte molto importante del framework di concorrenza di Android. Soddisfa la necessità di eseguire un'operazione di lunga durata all'interno di un'applicazione o fornisce alcune funzionalità per altre applicazioni. In questo tutorial ci concentreremo esclusivamente su Servizio
La capacità di svolgere attività a lungo termine e come utilizzare questo potere per migliorare la concorrenza.
UN Servizio
è un componente semplice che viene istanziato dal sistema per eseguire un lavoro di lunga durata che non dipende necessariamente dall'interazione dell'utente. Può essere indipendente dal ciclo di vita dell'attività e può anche essere eseguito su un processo completamente diverso.
Prima di immergerti in una discussione su cosa Servizio
rappresenta, è importante sottolineare che anche se i servizi sono comunemente utilizzati per operazioni in background di lunga durata e per eseguire attività su processi diversi, un Servizio
non rappresenta a Filo
o un processo. Funzionerà solo in un thread in background o in un processo diverso se è esplicitamente richiesto di farlo.
UN Servizio
ha due caratteristiche principali:
C'è molta confusione su servizi e thread. Quando un Servizio
è dichiarato, non contiene a Filo
. Di fatto, per impostazione predefinita viene eseguito direttamente sul thread principale e qualsiasi lavoro svolto su di esso può potenzialmente bloccare un'applicazione. (A meno che non sia un IntentService
, un Servizio
sottoclasse che già viene fornito con un thread di lavoro configurato).
Quindi, in che modo i servizi offrono una soluzione di concorrenza? Bene, a Servizio
non contiene un thread per impostazione predefinita, ma può essere facilmente configurato per funzionare con il proprio thread o con un pool di thread. Ne vedremo di più qui sotto.
Ignorando la mancanza di un thread incorporato, a Servizio
è una soluzione eccellente per i problemi di concorrenza in determinate situazioni. Le ragioni principali per scegliere a Servizio
su altre soluzioni di concorrenza come AsyncTask
o il framework HaMeR sono:
Servizio
può essere indipendente dai cicli di vita delle attività.Servizio
è appropriato per l'esecuzione di operazioni lunghe.Servizio
può essere riavviato per riprendere il suo lavoro.Ci sono due tipi di Servizio
, iniziato e limite.
UN iniziato il servizio è lanciato via Context.startService ()
. Generalmente esegue solo una operazione e funzionerà indefinitamente fino al termine dell'operazione, quindi si spegnerà automaticamente. In genere, non restituisce alcun risultato all'interfaccia utente.
Il servizio vincolato viene lanciato via Context.bindService ()
, e consente una comunicazione bidirezionale tra client e Servizio
. Può anche connettersi con più client. Distrugge se stesso quando non ci sono client collegati ad esso.
Per scegliere tra questi due tipi, il Servizio
deve implementare alcune callback: onStartCommand ()
da eseguire come servizio avviato, e onBind ()
per funzionare come servizio associato. UN Servizio
può scegliere di implementare solo uno di questi tipi, ma può anche adottare entrambi allo stesso tempo senza problemi.
Per utilizzare un servizio, estendere il Servizio
classificare e sovrascrivere i suoi metodi di callback, in base al tipo di Servizio
. Come accennato prima, per i servizi avviati il onStartCommand ()
il metodo deve essere implementato e per i servizi associati, il onBind ()
metodo. In realtà, il onBind ()
il metodo deve essere dichiarato per entrambi i tipi di servizio, ma può restituire null per i servizi avviati.
public class CustomService estende il servizio @ Override public int onStartCommand (Intent intent, int flags, int startId) // Esegui le tue operazioni // Il servizio non verrà terminato automaticamente restituirà Service.START_NOT_STICKY; @Nullable @Override public IBinder onBind (Intent intent) // Crea una connessione con un client // utilizzando un'interfaccia implementata su IBinder return null;
onStartCommand ()
: lanciato da Context.startService ()
. Questo è solitamente chiamato da un'attività. Una volta chiamato, il servizio può essere eseguito indefinitamente e spetta a te interromperlo, chiamando stopSelf ()
o StopService ()
.onBind ()
: chiamato quando un componente vuole connettersi al servizio. Chiamato sul sistema da Context.bindService ()
. Restituisce un IBinder
che fornisce un'interfaccia per comunicare con il cliente.Anche il ciclo di vita del servizio è importante da prendere in considerazione. Il onCreate ()
e OnDestroy ()
i metodi devono essere implementati per inizializzare e arrestare tutte le risorse o le operazioni del servizio.
Il Servizio
componente deve essere dichiarato sul manifest con il
elemento. In questa dichiarazione è anche possibile, ma non obbligatorio, impostare un diverso processo per il Servizio
correre dentro.
... ...
Per avviare un servizio iniziato è necessario chiamare Context.startService ()
metodo. Il Intento
deve essere creato con il Contesto
e il Servizio
classe. In questo caso dovrebbero essere trasmesse anche tutte le informazioni o i dati rilevanti Intento
.
Intent serviceIntent = new Intent (this, CustomService.class); // Passa i dati da elaborare sui dati del pacchetto di servizi = nuovo pacchetto (); data.putInt ("OperationType", 99); data.putString ("DownloadURL", "http://mydownloadurl.com"); serviceIntent.putExtras (dati); // Avvio del servizio startService (serviceIntent);
Nel tuo Servizio
classe, il metodo di cui dovresti preoccuparti è il onStartCommand ()
. È su questo metodo che devi chiamare qualsiasi operazione che vuoi eseguire sul servizio avviato. Elaborerai il Intento
per acquisire le informazioni inviate dal cliente. Il startID
rappresenta un ID univoco, creato automaticamente per questa specifica richiesta e il bandiere
può anche contenere informazioni aggiuntive su di esso.
@Override public int onStartCommand (Intent intent, int flags, int startId) Bundle data = intent.getExtras (); if (data! = null) int operation = data.getInt (KEY_OPERATION); // Controlla quale operazione eseguire e invia un messaggio se (operazione == OP_DOWNLOAD) // fai un download torna START_STICKY;
Il onStartCommand ()
restituisce una costante int
che controlla il comportamento:
Service.START_STICKY
: Il servizio viene riavviato se viene interrotto.Service.START_NOT_STICKY
: Il servizio non è stato riavviato.Service.START_REDELIVER_INTENT
: Il servizio viene riavviato dopo un arresto anomalo e gli intenti che verranno elaborati verranno riconsegnati.Come accennato in precedenza, è necessario arrestare un servizio avviato, altrimenti verrà eseguito a tempo indeterminato. Questo può essere fatto dal Servizio
chiamata stopSelf ()
su se stesso o da una chiamata client StopService ()
su di esso.
void someOperation () // esegue qualche operazione di lunga durata // e interrompe il servizio quando viene eseguito stopSelf ();
I componenti possono creare connessioni con i servizi, stabilendo con essi una comunicazione a due vie. Il cliente deve chiamare Context.bindService ()
, passando un Intento
, un ServiceConnection
interfaccia e a bandiera
come parametri. UN Servizio
può essere associato a più client e verrà distrutto una volta che non ci sono client connessi.
void bindWithService () Intent intent = new Intent (this, PlayerService.class); // collega con Service bindService (intent, mConnection, Context.BIND_AUTO_CREATE);
È possibile inviare Messaggio
oggetti ai servizi. Per farlo dovrai creare un Messaggero
dal lato client in a ServiceConnection.onServiceConnected
implementazione dell'interfaccia e usarlo per inviare Messaggio
oggetti al Servizio
.
private ServiceConnection mConnection = new ServiceConnection () @ Override pubblico onServiceConnected (ComponenName className, servizio IBinder) // usa l'IBinder ricevuto per creare un Messenger mServiceMessenger = new Messenger (servizio); mBound = true; @Override public void onServiceDisconnected (ComponentName arg0) mBound = false; mServiceMessenger = null; ;
È anche possibile passare una risposta Messaggero
al Servizio
per il client di ricevere messaggi. Attenzione però, perché il cliente potrebbe non essere più in giro per ricevere il messaggio del servizio. Puoi anche usare BroadcastReceiver
o qualsiasi altra soluzione di trasmissione.
gestore privato mResponseHandler = new Handler () @Override public void handleMessage (Message msg) // gestisce la risposta dal servizio; Message msgReply = Message.obtain (); msgReply.replyTo = new Messenger (mResponseHandler); prova mServiceMessenger.send (msgReply); catch (RemoteException e) e.printStackTrace ();
È importante non associarti al Servizio quando il cliente viene distrutto.
@Override protected void onDestroy () super.onDestroy (); // disconnetti dal servizio if (mBound) unbindService (mConnection); mBound = falso;
Sul Servizio
lato, è necessario implementare il Service.onBind ()
metodo, fornendo un IBinder
fornito da a Messaggero
. Questo trasmetterà una risposta handler
gestire il Messaggio
oggetti ricevuti dal cliente.
IncomingHandler (PlayerService playerService) mPlayerService = new WeakReference <> (playerService); @Override public void handleMessage (Message msg) // handle messages public IBinder onBind (Intent intent) // passa un raccoglitore usando il Messenger creato return mMessenger.getBinder (); Messenger finale mMessenger = new Messenger (new IncomingHandler (this));
Infine, è il momento di parlare di come risolvere i problemi di concorrenza utilizzando i servizi. Come accennato prima, uno standard Servizio
non contiene thread aggiuntivi e verrà eseguito sul main Filo
per impostazione predefinita. Per superare questo problema devi aggiungere un lavoratore Filo
, un pool di thread o eseguire il Servizio
su un altro processo. Puoi anche usare una sottoclasse di Servizio
chiamato IntentService
che contiene già un Filo
.
Per rendere il Servizio
eseguire su uno sfondo Filo
potresti semplicemente creare un extra Filo
ed eseguire il lavoro lì. Tuttavia, Android ci offre una soluzione migliore. Un modo per sfruttare al meglio il sistema è implementare il framework HaMeR all'interno di Servizio
, ad esempio eseguendo il ciclo a Filo
con una coda di messaggi che può elaborare i messaggi indefinitamente.
È importante capire che questa implementazione elaborerà i compiti in modo sequenziale. Se è necessario ricevere ed elaborare più attività contemporaneamente, è necessario utilizzare un pool di thread. L'utilizzo di pool di thread non rientra nell'ambito di questo tutorial e non ne parleremo oggi.
Per usare HaMeR devi fornire il Servizio
con un Looper
, un handler
e a HandlerThread
.
Looper privato mServiceLooper; servizio privatoHandler mServiceHandler; // Gestore per ricevere messaggi dalla classe finale privata del client ServiceHandler estende Handler ServiceHandler (Looper looper) super (looper); @Override public void handleMessage (Message msg) super.handleMessage (msg); // gestisce i messaggi // arrestando il servizio usando startId stopSelf (msg.arg1); @Override public void onCreate () HandlerThread thread = new HandlerThread ("ServiceThread", Process.THREAD_PRIORITY_BACKGROUND); Thread.start (); mServiceLooper = thread.getLooper (); mServiceHandler = new ServiceHandler (mServiceLooper);
Se la struttura HaMeR non ti è familiare, leggi i nostri tutorial su HaMer per Android.
Se non c'è bisogno per il Servizio
per essere tenuto in vita per molto tempo, potresti usarlo IntentService
, un Servizio
sottoclasse che è pronto per eseguire attività su thread in background. Internamente, IntentService
è un Servizio
con un'implementazione molto simile a quella sopra proposta.
Per utilizzare questa classe, tutto ciò che devi fare è estenderlo e implementare il onHandleIntent ()
, un metodo di aggancio che verrà chiamato ogni volta che un client chiama StartService ()
su questo Servizio
. È importante tenere presente che il IntentService
si fermerà non appena il suo lavoro sarà completato.
MyIntentService di classe pubblica estende IntentService public MyIntentService () super ("MyIntentService"); @Override protected void onHandleIntent (Intent intent) // handle Intents send by startService
UN Servizio
può funzionare su un completamente diverso Processi
, indipendentemente da tutte le attività che si verificano nel processo principale. Un processo ha la propria allocazione di memoria, il gruppo di thread e le priorità di elaborazione. Questo approccio può essere davvero utile quando è necessario lavorare indipendentemente dal processo principale.
La comunicazione tra diversi processi si chiama IPC (Inter Process Communication). In un Servizio
ci sono due modi principali per fare IPC: usando a Messaggero
o implementando un AIDL
interfaccia.
Abbiamo imparato come inviare e ricevere messaggi tra servizi. Tutto ciò che devi fare è usare create a Messaggero
usando il IBinder
istanza ricevuta durante il processo di connessione e usarla per inviare una risposta Messaggero
ritorno al Servizio
.
gestore privato mResponseHandler = new Handler () @Override public void handleMessage (Message msg) // gestisce la risposta dal servizio; private ServiceConnection mConnection = new ServiceConnection () @ Override pubblico onServiceConnected (ComponenName className, servizio IBinder) // usa l'IBinder ricevuto per creare un Messenger mServiceMessenger = new Messenger (servizio); Message msgReply = Message.obtain (); msgReply.replyTo = new Messenger (mResponseHandler); prova mServiceMessenger.send (msgReply); catch (RemoteException e) e.printStackTrace ();
Il AIDL
l'interfaccia è una soluzione molto potente che consente chiamate dirette Servizio
metodi in esecuzione su diversi processi ed è appropriato utilizzare quando il tuo Servizio
è davvero complesso però, AIDL
è complicato da implementare ed è usato raramente, quindi il suo uso non sarà discusso in questo tutorial.
I servizi possono essere semplici o complessi. Dipende dalle esigenze della tua applicazione. Ho cercato di coprire il maggior numero possibile di argomenti su questo tutorial, tuttavia mi sono concentrato solo sull'utilizzo dei servizi per scopi di concorrenza e ci sono più possibilità per questo componente. Voglio studiare di più, dai un'occhiata alla documentazione e alle guide di Android.
A presto!