Förstå samtidighet på Android med hjälp av HaMeR

1. Introduktion

Alla som försöker Android-utveckling upptäcker vikten av samtidighet. Det enda sättet att skapa en responsiv app är att lämna UI-tråden så ledigt som möjligt, så att allt det hårda arbetet kan ske asynkront med bakgrundsdragen.

På grund av designen av Android, hanterar tråden med endast java.lang.Thread och java.util.concurrent paket kan vara mycket svårt. Med hjälp av de låga nivån med Threading-paket med Android, betyder det att du måste oroa sig för en mycket svår synkronisering för att undvika löpförhållanden. Lyckligtvis gjorde folket på Google det hårda arbetet och byggde några bra verktyg för att göra våra jobb enklare: AsyncTaskIntentServiceLastareAsyncQueryHandler och CursorLoader är alla användbara, liksom HaMeR-klasserna Hanterare, Meddelande, och Runnable. Det finns många bra alternativ för dig att välja mellan, alla med sina fördelar och nackdelar.

Många har sagt om AsyncTask objekt, och många använder det som en silverkula lösning för samtidighet på Android. Det är mycket användbart för korta operationer, lätt att implementera, och förmodligen det mest populära sättet att samla på Android. Om du vill lära dig mer om AsyncTask, kolla in följande Envato Tuts + inlägg.

  • Android från början: Bakgrundsoperationer

    Threading i något programmeringsspråk eller -plattform är svårt, och Android är inget undantag. I den här handledningen lär du dig om några av verktygen ...
    Paul Trebilcox-Ruiz
    Android SDK
  • Förstå AsyncTask-värden på 60 sekunder

    På Android används AsyncTask-klassen vanligtvis för att utföra operationer på en bakgrundsgänga. I den här videon visar jag dig hur en AsyncTask fungerar och hur du ...
    Paul Trebilcox-Ruiz
    Android

dock, AsyncTask borde inte vara det enda verktyget på ditt verktygsbälte.

För långvariga operationer, för komplexa samtidighetsproblem, eller för att uppnå mer effektivitet i vissa situationer, bör du välja en annan lösning. Om du behöver mer flexibilitet eller effektivitet än AsyncTask ger dig möjlighet att använda HaMeR (Hanterare, Meddelande & Runnable) ramverk.I den här handledningen utforskar vi HaMeR-ramen, en av de mest kraftfulla samtidiga modellerna som finns på Android, och vi lär dig när och hur du använder den. I en uppföljningstutorial visar jag dig hur du kodar en applikation för att prova några möjligheter till HaMeR.

Följande avsnitt kommer att introducera vikten av bakgrundstrådar för Android-systemet. Om du är bekant med det här konceptet kan du hoppa över det och gå direkt till diskussionen om HaMeR-ramverket i avsnitt 3.

2. Responsiveness Through Background Threads

När en Android-applikation startas är den första tråden som genereras av processen processens huvudgänga, även känd som UI-tråden, som ansvarar för hanteringen av alla användargränssnittslogiken. Detta är den viktigaste tråden i en applikation. Det är ansvaret för hanteringen av all användarinteraktion och också att "binda" programmets rörliga delar tillsammans. Android tar det väldigt seriöst, och om din användargränssnitt fastnar på en uppgift i mer än några sekunder kommer appen att krascha.

[UI-tråden] är mycket viktig eftersom den ansvarar för att skicka händelser till lämpliga gränssnitt för användargränssnitt, inklusive teckningsevenemang. Det är också tråden där din applikation interagerar med komponenter från Android UI-verktygssatsen (komponenter från android.widget och android.view paket). Som sådan kallas huvudtråden ibland även UI-tråden. - Processer och trådar, Android Developer Guide

Problemet är att nästan all kod i en Android-applikation kommer att köras på UI-tråden som standard. Eftersom uppgifterna på en tråd görs i följd betyder det att ditt användargränssnitt kan "frysa", blir svarlöst medan det behandlar något annat arbete.

Långsamma uppgifter som kallas för användargränssnittet kommer troligen att vara dödliga för din app, och en dialogruta ANR (Application Not Responding) visas. Även små uppgifter kan äventyra användarupplevelsen. Därför är rätt sätt att ta bort så mycket arbete som möjligt från användargränssnittet med hjälp av bakgrundsgängor. Som sagt tidigare finns det många sätt att lösa problemet och vi kommer att undersöka HaMeR-ramen, en av de grundläggande lösningarna som tillhandahålls av Android för att ta itu med denna situation.

3. HaMeR Framework

HaMeR-ramen tillåter bakgrundsgängor att skicka meddelanden eller posta runnables till användargränssnittet och till någon annan tråds Meddelandekö via hanterare. HaMeR refererar till Hanterare, Meddelande, & Runnable. Det finns också några andra viktiga klasser som arbetar tillsammans med HaMeR: Looper och Meddelandekö. Tillsammans är dessa objekt ansvariga för att underlätta trådhantering på Android, ta hand om synkronisering och tillhandahålla enkla metoder för bakgrundsgängor att kommunicera med användargränssnittet och med andra trådar.

