acest articol se referă la Android Looper, Handler și HandlerThread. Acestea sunt printre blocurile de construcție ale sistemului de operare Android.
în propria mea experiență, le-am folosit într-un context foarte limitat până de curând. Cazul meu de utilizare a implicat trimiterea sarcinilor la firul principal / ui, în primul rând pentru a actualiza interfața de utilizare din orice alt fir. Celelalte aspecte ale operațiunii multi-threaded au fost tratate prin moduri alternative, cum ar fi ThreadPoolExecutor, IntentService, și AsyncTask.
MultiThreading și task running sunt subiecte vechi. Java în sine are java.util.pachet concurente și furculiță / se alăture cadru pentru a facilita. Mai multe biblioteci au fost scrise pentru a eficientiza operațiunile asincrone. RxJava este cea mai populară bibliotecă de astăzi pentru programarea reactivă și proiectarea unei aplicații asincrone.
deci, de ce scriu despre vechea școală?
Looper
, Handler
, și HandlerThread
sunt modul Android de a rezolva problemele programării asincrone. Nu sunt școală veche, ci o structură îngrijită pe care este construit un cadru android complex.
pentru dezvoltatorii noi, este foarte recomandat să înțeleagă principiile din spatele lor și cu experiență unul ar trebui să revizuiască acest subiect pentru a aminti detaliile minore.
am creat și un tutorial video pentru acest subiect și vă recomand să îl urmăriți. Click aici pentru a viziona acum.
Cazuri De Utilizare:
- firul principal din Android este construit cu un
Looper
șiHandlers
. Deci, înțelegerea este esențială pentru a crea un UI receptiv deblocat. - dezvoltatorii care scriu biblioteci nu își pot permite să utilizeze biblioteci terțe din cauza dimensiunii bibliotecii. Deci, pentru ei, cea mai bună opțiune este utilizarea resurselor disponibile existente. Scrierea soluției proprii pentru aceasta nu poate obține întotdeauna acel nivel de eficiență și optimizare.
- același argument poate fi făcut și pentru companiile/persoanele fizice care expediază SDK-uri. Clienții pot avea implementări variate, dar toți vor împărtăși API-urile comune Android framework.
- înțelegerea lor pe deplin va spori capacitatea de a urma SDK-ul Android și clasele de pachete în general.
să începem explorarea/revizuirea cu un chestionar.
mă aștept ca cititorul să aibă înțelegerea de bază a firelor java. Dacă aveți nevoie, obțineți o privire de ansamblu rapidă asupra java Thread și Runnable.
care este problema cu java thread?
firele Java sunt doar o singură utilizare și mor după executarea metodei sale de rulare.
îl putem îmbunătăți?
firul este o sabie cu două tăișuri. Putem accelera execuția prin distribuirea sarcinilor între firele de execuție, dar o putem încetini și atunci când firele sunt în exces. Crearea firului în sine este o deasupra capului. Deci, cea mai bună opțiune este să aveți un număr optim de fire și să le reutilizați pentru executarea sarcinilor.
Model pentru reutilizarea firului:
- firul este ținut în viață, într-o buclă prin metoda
run()
. - sarcina este executată în serie de acel fir și este menținută într-o coadă (MessageQueue).
- firul trebuie terminat când ați terminat.
care este modul Android de a face acest lucru?
modelul de mai sus este implementat în Android prin Looper
, Handler
și HandlerThread
. Sistemul poate fi vizualizat ca un vehicul ca în coperta articolului.
-
MessageQueue
este o coadă care are sarcini numite mesaje care ar trebui procesate. -
Handler
enqueues sarcina înMessageQueue
folosindLooper
și, de asemenea, le execută atunci când sarcina iese dinMessageQueue
. -
Looper
este un lucrător care ține un fir în viață, bucle prinMessageQueue
și trimite mesaje lahandler
corespunzător pentru a procesa. - în cele din urmă
Thread
se termină apelând metodaquit()
a lui Looper.
un fir poate avea un singur Looper unic și poate avea multe Handlere unice asociate cu acesta.
crearea Looper și MessageQueue pentru un fir:
un fir devine un Looper
și MessageQueue
apelând Looper.prepare()
după rularea acestuia. Looper.prepare()
identifică firul de apelare, creează un obiect Looper
și MessageQueue
și asociază thread
cu ele în clasa de stocare ThreadLocal
. Looper.loop()
trebuie apelat pentru a porni looper-ul asociat. În mod similar, looper
trebuie reziliat în mod explicit prin 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(); } }
crearea Handler-ului pentru un fir:
un Handler
devine implicit asociat cu firul care îl instanțiază prin firul Looper
, dar îl putem lega în mod explicit de un fir trecând firul looper
în constructorul Handler
.
handler = new Handler() {@Overridepublic void handleMessage(Message msg) { // process incoming messages here // this will run in the thread, which instantiates it }};
trimiterea mesajelor către MessageQueue
prin Handler
se poate face prin două moduri:
-
Message
: este o clasă care definește diferite metode utile pentru a face față datelor mesajelor. Pentru a trimite un obiect, setăm variabila obj.
Message msg = new Message();msg.obj = "Ali send message";handler.sendMessage(msg);
prezentare detaliată a clasei Message
poate fi găsită aici: https://developer.android.com/reference/android/os/Message.html
Runnable
: un runnable poate fi, de asemenea, postat în MessageQueue
. Ex: postarea și rularea unei sarcini în firul principal.
new Handler(Looper.getMainLooper()).post(new Runnable() {@Overridepublic void run() { // this will run in the main thread }});
în exemplul de mai sus, vom crea un Handler
și să furnizeze Looper
asociate cu firul principal. Acest lucru asociază acest handler la firul principal. Când postăm Runnable
, acesta se află în coada de așteptare în firul principal MessageQueue
și apoi executat în firul principal.
Handler este capabil de manipulare mesaj într-o mare varietate de moduri, care pot găsite aici: https://developer.android.com/reference/android/os/Handler.html
crearea unui fir propriu și furnizarea Lopper
și MessageQueue
nu este modalitatea corectă de a rezolva problema. Deci, Android a furnizat HandlerThread
(subclasa Thread
) pentru a eficientiza procesul. Pe plan intern face aceleași lucruri pe care le-am făcut, dar într-un mod robust. Deci, utilizați întotdeauna HandlerThread
.
una dintre modalitățile de a crea HandlerThread este să o subclasați și de cele mai multe ori veți folosi această metodă.
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 } }; }}
Notă: Am instanțiat Handler atunci când onLooperPrepared()
este numit. Deci, acel Handler
poate fi asociat cu acel Looper
.
-
Looper
este pregătit numai după HandlerThread ‘ sstart()
se numește adică după ce firul rulează. - un
Handler
poate fi asociat cu unHandlerThread
, numai după ce este pregătitLooper
.
alt mod de a crea HandlerThread:
HandlerThread handlerThread = new HandlerThread("MyHandlerThread");handlerThread.start();Handler handler = new Handler(handlerThread.getLooper());
notă: HandlerThread trebuie să apeleze myHandlerThread.quit()
pentru a elibera resursele și a opri executarea firului.
aș sugera practicarea codurilor de mai sus, astfel încât să puteți înțelege micile lor detalii.
am creat un exemplu de proiect pentru simularea Oficiului Poștal. Oficiul Poștal este construit pe Handlerfir și clienții comunică cu ajutorul Oficiului Poștal. O clasă de Simulator creează puțini roboți clienți și delegă comunicarea lor Principaluluiactivitate, care o face într-un feed live.
link-ul de la acest exemplu
am creat, de asemenea, un tutorial video pentru acest subiect, și am foarte recomanda sa ma uit la ea. Click aici pentru a viziona acum.
de asemenea, să devenim prieteni pe Twitter, Linkedin, Github și Facebook.
învățarea este o călătorie, să învățăm împreună!