Forstå samtidighet på Android ved hjelp av HaMeR

1. Introduksjon

Alle som prøver Android-utviklingen, oppdager betydningen av samtidighet. Den eneste måten å opprette en responsiv app på, er å la UI-tråden være så ledig som mulig, slik at alt det harde arbeidet blir gjort asynkront av bakgrunns tråder.

På grunn av utformingen av Android, administrerer tråder med bare java.lang.Thread og java.util.concurrent pakker kan være veldig vanskelig. Ved å bruke low-level threading-pakker med Android betyr det at du trenger å bekymre deg for mye vanskelig synkronisering for å unngå raseforhold. Heldigvis gjorde folkene på Google det harde arbeidet og bygget noen gode verktøy for å gjøre jobben enklere: AsyncTaskIntentServiceloaderAsyncQueryHandler og CursorLoader er alle nyttige, så vel som HaMeR-klassene handler, Budskap, og kjørbart. Det er mange flotte alternativer for deg å velge mellom, hver med sine fordeler og ulemper.

Det har blitt mye sagt om AsyncTask objekt, og mange bruker det som en sølvkule løsning for samtidighet på Android. Det er ekstremt nyttig for korte operasjoner, enkel å implementere, og sannsynligvis den mest populære tilnærmingen til samtidighet på Android. Hvis du vil lære mer om AsyncTask, sjekk ut de følgende Envato Tuts + innleggene.

  • Android fra grunnen: Bakgrunnsoperasjoner

    Threading i et hvilket som helst programmeringsspråk eller -plattform er vanskelig, og Android er ikke noe unntak. I denne opplæringen lærer du om noen av verktøyene ...
    Paul Trebilcox-Ruiz
    Android SDK
  • Forstå AsyncTask-verdier på 60 sekunder

    På Android er AsyncTask-klassen vanligvis brukt til å utføre operasjoner på en bakgrunnstråd. I denne videoen viser jeg deg hvordan en AsyncTask fungerer og hvordan du ...
    Paul Trebilcox-Ruiz
    Android

derimot, AsyncTask bør ikke være det eneste verktøyet på verktøybeltet ditt.

For langvarige operasjoner, for komplekse samtidighet problemer, eller for å oppnå mer effektivitet i noen situasjoner, bør du velge en annen løsning. Hvis du trenger mer fleksibilitet eller effektivitet enn AsyncTask gir, du kan bruke HaMeR (handler, Budskap & kjørbart) rammeverk.I denne veiledningen vil vi utforske HaMeR-rammeverket, en av de kraftigste samtidige modellene som er tilgjengelige på Android, og vi lærer når og hvordan du bruker den. I en oppfølgingstutorial vil jeg vise deg hvordan du kodes et program for å prøve ut noen muligheter for HaMeR.

Følgende avsnitt vil introdusere betydningen av bakgrunnstråder for Android-systemet. Hvis du er kjent med dette konseptet, er du velkommen til å hoppe over det og gå direkte til diskusjonen om HaMeR-rammen i seksjon 3.

2. Responsiveness Through Background Threads

Når en Android-applikasjon er startet, er den første tråden som fremkommer av prosessen, hovedtråden, også kjent som UI-tråden, som er ansvarlig for å håndtere alt brukergrensesnittlogikken. Dette er den viktigste tråden i et program. Det er ansvarlig for å håndtere all brukerinteraksjon og også "binde" programmets bevegelige deler sammen. Android tar dette veldig alvorlig, og hvis brukergrensesnittet ditt sitter fast, arbeider på en oppgave i mer enn noen sekunder, vil appen krasje.

[UI-tråden] er svært viktig fordi den har ansvaret for å sende hendelser til de riktige brukergrensesnittene, inkludert tegnearrangementer. Det er også tråden der applikasjonen din interagerer med komponenter fra Android UI toolkit (komponenter fra android.widget og android.view pakker). Som sådan kalles også hovedtråden noen ganger UI-tråden. - Prosesser og tråder, Android Developer Guide

Problemet er at nesten all kode i en Android-applikasjon vil bli utført på UI-tråden som standard. Siden oppgavene på en tråd gjøres i rekkefølge, betyr dette at brukergrensesnittet kan "fryse", blir uforsvarlig mens det behandler noe annet arbeid.

Langtidsoppgaver som kreves på brukergrensesnittet, vil trolig være dødelig for appen din, og en ANR (Application Not Responding) dialog vises. Selv små oppgaver kan kompromittere brukeropplevelsen, og dermed er den riktige tilnærmingen å fjerne så mye arbeid som mulig fra brukergrensesnittet ved hjelp av bakgrunnstråder. Som sagt tidligere, er det mange måter å løse dette problemet på, og vi vil utforske HaMeR-rammeverket, en av kjerne-løsningene fra Android for å løse denne situasjonen.

3. HaMeR Framework