Så här går klasserna i HaMeR-ramarna ihop.

  • Looper kör en meddelandeslinga på en tråd med hjälp av Meddelandekö.
  • Meddelandekö innehåller en lista över meddelanden som ska skickas av Looper.
  • Hanterare tillåter sändning och bearbetning av Meddelande och Runnable till Meddelandekö. Det kan användas för att skicka och bearbeta meddelanden mellan trådar.
  • Meddelande innehåller en beskrivning och data som kan skickas till en hanterare.
  • Runnable representerar en uppgift som ska utföras.

Med HaMeR-ramverket kan trådar skicka meddelanden eller posta runnbara objekt antingen till sig själva eller till UI-tråden. HaMeR främjar också bakgrundsinteraktioner via Hanterare.

3,1. Handler-klassen

Hanterare är HaMeR-arbetshästen. Det är ansvarigt för att skicka Meddelande (data meddelande) och post Runnable (uppgiftsmeddelande) objekt till Meddelandekö associerad med a Tråd. Efter att ha levererat uppgifterna till köen mottar hanteraren objekten från Looper och behandlar meddelandena vid rätt tidpunkt med hjälp av Hanterare associerad med det.

en Hanterare kan användas för att skicka eller posta Meddelande och Runnable objekt mellan trådar, så länge sådana trådar delar samma process. Annars är det nödvändigt att skapa en Inter Process Communication (IPC), en metod som överträffar omfattningen av denna handledning.

Instantiating a Handler

en Hanterare måste alltid vara associerad med a Looper, och denna anslutning måste göras under sin instansiering. Om du inte ger en Looper till Hanterare, det kommer att vara bunden till dagens Tråd's Looper.

// Handler använder nuvarande trådens Looper Handler handler = ny Handler (); // Handler använder Looper ger Handler handler = ny Handler (Looper);

Tänk på att a Hanterare är alltid associerad med a Looper, och den här anslutningen är permanent och kan inte ändras när den är etablerad. Men a Loopers tråd kan ha föreningar med flera Hanterares. Det är också viktigt att notera att a Looper måste vara aktiv före dess förening med a Hanterare.

3,2. Looper och MessageQueue

Samarbetet mellan Looper och Meddelandekö I en Java-tråd skapas en slinga av uppgifter som behandlas i följd. En sådan slinga håller tråden vid liv medan den väntar på att ta emot fler uppgifter. En tråd kan bara ha en Looper och en Meddelandekö associerad med det; Det kan dock finnas flera hanterare för varje tråd. Hanteraren ansvarar för att bearbeta uppgifterna i kön, och varje uppgift vet vilken hanterare som är ansvarig för behandlingen.

3,3. Förbereder en tråd för HaMeR

UI eller huvudtråden är den enda typen tråd som som standard redan har en Hanterare, en Looper, och a Meddelandekö. Andra trådar måste förberedas med dessa objekt innan de kan arbeta med HaMeR-ramen. Först måste vi skapa en Looper som redan innehåller a Meddelandekö och fäst den på tråden. Du kan göra detta med en underklass av Tråd, som följer.

// Förbereda en tråd för HaMeR-klassen LooperThread utökar tråd public Handler mHandler; public void run () // lägga till och förbereda Looper Looper.prepare (); // Handler-förekomsten kommer att associeras med Thread Looper mHandler = new Handler () public void handleMessage (Meddelande msg) // bearbeta inkommande meddelanden här; // Starta meddelandekönslingan med Looper Looper.loop (); 

Det är dock enklare att använda en hjälparklass som heter HandlerThread, som har a Looper och a Meddelandekö inbyggd i en Java Tråd och är redo att ta emot en hanterare.

// HandlerThread-klassen innehåller en fungerande Looper public class. HamerThread utökar HandlerThread // du behöver bara lägga till Handler Private Handler Handler; offentlig HamerThread (strängnamn) super (namn); 

4. Bokföring av Runnables

De Runnable är ett Java-gränssnitt som har många användningsområden. Det kan förstås som en enda uppgift som ska utföras på en Tråd. Den har en enda metod som måste genomföras, Runnable.run (), att utföra uppgiften.

// Deklarera en Runnable Runnable r = Ny Runnable () @Override public void run () // uppgiften går här;

Det finns flera alternativ att skicka en Runnable på en Hanterare.

  • Handler.post (Runnable r): Lägg till Runnable till Meddelandekö.
  • Handler.postAtFrontOfQueue (Runnable r): Lägg till Runnable på framsidan av Meddelandekö.
  • Hanterare.postAtTime (Runnable r, long timeMillis): Lägg till RunnableMeddelandekö att ringas vid en viss tidpunkt.
  • Hanterare.postDelayed (Runnable r, lång fördröjning): Lägg till Runnable till köen som ska ringas efter en viss tid har gått.
