Inzicht in concurrency op Android met behulp van HaMeR

1. Inleiding

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: AsyncTaskIntentServiceladerAsyncQueryHandler 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.

  • Android from Scratch: achtergrondbewerkingen

    Threading in elke programmeertaal of platform is moeilijk, en Android is geen uitzondering. In deze tutorial leert u enkele van de tools kennen ...
    Paul Trebilcox-Ruiz
    Android SDK
  • AsyncTask-waarden in 60 seconden begrijpen

    In Android wordt de klasse AsyncTask vaak gebruikt om bewerkingen uit te voeren op een achtergrondthread. In deze video laat ik je zien hoe een AsyncTask werkt en hoe jij ...
    Paul Trebilcox-Ruiz
    Android

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.

2. Responsiviteit door achtergronddraden

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 de android.widget en android.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.

3. Het HaMeR-raamwerk

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.

3.1. De handler klasse

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.

Instantiëren van een handler

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 lussenmakerde thread kan associaties hebben met meerdere handlers. Het is ook belangrijk op te merken dat een lussenmaker moet actief zijn voordat het wordt geassocieerd met een handler.

3.2. Looper en MessageQueue

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.

3.3. Een draad voorbereiden voor de HaMeR

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); 

4. Runnables plaatsen

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 uitvoerbares. 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.

5. Berichten verzenden

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 argument
  • Message.arg2: int willekeurig argument
  • Message.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.

5.1. bericht versturen() opties

Evenzo als hoe we kunnen posten uitvoerbares, er zijn meerdere opties om te verzenden Berichts:

  • 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.

5.2. Berichten afhandelen met handler

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; 

6. Conclusie

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.

6.1. Wat is het volgende?

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 uitvoerbares met vertragingen. 

Tot ziens!