HaMeR-rammen tillater bakgrunns tråder å sende meldinger eller post runnables til brukergrensesnittet og til alle andre tråder MessageQueue via handlers. HaMeR refererer til handler, Budskap, & kjørbart. Det er også noen andre viktige klasser som samarbeider med HaMeR: Looper og MessageQueue. Sammen er disse objektene ansvarlige for å lette trådstyring på Android, ta vare på synkronisering og gi enkle metoder for bakgrunnstråder for å kommunisere med brukergrensesnittet og med andre tråder.

Slik går det i klassene i HaMeR-rammen.

  • Looper kjører en meldingssløyfe på en tråd ved hjelp av MessageQueue.
  • MessageQueue inneholder en liste over meldinger som skal sendes av Looper.
  • handler tillater sending og behandling av Budskap og kjørbart til MessageQueue. Det kan brukes til å sende og behandle meldinger mellom tråder.
  • Budskap inneholder en beskrivelse og data som kan sendes til en handler.
  • kjørbart representerer en oppgave som skal utføres.

Med HaMeR-rammen kan tråder sende meldinger eller legge ut runnable objekter enten til seg selv eller til brukergrensesnittet. HaMeR fremmer også bakgrunnsinteraksjoner via handler.

3.1. Handler-klassen

handler er HaMeR arbeidshorse. Det er ansvarlig for å sende Budskap (data melding) og innlegg kjørbart (oppgavemeddelelse) objekter til MessageQueue assosiert med a Tråd. Etter at oppgavene er levert til køen, mottar håndterer objektene fra Looper og behandler meldingene ved riktig tidspunkt ved hjelp av handler forbundet med det.

EN handler kan brukes til å sende eller poste Budskap og kjørbart objekter mellom tråder, så lenge slike tråder deler samme prosess. Ellers vil det være nødvendig å opprette en interprosesskommunikasjon (IPC), en metode som overgår omfanget av denne opplæringen.

Instantiere en Handler

EN handler må alltid være knyttet til a Looper, og denne forbindelsen må gjøres i løpet av sin oppstart. Hvis du ikke gir en Looper til handler, det vil være bundet til dagens Tråd's Looper.

// Handler bruker nåværende trådens Looper Handler handler = ny Handler (); // Handler bruker Looper gir Handler handler = ny Handler (Looper);

Husk at a handler er alltid knyttet til a Looper, og denne forbindelsen er permanent og kan ikke endres når den er etablert. Imidlertid a Loopertråden kan ha foreninger med flere handlers. Det er også viktig å merke seg at a Looper må være aktiv før foreningen med a handler.

3.2. Looper og MessageQueue

Samarbeidet mellom Looper og MessageQueue I en Java-tråd opprettes en løkke med oppgaver som behandles i rekkefølge. En slik løkke vil holde tråden i live mens den venter på å motta flere oppgaver. En tråd kan bare ha en Looper og en MessageQueue forbundet med det; Det kan imidlertid være flere håndtere for hver tråd. Handlers er ansvarlige for å behandle oppgavene i køen, og hver oppgave vet hvilken handler som er ansvarlig for behandlingen.

3.3. Forbereder en tråd for HaMeR

Brukergrensesnittet eller hovedtråden er den eneste typen tråd som som standard allerede har a handler, en Looper, og a MessageQueue. Andre tråder må være forberedt med disse objektene før de kan arbeide med HaMeR-rammen. Først må vi opprette en Looper som allerede inneholder a MessageQueue og fest den til tråden. Du kan gjøre dette med en underklasse av Tråd, som følger.

// Forbereder en tråd for HaMeR-klassen LooperThread utvider tråden public Handler mHandler; offentlig tomgangsløp () // legge til og klargjøre Looper Looper.prepare (); // Handler-forekomsten vil bli knyttet til Thread's Looper mHandler = ny Handler () Offentlig tomgangshåndteringMessage (Message msg) // behandle innkommende meldinger her; // Starte meldingen kø loop med Looper Looper.loop (); 

Det er imidlertid enklere å bruke en hjelpeklasse som heter HandlerThread, som har a Looper og a MessageQueue innebygd i en Java Tråd og er klar til å motta en Handler.

// HandlerThread-klassen inneholder en fungerende Looper-offentlig klasse HamerThread utvider HandlerThread // du trenger bare å legge til Handler Private Handler Handler; offentlig HamerThread (String navn) super (navn); 

4. Posting Runnables

De kjørbart er et Java-grensesnitt som har mange bruksområder. Det kan forstås som en enkelt oppgave som skal utføres på en Tråd. Den har en enkelt metode som må implementeres, Runnable.run (), å utføre oppgaven.

// Deklarere en Runnable Runnable r = Ny Runnable () @Override offentlig tomgang () // oppgaven går her;

Det er flere alternativer å legge inn en kjørbart på en handler.

  • Handler.post (Runnable r): Legg til kjørbart til MessageQueue.
  • Handler.postAtFrontOfQueue (Runnable r): Legg til kjørbart på forsiden av MessageQueue.
  • handler.postAtTime (Runnable r, long timeMillis): Legg til kjørbartMessageQueue å bli kalt på et bestemt tidspunkt.
  • handler.postDelayed (Runnable r, lang forsinkelse): Legg til kjørbart til køen som skal ringes etter at en bestemt tid har gått.