// posta en Runnable på en Handler Handler handler = ny Handler (); handler.post (new Runnable () @Override public void run () // uppgiften går här);

Det är också möjligt att använda standardgränssnittshanteraren för att lägga in en Runnable kallelse Activity.runOnUiThread ().

// posta Runnable med användarhandboken Activity.runOnUiThread (ny Runnable () @Override public void run () // uppgift att utföra);

Det är viktigt att komma ihåg några saker om Runnables. Till skillnad från en Meddelande, en Runnable kan inte återvinnas - när jobbet är klart är det dött. Eftersom det ingår i ett standard Java-paket, a Runnable beror inte på Hanterare och kan kallas på en standard Tråd använda Runnable.run () metod. Detta tillvägagångssätt har emellertid ingenting att göra med HaMeR-ramverket och kommer inte att dela några av dess fördelar.

5. Skicka meddelanden

De Meddelande objekt definierar ett meddelande som innehåller en beskrivning och några godtyckliga data som kan skickas och behandlas via Hanterare. De Meddelande identifieras med en int definierad på Message.what (). De Meddelande kan hålla två andra int argument och an Objekt att lagra olika typer av data.

  • Message.what: int identifiera Meddelande
  • Message.arg1: int godtyckligt argument
  • Message.arg2: int godtyckligt argument
  • Message.obj: Objekt att lagra olika typer av data  

När du behöver skicka ett meddelande, istället för att skapa en från början, är det rekommenderade sättet att hämta en återvunnen enhet direkt från den globala poolen med Message.obtain () eller Handler.obtainMessage () kommandon. Det finns några olika versioner av de metoder som låter dig få en Meddelande enligt ditt behov.

En gemensam användning av Handler.obtainMessage () är när du behöver skicka ett meddelande till en bakgrundstråd. Du kommer att använda Hanterare associerad med den här tråden Looper för att erhålla en Meddelande och skicka den till bakgrundsgängan, som i exemplet nedan.

int vad = 0; String hej = "Hej!"; // Hämta meddelande i samband med bakgrunden Trådmeddelande msg = handlerBGThread.obtainMessage (vad hej); // Skickar meddelandet till bakgrunden TrådhanterareBGThread.sendMessage (msg); 

Det finns massor av coola metoder på Meddelande klass, och jag råder dig att titta närmare på dokumentationen.

5,1. skicka meddelande() alternativ

På samma sätt som hur vi kan posta Runnables, det finns flera alternativ att skicka Meddelandes:

  • Handler.sendMessage (Meddelande msg): Lägg till en Meddelande till Meddelandekö.
  • Handler.sendMessageAtFrontOfQueue (Meddelande meddelande): Lägg till en Meddelande till framsidan av Meddelandekö.
  • Handler.sendMessageAtTime (Message msg, long timeInMillis): Lägg till en Meddelande till köen vid en viss tidpunkt.
  • Handler.sendMessageDelayed (Message msg, long timeInMillis): Lägg till en Meddelande till köen efter en viss tid har gått.

5,2. Hantering av meddelanden med hanteraren

De Meddelande föremål som skickas av Looper bearbetas av hanteraren med metoden Handler.handleMessage. Allt du behöver göra är att förlänga Hanterare klass och åsidosätta denna metod för att behandla meddelandena.

public class MessageHandler utökar Handler @Override public void handleMessage (Meddelande msg) switch (msg.what) // hantera "Hello" msg fall 0: String hej = (String) msg.obj; System.out.println (hello); ha sönder; 

6. Sammanfattning

HaMeR-ramen kan hjälpa till att förbättra din Android-applikations samtidiga kod. Det kan tyckas förvirrande först i jämförelse med enkelheten hos en AsyncTask, men haMeRs öppenhet kan vara en fördel om den används korrekt. 

Kom ihåg:

  • Handler.post () metoder används när avsändare vet vilka åtgärder som ska utföras.
  • Handler.sendMessage() metoder används när mottagaren vet vilken operation som ska utföras.

Om du vill veta mer om tråden i Android kan du vara intresserad av boken Effektiv Android-tråder: Asynkrona Behandlingstekniker för Android-applikationer av Anders Goransson.

6,1. Vad kommer härnäst?

I nästa handledning fortsätter vi att utforska HaMeR-ramverket med ett praktiskt tillvägagångssätt, genom att bygga en applikation som visar olika sätt att använda denna parallellram för Android. Vi skapar den här appen från grunden och försöker olika möjligheter som kommunikation mellan Trådar, pratar med användargränssnittet, samt skickar meddelanden och inlägg Runnables med förseningar. 

Ses snart!