Questo articolo copre Android Looper, Handler e HandlerThread. Questi sono tra gli elementi costitutivi del sistema operativo Android.
Nella mia esperienza personale, li ho usati in un contesto molto limitato fino a poco tempo fa. Il mio caso d’uso riguardava l’invio di attività al thread principale/ui, principalmente per aggiornare l’interfaccia utente da qualsiasi altro thread. Gli altri aspetti dell’operazione multi-threaded sono stati gestiti tramite modi alternativi come ThreadPoolExecutor, IntentService e AsyncTask.
Il MultiThreading e l’esecuzione delle attività sono soggetti vecchi. Java stesso ha java.util.pacchetto simultaneo e Fork/Join framework per facilitarlo. Diverse librerie sono state scritte per semplificare le operazioni asincrone. RxJava è la libreria più popolare oggi per la programmazione reattiva e la progettazione di un’applicazione asincrona.
Quindi, perché sto scrivendo sulla vecchia scuola?
Looper
, Handler
, e HandlerThread
sono il modo in cui Android risolve i problemi della programmazione asincrona. Non sono vecchia scuola, ma una struttura ordinata su cui è costruito un complesso framework Android.
Per i nuovi sviluppatori, si consiglia vivamente di comprendere i principi alla base di loro e uno esperto dovrebbe rivisitare questo argomento per ricordare i dettagli minori.
Ho anche creato un video tutorial per questo argomento, e consiglio vivamente di guardarlo. Clicca qui per guardare ora.
Casi d’uso:
- Il thread principale in Android è costruito con un
Looper
eHandlers
. Quindi, la comprensione di esso è essenziale per creare un’interfaccia utente reattiva sbloccata. - Gli sviluppatori che scrivono librerie non possono permettersi di utilizzare librerie di terze parti a causa delle dimensioni della libreria. Quindi, per loro, l’opzione migliore è utilizzare la risorsa disponibile esistente. Scrivere la propria soluzione potrebbe non sempre ottenere quel livello di efficienza e ottimizzazione.
- Lo stesso argomento può essere fatto anche per le aziende/individui che spediscono SDK. I client possono avere varie implementazioni, ma tutti condivideranno le comuni API del framework Android.
- Comprenderli completamente migliorerà la capacità di seguire l’SDK di Android e le classi di pacchetti in generale.
Iniziamo l’esplorazione/revisione con un questionario.
Mi aspetto che il lettore abbia la conoscenza di base dei thread java. Se avete bisogno, quindi ottenere una rapida panoramica di thread java e Runnable.
Qual è il problema con il thread java?
I thread Java sono monouso e muoiono dopo aver eseguito il metodo run.
Possiamo migliorare su di esso?
Il filo è un’arma a doppio taglio. Possiamo accelerare l’esecuzione distribuendo le attività tra i thread di esecuzione, ma possiamo anche rallentarlo quando i thread sono in eccesso. La creazione di thread di per sé è un overhead. Quindi, l’opzione migliore è avere un numero ottimale di thread e riutilizzarli per l’esecuzione delle attività.
Modello per la riusabilità del filo:
- Il thread viene mantenuto vivo, in un ciclo tramite il metodo
run()
. - L’attività viene eseguita in serie da quel thread e viene mantenuta in una coda (MessageQueue).
- Il thread deve essere terminato al termine.
Qual è il modo di farlo di Android?
Il modello di cui sopra è implementato in Android tramite Looper
, Handler
e HandlerThread
. Il sistema può essere visualizzato come un veicolo come nella copertina dell’articolo.
-
MessageQueue
è una coda che ha attività chiamate messaggi che dovrebbero essere elaborati. -
Handler
accoda l’attività inMessageQueue
usandoLooper
e li esegue anche quando l’attività esce daMessageQueue
. -
Looper
è un worker che mantiene vivo un thread, esegue il loop diMessageQueue
e invia messaggi al corrispondentehandler
da elaborare. - Infine
Thread
viene terminato chiamando il metodoquit()
di Looper.
Un thread può avere un solo Looper unico e può avere molti gestori unici ad esso associati.
Creazione di Looper e MessageQueue per un thread:
Un thread ottiene un Looper
e MessageQueue
chiamando Looper.prepare()
dopo la sua esecuzione. Looper.prepare()
identifica il thread chiamante, crea un oggetto Looper
e MessageQueue
e associa thread
a loro nella classe di archiviazione ThreadLocal
. Looper.loop()
deve essere chiamato per avviare il looper associato. Allo stesso modo, looper
deve essere terminato esplicitamente tramite looper.quit()
.
class LooperThread extends Thread { public Handler mHandler; public void run() { Looper.prepare(); mHandler = new Handler() { public void handleMessage(Message msg) { // process incoming messages here // this will run in non-ui/background thread } }; Looper.loop(); } }
Creazione del gestore per un thread:
Un Handler
viene implicitamente associato al thread che lo istanzia tramite il thread Looper
, ma possiamo legarlo esplicitamente a un thread passando il thread looper
nel costruttore del Handler
.
handler = new Handler() {@Overridepublic void handleMessage(Message msg) { // process incoming messages here // this will run in the thread, which instantiates it }};
L’invio di messaggi al MessageQueue
tramite Handler
può essere fatto da due modalità:
-
Message
: È una classe che definisce vari metodi utili per gestire i dati dei messaggi. Per inviare un oggetto impostiamo la variabile obj.
Message msg = new Message();msg.obj = "Ali send message";handler.sendMessage(msg);
Una panoramica dettagliata della classe Message
può essere trovata qui: https://developer.android.com/reference/android/os/Message.html
Runnable
: Un runnable può anche essere pubblicato nel MessageQueue
. Es: pubblicazione ed esecuzione di un’attività nel thread principale.
new Handler(Looper.getMainLooper()).post(new Runnable() {@Overridepublic void run() { // this will run in the main thread }});
Nell’esempio precedente, creiamo un Handler
e forniamo Looper
associato al thread principale. Questo associa questo gestore al thread principale. Quando pubblichiamo Runnable
, viene messo in coda nel thread principale MessageQueue
e quindi eseguito nel thread principale.
Il gestore è in grado di manipolare i messaggi in un’ampia varietà di modi, che possono essere trovati qui: https://developer.android.com/reference/android/os/Handler.html
Creare un proprio thread e fornire Lopper
e MessageQueue
non è il modo giusto per affrontare il problema. Quindi, Android ha fornito HandlerThread
(sottoclasse di Thread
) per semplificare il processo. Internamente fa le stesse cose che abbiamo fatto ma in modo robusto. Quindi, usa sempre HandlerThread
.
Uno dei modi per creare HandlerThread è sottoclassarlo e la maggior parte delle volte utilizzerai questo metodo.
private class MyHandlerThread extends HandlerThread { Handler handler; public MyHandlerThread(String name) { super(name); } @Override protected void onLooperPrepared() { handler = new Handler(getLooper()) { @Override public void handleMessage(Message msg) { // process incoming messages here // this will run in non-ui/background thread } }; }}
Nota: Abbiamo istanziato il Gestore quando viene chiamato onLooperPrepared()
. Quindi, quel Handler
può essere associato a quel Looper
.
-
Looper
viene preparato solo dopo HandlerThreadstart()
viene chiamato cioè dopo che il thread è in esecuzione. - A
Handler
può essere associato aHandlerThread
, solo dopo che è stato preparatoLooper
.
Altro modo per creare HandlerThread:
HandlerThread handlerThread = new HandlerThread("MyHandlerThread");handlerThread.start();Handler handler = new Handler(handlerThread.getLooper());
Nota: HandlerThread deve chiamare myHandlerThread.quit()
per liberare le risorse e interrompere l’esecuzione del thread.
Suggerirei di praticare i codici di cui sopra, in modo da poter cogliere i loro piccoli dettagli.
Ho creato un progetto di esempio per la simulazione dell’ufficio postale. L’ufficio postale è costruito su HandlerThread e i clienti comunicano con l’aiuto dell’ufficio postale. Una classe Simulator crea pochi Bot client e delega la loro comunicazione a MainActivity, che la rende in un feed live.
Il link a questo esempio
Ho anche creato un video tutorial per questo argomento, e consiglio vivamente di guardarlo. Clicca qui per guardare ora.
Inoltre, diventiamo amici su Twitter, Linkedin, Github e Facebook.
Imparare è un viaggio, impariamo insieme!