// Posting a Runnable på en Handler Handler Handler = Ny Handler (); handler.post (new Runnable () @Override public void run () // oppgaven går her);

Det er også mulig å bruke standardbrukerens brukerhåndterer til å legge inn en kjørbart ringer Activity.runOnUiThread ().

// posting Runnable ved hjelp av UI Handler Activity.runOnUiThread (ny Runnable () @Override public void run () // oppgave å utføre);

Det er viktig å huske på noen ting om kjørbarts. I motsetning til en Budskap, en kjørbart kan ikke resirkuleres - når jobben er ferdig, er den død. Siden det er en del av en standard Java-pakke, a kjørbart er ikke avhengig av handler og kan kalles på en standard Tråd bruker Runnable.run () metode. Denne tilnærmingen har imidlertid ikke noe å gjøre med HaMeR-rammeverket, og vil ikke dele noen av fordelene.

5. Sende meldinger

De Budskap objekt definerer en melding som inneholder en beskrivelse og noen vilkårlig data som kan sendes og behandles via handler. De Budskap er identifisert med en int definert på Message.what (). De Budskap kan holde to andre int argumenter og an Gjenstand å lagre ulike typer data.

  • Message.what: int identifisere Budskap
  • Message.arg1: int vilkårlig argumentasjon
  • Message.arg2: int vilkårlig argumentasjon
  • Message.obj: Gjenstand å lagre ulike typer data  

Når du trenger å sende en melding, i stedet for å lage en fra begynnelsen, er den anbefalte tilnærmingen å hente en resirkulert direkte fra det globale bassenget med Message.obtain () eller Handler.obtainMessage () kommandoer. Det finnes noen forskjellige versjoner av de metodene som lar deg få en Budskap i henhold til ditt behov.

En vanlig bruk av Handler.obtainMessage () er når du trenger å sende en melding til en bakgrunnstråd. Du vil bruke handler forbundet med den tråden er Looper å få en Budskap og send den til bakgrunns tråden, som i eksemplet nedenfor.

int hva = 0; String hallo = "Hei!"; // Hent melding knyttet til bakgrunnen Trådmelding msg = handlerBGThread.obtainMessage (hva, hallo); // Sende meldingen til bakgrunnen Thread handlerBGThread.sendMessage (msg); 

Det er mange kule metoder på Budskap klasse, og jeg anbefaler deg å ta en nærmere titt på dokumentasjonen.

5.1. sende melding() alternativer

På samme måte som hvordan vi kan legge inn kjørbarts, det er flere alternativer å sende Budskaps:

  • Handler.sendMessage (Meldingsmelding): Legg til en Budskap til MessageQueue.
  • Handler.sendMessageAtFrontOfQueue (Meldingsmelding): Legg til en Budskap til forsiden av MessageQueue.
  • Handler.sendMessageAtTime (Message msg, long timeInMillis): Legg til en Budskap til køen på et bestemt tidspunkt.
  • Handler.sendMessageDelayed (Message msg, long timeInMillis): Legg til en Budskap til køen etter at en bestemt tid har gått.

5.2. Håndtering av meldinger med handler

De Budskap objekter sendt av Looper behandles av handleren med metoden Handler.handleMessage. Alt du trenger å gjøre er å forlenge handler klassen og overstyr denne metoden for å behandle meldingene.

offentlig klasse MessageHandler utvider Handler @Override public void handleMessage (Melding msg) switch (msg.what) // håndtere 'Hello' msg tilfelle 0: String hallo = (String) msg.obj; System.out.println (hei); gå i stykker; 

6. Konklusjon

HaMeR-rammeverket kan bidra til å forbedre Android-programmets samtidige kode. Det kan virke forvirrende først når det sammenlignes med enkelheten til en AsyncTask, men haMeRs åpenhet kan være en fordel hvis den brukes riktig. 

Huske:

  • Handler.post () metoder brukes når avsendere vet hvilke operasjoner som skal utføres.
  • Handler.sendMessage() metoder brukes når mottakeren vet hvilken operasjon som skal utføres.

Hvis du vil vite mer om tråder i Android, kan du være interessert i boken Efficient Android Threading: Asynkron Behandlingsteknikker for Android Applications av Anders Goransson.

6.1. Hva blir det neste?

I den neste opplæringen fortsetter vi å utforske HaMeR-rammeverket med en praktisk tilnærming, ved å bygge et program som demonstrerer forskjellige måter å bruke denne Android-konseptrammen på. Vi lager denne appen fra bakken, prøver forskjellige muligheter som kommunikasjon mellom tråder, snakker med brukergrensesnittet, samt sende meldinger og innlegg kjørbarts med forsinkelser. 

Ser deg snart!