Tutti coloro che cercano lo sviluppo di Android scopre l'importanza della concorrenza. L'unico modo per creare un'app reattiva è lasciare il thread UI il più libero possibile, lasciando che tutto il duro lavoro sia svolto in modo asincrono dai thread in background.
A causa del design di Android, la gestione dei thread utilizzando solo il java.lang.Thread
e java.util.concurrent
i pacchetti potrebbero essere davvero difficili. Usare i pacchetti di threading di basso livello con Android significa che devi preoccuparti di una sincronizzazione complicata per evitare condizioni di gara. Fortunatamente la gente di Google ha fatto il duro lavoro e ha creato alcuni ottimi strumenti per semplificare il nostro lavoro: AsyncTask
, IntentService
, caricatore
, AsyncQueryHandler
e CursorLoader
sono tutti utili, così come le classi HaMeR handler
, Messaggio
, e Runnable
. Ci sono molte ottime opzioni tra cui scegliere, ognuna con i suoi pro e contro.
Molto è stato detto a proposito del AsyncTask
oggetto, e molte persone lo usano come un pallottola d'argento soluzione per concorrenza su Android. È estremamente utile per operazioni brevi, facili da implementare e probabilmente l'approccio più popolare alla concorrenza su Android. Se vuoi saperne di più AsyncTask
, controlla i seguenti post di Envato Tuts +.
però, AsyncTask
non dovrebbe essere l'unico strumento sulla tua cintura degli attrezzi.
Per operazioni di lunga durata, problemi complessi di concorrenza o per ottenere maggiore efficienza in alcune situazioni, è necessario scegliere un'altra soluzione. Se hai bisogno di più flessibilità o efficienza rispetto a AsyncTask
fornisce, è possibile utilizzare l'HaMeR (handler
, Messaggio
& Runnable
) struttura.In questo tutorial esploreremo il framework HaMeR, uno dei più potenti modelli di concorrenza disponibili su Android, e impareremo quando e come usarlo. In un tutorial di follow-up, ti mostrerò come codificare un'applicazione per provare alcune possibilità di HaMeR.
La seguente sezione introdurrà l'importanza dei thread in background per il sistema Android. Se hai familiarità con questo concetto, sentiti libero di saltarlo e vai direttamente alla discussione del framework HaMeR nella sezione 3.
Quando viene avviata un'applicazione Android, il primo thread generato dal suo processo è il thread principale, noto anche come thread UI, che è responsabile della gestione dell'intera logica dell'interfaccia utente. Questo è il thread più importante di un'applicazione. È responsabile della gestione di tutte le interazioni dell'utente e anche del "collegamento" delle parti mobili dell'applicazione. Android lo prende molto seriamente, e se il tuo thread dell'interfaccia utente è bloccato a lavorare su un'attività per più di qualche secondo, l'app si bloccherà.
[Il thread dell'interfaccia utente] è molto importante perché è responsabile degli eventi di distribuzione per i widget dell'interfaccia utente appropriati, inclusi gli eventi di disegno. È anche il thread in cui l'applicazione interagisce con i componenti del toolkit dell'interfaccia utente Android (componenti dalandroid.widget
eandroid.view
pacchi). Di conseguenza, il thread principale viene anche chiamato il thread dell'interfaccia utente. - Processi e discussioni, Guida per gli sviluppatori Android
Il problema è che quasi tutto il codice in un'applicazione Android verrà eseguito sul thread dell'interfaccia utente per impostazione predefinita. Poiché le attività su un thread vengono eseguite in sequenza, ciò significa che l'interfaccia utente potrebbe "bloccarsi", non rispondere mentre sta elaborando un altro lavoro.
Le attività a esecuzione prolungata richiamate nell'interfaccia utente saranno probabilmente fatali per la tua app e verrà visualizzata una finestra di dialogo ANR (Application Not Responding). Anche le piccole attività possono compromettere l'esperienza dell'utente, quindi l'approccio corretto è quello di rimuovere il più lavoro possibile dal thread dell'interfaccia utente utilizzando thread in background. Come detto prima, ci sono molti modi per risolvere questo problema, e esploreremo il framework HaMeR, una delle soluzioni core fornite da Android per affrontare questa situazione.
Il framework HaMeR consente ai thread in background di inviare messaggi o post di esecuzione al thread dell'interfaccia utente e a qualsiasi altro thread MessageQueue
tramite i gestori. HaMeR si riferisce a handler
, Messaggio
, & Runnable
. Ci sono anche altre importanti classi che funzionano insieme con HaMeR: Looper
e MessageQueue
. Insieme, questi oggetti sono responsabili di facilitare la gestione dei thread su Android, occupandosi della sincronizzazione e fornendo metodi semplici per i thread in background per comunicare con l'interfaccia utente e con altri thread.
Ecco come si integrano le classi nel framework HaMeR.
Looper
esegue un loop di messaggi su un thread usando il MessageQueue
.MessageQueue
contiene un elenco di messaggi che devono essere inviati dal Looper
.handler
consente l'invio e l'elaborazione di Messaggio
e Runnable
al MessageQueue
. Può essere usato per inviare ed elaborare messaggi tra thread.Messaggio
contiene una descrizione e dati che possono essere inviati a un gestore.Runnable
rappresenta un compito da eseguire.Con il framework HaMeR, i thread possono inviare messaggi o pubblicare oggetti eseguibili su se stessi o sul thread dell'interfaccia utente. HaMeR promuove anche le interazioni thread in background tramite handler
.
handler
è il cavallo di battaglia HaMeR. È responsabile dell'invio Messaggio
(messaggio dati) e post Runnable
(messaggio di attività) oggetti al MessageQueue
associato a a Filo
. Dopo aver consegnato le attività alla coda, il gestore riceve gli oggetti dal Looper
ed elabora i messaggi al momento opportuno usando il handler
associato ad esso.
UN handler
può essere usato per inviare o pubblicare Messaggio
e Runnable
oggetti tra i thread, purché tali thread condividano lo stesso processo. Altrimenti sarà necessario creare una Inter Process Communication (IPC), una metodologia che supera lo scopo di questo tutorial.
UN handler
deve sempre essere associato a a Looper
, e questa connessione deve essere fatta durante la sua istanziazione. Se non fornisci un Looper
al handler
, sarà legato alla corrente Filo
'S Looper
.
// Il gestore utilizza il gestore del gestore del looper corrente di Thread = new Handler (); // Il gestore usa il Looper fornisce Handler handler = new Handler (Looper);
Tieni presente che a handler
è sempre associato a a Looper
, e questa connessione è permanente e non può essere modificata una volta stabilito. Tuttavia, a Looper
La discussione di 's può avere associazioni con multipli handler
S. È anche importante notare che a Looper
deve essere attivo prima della sua associazione con a handler
.
Il lavoro cooperativo tra Looper
e MessageQueue
in un thread Java crea un ciclo di attività che vengono elaborate in sequenza. Tale ciclo manterrà il thread attivo mentre attende di ricevere più attività. Un thread può avere solo uno Looper
e uno MessageQueue
associato ad esso; tuttavia, possono esserci più gestori per ogni thread. I gestori sono responsabili dell'elaborazione delle attività in coda e ogni attività sa quale gestore è responsabile dell'elaborazione.
L'interfaccia utente o thread principale è l'unico tipo di thread che per impostazione predefinita ha già un handler
, un Looper
, e a MessageQueue
. Altri thread devono essere preparati con tali oggetti prima che possano lavorare con il framework HaMeR. Per prima cosa dobbiamo creare un Looper
che include già un MessageQueue
e collegalo al filo. Puoi farlo con una sottoclasse di Filo
, come segue.
// Preparazione di un thread per HaMeR class LooperThread estende Thread public Handler mHandler; public void run () // aggiunta e preparazione di Looper Looper.prepare (); // l'istanza di Handler sarà associata a Looper di Thread mHandler = new Handler () public void handleMessage (Message msg) // process incoming messages here; // Avvio del ciclo della coda dei messaggi utilizzando Looper Looper.loop ();
Tuttavia, è più semplice usare una classe helper chiamata HandlerThread
, che ha un Looper
e a MessageQueue
costruito in un Java Filo
ed è pronto a ricevere un gestore.
// La classe HandlerThread include una classe pubblica di Looper funzionante HamerThread estende HandlerThread // è sufficiente aggiungere il gestore Handler Private Handler; HamerThread pubblico (nome stringa) super (nome);
Il Runnable
è un'interfaccia Java che ha molti usi. Potrebbe essere inteso come un singolo compito da eseguire su a Filo
. Ha un unico metodo che deve essere implementato, Runnable.run ()
, per eseguire l'operazione.
// Dichiarare un Runnable Runnable r = new Runnable () @ Override public void run () // l'attività va qui;
Ci sono più opzioni per pubblicare un Runnable
a handler
.
Handler.post (Runnable r)
: Aggiungi il Runnable
al MessageQueue
.Handler.postAtFrontOfQueue (Runnable r)
: Aggiungi il Runnable
nella parte anteriore del MessageQueue
.handler.postAtTime (Runnable r, long timeMillis)
: Aggiungi il Runnable
sul MessageQueue
essere chiamato in un momento specifico.handler.postDelayed (Runnable r, long delay)
: Aggiungi il Runnable
alla coda da chiamare dopo che è trascorso un determinato periodo di tempo.// postare un Runnable su un gestore Handler handler = new Handler (); handler.post (new Runnable () @Override public void run () // task goes here);
È anche possibile utilizzare il gestore dell'interfaccia utente predefinito per pubblicare un Runnable
chiamata Activity.runOnUiThread ()
.
// postare eseguibile utilizzando il gestore dell'interfaccia utente Activity.runOnUiThread (new Runnable () @Override public void run () // task da eseguire);
È importante tenere a mente alcune cose Runnable
S. A differenza di Messaggio
, un Runnable
non può essere riciclato, una volta che il suo lavoro è finito, è morto. Poiché fa parte di un pacchetto Java standard, a Runnable
non dipende da handler
e può essere chiamato su uno standard Filo
usando il Runnable.run ()
metodo. Tuttavia, questo approccio non ha nulla a che fare con il framework HaMeR e non condividerà nessuno dei suoi vantaggi.
Il Messaggio
oggetto definisce un messaggio contenente una descrizione e alcuni dati arbitrari che possono essere inviati ed elaborati tramite handler
. Il Messaggio
è identificato con un int
definito su Message.what ()
. Il Messaggio
può tenere due altri int
argomenti e an Oggetto
per memorizzare diversi tipi di dati.
Message.what
: int
identificando il Messaggio
Message.arg1
: int
argomento arbitrarioMessage.arg2
: int
argomento arbitrarioMessage.obj
: Oggetto
per memorizzare diversi tipi di dati Quando è necessario inviare un messaggio, invece di crearne uno da zero, l'approccio consigliato è quello di recuperare uno riciclato direttamente dal pool globale con il Message.obtain ()
o Handler.obtainMessage ()
comandi. Ci sono alcune versioni differenti di quei metodi che ti permettono di ottenere un Messaggio
in base alle tue necessità.
Un uso comune di Handler.obtainMessage ()
è quando devi inviare un messaggio a un thread in background. Userai il handler
associato a quel thread Looper
ottenere un Messaggio
e inviarlo al thread in background, come nell'esempio seguente.
int what = 0; String hello = "Hello!"; // Ottenimento del messaggio associato allo sfondo Thread Message msg = handlerBGThread.obtainMessage (what, hello); // Invio del messaggio allo sfondo Thread handlerBGThread.sendMessage (msg);
Ci sono molti metodi interessanti su Messaggio
classe, e ti consiglio di dare un'occhiata più da vicino alla documentazione.
invia messaggio()
OpzioniAllo stesso modo di come possiamo pubblicare Runnable
s, ci sono più opzioni da inviare Messaggio
S:
Handler.sendMessage (messaggio msg)
: aggiungere un Messaggio
al MessageQueue
.Handler.sendMessageAtFrontOfQueue (messaggio msg)
: aggiungere un Messaggio
alla parte anteriore del MessageQueue
.Handler.sendMessageAtTime (messaggio msg, long timeInMillis)
: aggiungere un Messaggio
alla coda in un momento specifico.Handler.sendMessageDelayed (messaggio msg, long timeInMillis)
: aggiungere un Messaggio
alla coda dopo che è trascorso un determinato periodo di tempo.Il Messaggio
oggetti spediti da Looper
vengono elaborati dal gestore con il metodo Handler.handleMessage
. Tutto ciò che devi fare è estendere il handler
classificare e sovrascrivere questo metodo per elaborare i messaggi.
public class MessageHandler extends Handler @Override public void handleMessage (Message msg) switch (msg.what) // handle 'Hello' msg case 0: String hello = (String) msg.obj; System.out.println (ciao); rompere;
Il framework HaMeR può aiutare a migliorare il codice concorrente dell'applicazione Android. Può sembrare confuso in un primo momento rispetto alla semplicità di un AsyncTask
, ma l'apertura di HaMeR può essere un vantaggio, se usato correttamente.
Ricorda:
Handler.post ()
i metodi vengono utilizzati quando i mittenti sanno quali operazioni eseguire.Handler.sendMessage()
i metodi vengono utilizzati quando il ricevitore sa quale operazione eseguire.Per ulteriori informazioni sul threading in Android, potresti essere interessato al libro Efficient Android Threading: tecniche di elaborazione asincrona per applicazioni Android di Anders Goransson.
Nel prossimo tutorial, continueremo a esplorare il framework HaMeR con un approccio pratico, creando un'applicazione che mostri diversi modi di utilizzare questo framework di concorrenza Android. Creeremo questa app da zero, provando diverse possibilità come la comunicazione tra discussioni
, parlando con il thread dell'interfaccia utente, oltre a inviare messaggi e post Runnable
s con ritardi.
A presto!