Iedereen die Android-ontwikkeling probeert, ontdekt het belang van concurrency. De enige manier om een responsieve app te maken is door de UI-thread zo vrij mogelijk te laten, zodat al het harde werk asynchroon door achtergronddraden wordt gedaan.
Vanwege het ontwerp van Android, het beheren van threads met alleen de java.lang.Thread
en java.util.concurrent
pakketten kunnen heel moeilijk zijn. Het gebruik van de low-level threading-pakketten met Android betekent dat je je zorgen moet maken over veel lastige synchronisatie om raceomstandigheden te voorkomen. Gelukkig hebben de mensen bij Google het harde werk gedaan en een aantal geweldige hulpmiddelen gebouwd om ons werk gemakkelijker te maken: AsyncTask
, IntentService
, lader
, AsyncQueryHandler
en CursorLoader
zijn allemaal nuttig, evenals de HaMeR-klassen handler
, Bericht
, en uitvoerbare
. Er zijn veel geweldige opties om uit te kiezen, elk met zijn voor- en nadelen.
Er is veel gezegd over de AsyncTask
object, en veel mensen gebruiken het als een zilveren kogel oplossing voor concurrency op Android. Het is uitermate handig voor korte bewerkingen, eenvoudig te implementeren en waarschijnlijk de meest populaire benadering voor gelijktijdigheid op Android. Als je meer wilt weten over AsyncTask
, bekijk de volgende Envato Tuts + berichten.
Echter, AsyncTask
zou niet het enige gereedschap op uw gereedschapsriem moeten zijn.
Voor langdurige operaties, voor complexe concurrency-problemen, of om in sommige situaties meer efficiëntie te bereiken, moet u een andere oplossing kiezen. Als u meer flexibiliteit of efficiëntie nodig hebt dan AsyncTask
biedt, je zou de HaMeR kunnen gebruiken (handler
, Bericht
& uitvoerbare
) kader.In deze tutorial verkennen we het HaMeR-framework, een van de krachtigste concurrency-modellen die beschikbaar zijn op Android, en we leren wanneer en hoe het te gebruiken. In een vervolg-zelfstudie laat ik je zien hoe je een applicatie kunt coderen om enkele mogelijkheden van HaMeR uit te proberen.
In het volgende gedeelte wordt het belang van achtergrondthreads voor het Android-systeem geïntroduceerd. Als u bekend bent met dit concept, kunt u dit overslaan en direct doorgaan naar de bespreking van het HaMeR-framework in sectie 3.
Wanneer een Android-toepassing wordt gestart, is de eerste thread die wordt voortgebracht door het proces de rode draad, ook wel de UI-thread genoemd, die verantwoordelijk is voor het verwerken van alle logica van de gebruikersinterface. Dit is de belangrijkste thread van een applicatie. Het is verantwoordelijk voor het afhandelen van alle gebruikersinteracties en het 'verbinden' van de bewegende delen van de toepassing. Android neemt dit zeer serieus, en als je UI-thread vastzit aan een taak die langer dan een paar seconden duurt, crasht de app.
[De UI-thread] is erg belangrijk omdat deze de leiding heeft over het verzenden van gebeurtenissen naar de juiste widgets voor de gebruikersinterface, inclusief tekengebeurtenissen. Het is ook de rode draad waarop uw applicatie interageert met componenten uit de Android UI-toolkit (componenten uit deandroid.widget
enandroid.view
pakketjes). Als zodanig wordt de hoofdthread soms ook de UI-thread genoemd. - Processen en threads, Android-ontwikkelaarshandleiding
Het probleem is dat bijna alle code in een Android-applicatie standaard wordt uitgevoerd op de UI-thread. Aangezien de taken op een thread sequentieel worden uitgevoerd, betekent dit dat uw gebruikersinterface kan 'bevriezen' en niet meer reageert terwijl deze een ander werk verwerkt.
Langdurige taken die in de gebruikersinterface worden uitgevoerd, zijn waarschijnlijk dodelijk voor uw app en er verschijnt een ANR (Application Not Responding) -dialoogvenster. Zelfs kleine taken kunnen de gebruikerservaring in gevaar brengen, daarom is de juiste aanpak om zoveel mogelijk werk van de UI-thread te verwijderen met behulp van achtergrondthreads. Zoals eerder gezegd, zijn er veel manieren om dit probleem op te lossen en zullen we het HaMeR-framework verkennen, een van de kernoplossingen van Android om deze situatie aan te pakken.
Het HaMeR-raamwerk staat achtergronddraden toe om berichten te verzenden of loopbare berichten naar de UI-thread en naar andere threads te posten message queue
via handlers. HaMeR verwijst naar handler
, Bericht
, & uitvoerbare
. Er zijn ook enkele andere belangrijke klassen die samenwerken met de HaMeR: lussenmaker
en message queue
. Samen zijn deze objecten verantwoordelijk voor het vergemakkelijken van threadbeheer op Android, zorgen ze voor synchronisatie en bieden ze eenvoudige methoden voor achtergrondthreads om te communiceren met de gebruikersinterface en met andere threads.
Dit is hoe de klassen in het HaMeR-framework bij elkaar passen.
lussenmaker
voert een berichtenlus uit op een thread met behulp van de message queue
.message queue
bevat een lijst met berichten die moeten worden verzonden door de lussenmaker
.handler
staat het verzenden en verwerken van Bericht
en uitvoerbare
naar de message queue
. Het kan worden gebruikt om berichten tussen threads te verzenden en te verwerken.Bericht
bevat een beschrijving en gegevens die naar een afhandelaar kunnen worden verzonden.uitvoerbare
staat voor een taak die moet worden uitgevoerd.Met het HaMeR-framework kunnen threads berichten verzenden of uitvoerbare objecten naar zichzelf of naar de UI-thread verzenden. HaMeR promoot ook interacties met achtergrondthreads via handler
.
handler
is het werkpaard van HaMeR. Het is verantwoordelijk voor het verzenden Bericht
(gegevensbericht) en plaatsen uitvoerbare
(taakbericht) objecten naar de message queue
geassocieerd met een Draad
. Nadat de taken aan de wachtrij zijn geleverd, ontvangt de afhandelaar de objecten van de lussenmaker
en verwerkt de berichten op het juiste moment met behulp van de handler
geassocieerd.
EEN handler
kan worden gebruikt om te verzenden of te posten Bericht
en uitvoerbare
objecten tussen threads, zolang dergelijke threads hetzelfde proces delen. Anders zal het nodig zijn om een Inter Process Communication (IPC) te creëren, een methodiek die de reikwijdte van deze tutorial overtreft.
EEN handler
moet altijd worden geassocieerd met een lussenmaker
, en deze verbinding moet gemaakt worden tijdens zijn instantiatie. Als u geen a verstrekt lussenmaker
naar de handler
, het zal gebonden zijn aan de stroom Draad
's lussenmaker
.
// Handler gebruikt de huidige Handler-handler van Thread = nieuwe Handler (); // Handler gebruikt de Looper biedt Handler handler = nieuwe Handler (Looper);
Houd er rekening mee dat a handler
wordt altijd geassocieerd met een lussenmaker
, en deze verbinding is permanent en kan niet worden gewijzigd als deze eenmaal is vastgesteld. Echter, een lussenmaker
de thread kan associaties hebben met meerdere handler
s. Het is ook belangrijk op te merken dat een lussenmaker
moet actief zijn voordat het wordt geassocieerd met een handler
.
Het samenwerkingsverband tussen lussenmaker
en message queue
in een Java-thread maakt een lus van taken die opeenvolgend worden verwerkt. Zo'n lus zal de draad in leven houden terwijl hij wacht om meer taken te ontvangen. Een thread kan er maar één hebben lussenmaker
en een message queue
ermee geassocieerd; er kunnen echter voor elke thread meerdere handlers zijn. De handlers zijn verantwoordelijk voor het verwerken van de taken in de wachtrij en elke taak weet welke handler verantwoordelijk is voor de verwerking ervan.
De UI of de hoofdthread is de enige soort thread die standaard al een handler
, een lussenmaker
, en een message queue
. Andere threads moeten met die objecten worden voorbereid voordat ze met het HaMeR-framework kunnen werken. Eerst moeten we een maken lussenmaker
dat bevat al een message queue
en bevestig het aan de draad. U kunt dit doen met een subklasse van Draad
, als volgt.
// Een draad voorbereiden voor de HaMeR-klasse LooperThread breidt Thread uit public Handler mHandler; public void run () // toevoegen en voorbereiden van de Looper Looper.prepare (); // de handler-instantie wordt gekoppeld aan Thread's Looper mHandler = new Handler () public void handleMessage (Message msg) // verwerk inkomende berichten hier; // Starten van de loop van de berichtwachtrij met de Looper Looper.loop ();
Het is echter eenvoudiger om een helperklasse te gebruiken HandlerThread
, welke een heeft lussenmaker
en een message queue
ingebouwd in een Java Draad
en is klaar om een handler te ontvangen.
// De klasse HandlerThread bevat een werkende openbare klasse van Looper HamerThread breidt HandlerThread uit // u hoeft alleen maar de handler-handler voor privéafhandelingsdiensten toe te voegen; public HamerThread (String name) super (name);
De uitvoerbare
is een Java-interface die vele toepassingen heeft. Het kan worden opgevat als een enkele taak die moet worden uitgevoerd op a Draad
. Het heeft een enkele methode die moet worden geïmplementeerd, Runnable.run ()
, om de taak uit te voeren.
// Declaration a Runnable Runnable r = new Runnable () @Override public void run () // de taak gaat hier;
Er zijn meerdere opties om een te plaatsen uitvoerbare
op een handler
.
Handler.post (Runnable r)
: voeg de uitvoerbare
naar de message queue
.Handler.postAtFrontOfQueue (Runnable r)
: voeg de uitvoerbare
aan de voorkant van de message queue
.handler.postAtTime (Ranable r, long timeMillis)
: voeg de uitvoerbare
op de message queue
om op een specifiek tijdstip te worden gebeld.handler.postDelayed (Runnable r, lange vertraging)
: voeg de uitvoerbare
naar de wachtrij die moet worden gebeld nadat een specifieke hoeveelheid tijd is verstreken.// posting een Runnable op een Handler Handler handler = new Handler (); handler.post (nieuw Runnable () @Override public void run () // task goes here);
Het is ook mogelijk om de standaard UI-handler te gebruiken om een a te plaatsen uitvoerbare
roeping Activity.runOnUiThread ()
.
// posting Runnable met behulp van de UI Handler Activity.runOnUiThread (nieuw Runnable () @Override public void run () // task to perform);
Het is belangrijk om enkele dingen in gedachte te houden uitvoerbare
s. In tegenstelling tot een Bericht
, een uitvoerbare
kan niet worden gerecycled - als het werk eenmaal is voltooid, is het dood. Omdat het deel uitmaakt van een standaard Java-pakket, uitvoerbare
is niet afhankelijk van handler
en kan op een standaard worden aangeroepen Draad
de ... gebruiken Runnable.run ()
methode. Deze aanpak heeft echter niets te maken met het HaMeR-raamwerk en zal geen enkele van zijn voordelen delen.
De Bericht
object definieert een bericht met een beschrijving en enkele willekeurige gegevens die kunnen worden verzonden en verwerkt via handler
. De Bericht
is geïdentificeerd met een int
gedefinieerd op Message.what ()
. De Bericht
kan twee andere bevatten int
argumenten en een Voorwerp
om verschillende soorten gegevens op te slaan.
Message.what
: int
het identificeren van de Bericht
Message.arg1
: int
willekeurig argumentMessage.arg2
: int
willekeurig argumentMessage.obj
: Voorwerp
om verschillende soorten gegevens op te slaan Wanneer u een bericht moet verzenden, in plaats van er een vanuit het niets te creëren, is de aanbevolen aanpak om een gerecycled exemplaar direct uit de globale pool op te halen met de Message.obtain ()
of Handler.obtainMessage ()
commando's. Er zijn enkele verschillende versies van die methoden waarmee u een Bericht
volgens uw behoefte.
Een veelgebruikt gebruik van Handler.obtainMessage ()
is wanneer u een bericht naar een achtergrondthread wilt verzenden. Je gebruikt de handler
geassocieerd met die threads lussenmaker
verkrijgen van een Bericht
en stuur het naar de achtergrondthread, zoals in het onderstaande voorbeeld.
int wat = 0; String hallo = "Hallo!"; // Verkrijgen van een bericht behorende bij de achtergrond Thread Message msg = handlerBGThread.obtainMessage (what, hallo); // Bericht verzenden naar achtergrond Thread handlerBGThread.sendMessage (msg);
Er zijn veel coole methoden op de Bericht
klasse, en ik raad u aan de documentatie van naderbij te bekijken.
bericht versturen()
optiesEvenzo als hoe we kunnen posten uitvoerbare
s, er zijn meerdere opties om te verzenden Bericht
s:
Handler.sendMessage (berichtbericht)
: Voeg een ... toe Bericht
naar de message queue
.Handler.sendMessageAtFrontOfQueue (berichtbericht)
: Voeg een ... toe Bericht
naar de voorkant van de message queue
.Handler.sendMessageAtTime (bericht msg, long timeInMillis)
: Voeg een ... toe Bericht
naar de wachtrij op een specifiek tijdstip.Handler.sendMessageDelayed (Message msg, long timeInMillis)
: Voeg een ... toe Bericht
naar de wachtrij nadat een specifieke hoeveelheid tijd is verstreken.De Bericht
objecten verzonden door lussenmaker
worden verwerkt door de handler met de methode Handler.handleMessage
. Het enige dat u hoeft te doen is het uitbreiden van het handler
class en overschrijf deze methode om de berichten te verwerken.
public class MessageHandler breidt Handler uit @Overleden openbare ongeldige handleMessage (Message msg) switch (msg.what) // handle 'Hello' msg case 0: String hello = (String) msg.obj; System.out.println (hallo); breken;
Het HaMeR-framework kan helpen om de gelijktijdige code van uw Android-applicatie te verbeteren. Het lijkt in eerste instantie misschien verwarrend in vergelijking met de eenvoud van een AsyncTask
, maar de openheid van HaMeR kan een voordeel zijn, mits correct gebruikt.
Onthouden:
Handler.post ()
methoden worden gebruikt wanneer afzenders weten welke bewerkingen moeten worden uitgevoerd.Handler.sendMessage()
methoden worden gebruikt wanneer de ontvanger weet welke bewerking moet worden uitgevoerd.Voor meer informatie over threading in Android, bent u mogelijk geïnteresseerd in het boek Efficient Android Threading: Asynchronous Processing Techniques voor Android Applications van Anders Goransson.
In de volgende zelfstudie gaan we door met het verkennen van het HaMeR-framework met een praktische benadering, door een applicatie te bouwen die verschillende manieren toont om dit gelijktijdigheidskader voor Android te gebruiken. We maken deze app vanaf de grond af, waarbij we verschillende mogelijkheden proberen, zoals communicatie tussen threads
, praten met de UI-thread, evenals het verzenden van berichten en posten uitvoerbare
s met vertragingen.
Tot ziens!