I denna handledning kommer vi att förklara hur Android fungerar när du kör en tjänst, kommer vi att beskriva vad exekveringstrådarna består av och vad processerna handlar om. Detta gör att vi kan förstå hur våra applikationer körs, vilket ger oss större kontroll och stabilitet på de mobila enheter där de kommer att installeras.
Tråd
När användaren kör ett program, Android skapar en tråd som heter main (main). Den här tråden är mycket viktig eftersom den är ansvarig för att hantera händelser som användaren utlöser till lämpliga komponenter och innehåller även händelser som ritar skärmen. En körningstråd, den minsta delen som kan bearbetas av en schemaläggare i ett operativsystem, i det här fallet Android (med Linux -kärna).
De implementering av flera trådar som bearbetas samtidigt i samma applikation, (kallar det samtidighet, vilket hänvisar till körningens samtidighet), det är känt som multithreading. Multithreading tillämpas så att dessa trådar delar resurser Och det är vad en process består av, kom ihåg att detta kan tillämpas programmatiskt inom koden för samma applikation, implementeringen av multithreading på operativsystemnivå beror inte på oss.
Början av livscykeln för en applikation inkluderar generering av en ny Linux -process som tilldelas en huvudtråd eller UI -tråd (tråden som är ansvarig för den grafiska behandlingen av applikationen, användargränssnittstråd på engelska).
NoteraLivscykeln inkluderar utförandet av metoderna: onCreate (), onStart () och onResume (); vid starten och när den stängs: onPause (), onStop () och onDestroy ().
En process kan tvingas stängas av Android på grund av brist på minne, denna typ av fall är sällsynt på grund av tekniska framsteg men det händer fortfarande.
Frågan är: Vilka processer beslutar Android att stänga?
Dessa stängs genom att jämföra deras betydelse, det sammanfattas enligt följande:
Det viktigaste: förgrundsprocesserAnvändaren interagerar med processen (onResume () -metoden för processen körs för närvarande). Det finns en tjänst som kör sina livscykelmetoder. Eller finns det en BroadcastReceiver kör hans onReceive () metod.
Den näst viktigaste: Synliga processerAktivitet med samtal till onPause () -metod. Tjänst kopplad till en synlig aktivitet (bunden tjänst).
Den tredje viktigaste: Process med en tjänstAnvändaren interagerar inte direkt med processen. Processen har en tjänst som körs i bakgrunden.
Den näst minst viktiga: BakgrundsprocessenDet finns ingen typ av interaktion med användaren. Den process som användaren senast tittade på blir den sista som förstörs.
Det minst viktiga: Tom processDet har inga aktiva komponenter. Processen lever fortfarande för cachelagring, vilket hindrar användaren från att återgå till användning av den processen.
Den senare, den tomma processen, är den första som avslutas vid brist på minne. Således kommer en applikation som implementerar en tjänst där en tråd skapas för att ladda ner innehåll från internet, vara viktigare än en applikation som skapar tråden utan att implementera en tjänst, så att det är mer troligt att den avslutas innan nedladdningen slutförs. , eftersom de är långvariga processer.
För att förstå multhreading låt oss se hur Android hanterar sin huvudtråd.
PROCESS A har ett användargränssnitt eller Huvudtråd, den här tråden hanterar a meddelandekö eller meddelandekö, som körs när tråden blir inaktiv, vem hanterar detta? De Looper.
Looper är en användargränssnittsklass av Android Java det, tillsammans med Hanterarklass, bearbetar användargränssnittshändelser som knapptryckningar, ritade om skärmar och orienteringsomkopplare. Händelser kan också användas för att ladda innehåll till en HTTP -tjänst, ändra storlek på bilder och köra fjärrförfrågningar. Nyckelfunktionen för dessa klasser är att de kan implementera ett samtidighetsmönster.
De Android Looper -klass innehåller en MessageQueue (meddelandekö) och är endast kopplat till ämnet från vilket det skapades. Observera att denna anslutning inte kan brytas och att lLooper den kan inte bifogas någon annan tråd. Looper finns också på lokal lagring och kan bara ringas från en statisk metod. En iscensättningsmetod kontrollerar om en Looper redan är associerad med en tråd, och sedan skapar den statiska metoden Looper. Efteråt kan en loop användas för att kontrollera meddelandena i kön.
Hittills förstår vi flera begrepp: process, tråd, UI -tråd, looper, men vi vet fortfarande inte varför multithreading.
Långsiktig verksamhet
Det anses vara lång varaktighet för varje metod vars körning överstiger 5 sekunder, vilket utlöser det typiska meddelandet "programmet svarar inte. Vill du stänga den?
Vad kan dessa operationer vara?: Internetåtkomst, SQL -frågor, XML / HTML / JSON -analys, komplex grafikbehandling. Vilken som helst av dessa operationer som körs i huvudtråden blockerar den, och eftersom det är den som hanterar det grafiska användargränssnittet tolkas det som en frysning, som android bestämmer sig för att stänga.
Låt oss bara föreställa oss att någon av dessa operationer varar 7 sekunder och användaren bestämmer sig för att skriva något i någon textinmatning, så även om dessa 7 sekunder inte har gått kan UI -tråden inte uppdatera vyn så att användaren uppskattar att han skriver, och så det genererar en frysning, meddelandet "inget svar" utlöses som du har två alternativ med, vänta eller förstöra, även om du aldrig kan veta hur länge du ska vänta, kan det vara några sekunder eller till och med minuter beroende på meddelandekön som har huvudtråden.
Hur undviker vi att frysa?
Om du använder trådar eller tjänster, beroende på om uppgif.webpten kräver ändring av vyn, implementeras i detta fall en tjänst eftersom vyn för en applikation inte kan ändras utanför UI -tråden. Det bästa sättet att undvika att frysa är att använda asynkrona uppgif.webpter med AsyncTask -klassen, i denna handledning kommer vi att implementera flera trådar för att förstå hur Android -arkitekturen fungerar.
Kod och utveckling
Projektet som vi kommer att skapa härnäst kommer att baseras på en bildnedladdning med vilken vi måste skapa en tråd som gör att vi kan hantera åtkomst och nedladdning över internet eftersom HUVUD eller UI -tråd tillåter inte denna åtgärd.
Vi börjar med att skapa ett nytt projekt med en tom aktivitet, vi har betecknat det här projektet "MultiThreadExample", med en enkel aktivitet vi kommer att skapa strukturen för XML -filen som tillhör denna aktivitet.
Vi har ett textfält, en knapp, en linjär layout som motsvarar en obestämd laddningsfält som vi kommer att använda senare och en listvy som innehåller en rad webbadresser till bilder som finns på internet. I filen som innehåller Java -klassen för vår (unika) aktivitet är den skriven med följande kod:
paket com.omglabs.multithreaexample; importera android.support.v7.app.AppCompatActivity; importera android.os.Bundle; importera android.view.View; importera android.widget.AdapterView; importera android.widget.EditText; importera android.widget.LinearLayout; importera android.widget.ListView; importera android.widget.ProgressBar; public class MainActivity utökar AppCompatActivity implementerar AdapterView.OnItemClickListener {private EditText editText; privat ListView listView; privata sträng [] webbadresser; privat ProgressBar progressBar; privat LinearLayout progressLayout; @Override skyddad void onCreate (Bundle savedInstanceState) {super.onCreate (saveInstanceState); setContentView (R.layout.activity_main); editText = (EditText) findViewById (R.id.downloadURL); listView = (ListView) findViewById (R.id.listurls); listView.setOnItemClickListener (detta); urls = getResources (). getStringArray (R.array.URLs); progressBar = (ProgressBar) findViewById (R.id.progressbar); progressLayout = (LinearLayout) findViewById (R.id.progresslayout); } nedladdning av offentligt tomrum (View view) {} @Override public void onItemClick (AdapterView adapterView, View view, int i, long l) {editText.setText (url [i]); }}Hittills kan programmet kompileras utan problem, i den här klassen deklarerar vi variablerna:
- redigera text
- Listvy
- webbadresser
- progressBar
- progressLayout
Ett textfält, en lista, ett strängarrangemang, en förloppsindikator och en linjär layout.
I onCreate -metod Vi tilldelar dem respektive vy som tillhör dem och som skapades i XML -filen för aktiviteten, med undantag för de webbadresser som tilldelar dess värden från mappen värden i strängfilen och vars arrangemang deklareras som följer:
http://www.fmdos.cl/wp-content/uploads/2016/03/1.jpg.webp http://vignette3.wikia.nocookie.net/teenwolf/images/9/90/Crystal_Reed_003.jpeg.webp https: // pbs.twimg.com/profile_images/699667844129107968/EvhTFBHN.jpg.webp http://vignette1.wikia.nocookie.net/teen-wolf-pack/images/0/0b/Holland-holland-roden-31699868-500-600.png.webpDen tomma metodnedladdningen (View view) fylls med koden som gör nedladdningen och som är länkad till Nedladdningsknapp Bot genom onclick -attributet. Slutligen onitemclick -metod som tillhör Listvy, fyller det i textfältet när du klickar på någon av webbadresserna i listan. När den här koden har sammanställts kommer den att se ut så här:
I nästa steg kommer vi att skapa de metoder som går vidare till nedladdningen enligt följande steg:
- Skapa ett objekt av URL -klassen (java.net) som representerar webbadressen för nedladdning.
- Öppna anslutningen med det objektet.
- Läs data (via webben) med hjälp av inmatningsklassen i en byte -array.
- Öppna / skapa en utdataströmfil där url -data sparas på SD -kortet.
- Skriv data till den filen.
- Och slutligen stänga anslutningen.
För tillfället kommer det att se ut så här:
offentlig boolsk nedladdning med hjälp avThreads (String -länk) {boolsk bekräftelse = false; URL downloadLink = null; HttpURLConnection conne = null; InputStream inputStream = null; prova {downloadLink = ny URL (länk); anslutning = (HttpURLConnection) downloadLink.openConnection (); inputStream = conne.getInputStream (); } catch (MalformedURLException e) {e.printStackTrace (); } catch (IOException e) {e.printStackTrace (); } slutligen {if (connex! = null) {connex.disconnect (); } if (inputStream! = null) {försök {inputStream.close (); } catch (IOException e) {e.printStackTrace (); }}} returbekräftelse; }Denna metod som vi har byggt behöver bara a Sträng som kommer att vara webbadressen att ladda ner booleskt För att bekräfta nedladdningen är downloadLink URL -objektet, anslutningen är anslutningen som kommer att göras för att komma åt objektet och inputStream är den som kommer att fortsätta läsa data om vi försöker använda den här metoden på knappen downloadBot programmet skulle sluta på grund av att det inte gick att köra på huvudtråd.
Här går vi med användning av trådar, det finns två sätt att göra detta med en klass och det är genom att utöka den klassen till tråd eller implementera klassen Runnable, den här klassen är inte en tråd, den låter dig helt enkelt skapa en metod som du kan köras på ett ögonblick specifikt och om du skapar en separat tråd kör den i den.
Inne i nedladdningsknappen skriver vi den här koden och den kommer att se ut så här:
public void download (Visa vy) {Thread mThread = new Thread (new mRunn ()); mThread.start (); }Här skapar vi en ny tråd som behöver ett Runnable -objekt som vi skapar i en privat klass så här:
privat klass mRunn implementerar Runnable {@Override public void run () {download usingThreads (urls [0]); }}Skapa privat klass
NoteraKom ihåg att detta är allt i Java -klassen för vår enda aktivitet.
Med raden:
ladda ner usingThreads (webbadresser [0]);Vi kallar funktionen som vi skapade där vi öppnade anslutningen, ett objekt i URL -arrayen skickas till den så att den kan läsa data från den adressen. Senare kommer det att ändras.
Om vi försökte köra den här applikationen genom att trycka på knappen, skulle applikationen sluta, eftersom vi behöver ett särskilt tillstånd för att komma åt internet, vilket begärs genom manifestet för vår applikation. Lägga till raden före etiketten:
Nu för att verifiera att programmet faktiskt utför nedladdningen kommer vi att lägga till några rader med kod till nedladdningsmetod med hjälp av Threads, det kommer att se ut så här:
offentlig boolsk nedladdning med hjälp avThreads (String -länk) {boolsk bekräftelse = false; URL downloadLink = null; HttpURLConnection conne = null; InputStream inputStream = null; FileOutputStream archOutputStream = null; Filfil = null; prova {downloadLink = ny URL (länk); anslutning = (HttpURLConnection) downloadLink.openConnection (); inputStream = conne.getInputStream (); file = new File (Environment.getExternalStoragePublicDirectory (Environment.DIRECTORY_DOWNLOADS) + "/" + Uri.parse (länk) .getLastPathSegment ()); archOutputStream = ny FileOutputStream (fil); int Läs = -1; byte [] buffert = ny byte [1024]; medan ((Read = inputStream.read (buffer))! = -1) {archOutputStream.write (buffer, 0, Read); } bekräftelse = sant; } catch (MalformedURLException e) {e.printStackTrace (); } catch (IOException e) {e.printStackTrace (); } slutligen {if (connex! = null) {connex.disconnect (); } if (inputStream! = null) {försök {inputStream.close (); } catch (IOException e) {e.printStackTrace (); }} if (archOutputStream! = null) {prova {archOutputStream.close (); } catch (IOException e) {e.printStackTrace (); }}} returbekräftelse; } FileOutputStream archOutputStream = null; Filfil = null;Deklarationerna för dessa objekt representerar skrivningen av filen som läses och den tomma filen där avläsningen kommer att sparas.
file = new File (Environment.getExternalStoragePublicDirectory (Environment.DIRECTORY_DOWNLOADS) + "/" + Uri.parse (url [0]). getLastPathSegment ()); archOutputStream = ny FileOutputStream (fil); int Läs = -1; byte [] buffert = ny byte [1024]; medan ((Read = inputStream.read (buffer))! = -1) {archOutputStream.write (buffer, 0, Read); } bekräftelse = sant;"Fil" är det tomma filobjekt vars adress är konstruerad genom att komma åt SD -kortet "Environment.getExternalStoragePublicDirectory (Environment.DIRECTORY_DOWNLOADS)" och lägga till ett snedstreck "/" och det sista segmentet i URL: en som vanligtvis representerar filens namn till ladda ner, uppnår vi detta med metoden getLastPathSegment ().
Innan vi testar applikationen kommer vi att lägga till en sista behörighet i manifestet:
Efter att ha kört programmet på emulatorn eller Android -enheten, när vi trycker på knappen kommer vi att se att det tydligen inte händer något, men om vi kontrollerar nedladdningsmappen med en filutforskare inser vi att det första objektet på listan har laddats ner; ett foto som heter 1.jpg.webp.
Att göra det dynamisk applikation och implementera URL: erna i listvyn, vi kommer att uppdatera nedladdningsmetod (Visa vy) och vi kommer att lägga till detta som den första raden:
Stränglänk = editText.getText (). ToString ();Och i mRunn -klass vi lägger till detta före metoden run ():
privat klass mRunn implementerar Runnable {private String -länk; offentlig mRunn (stränglänk) {this.link = länk; } @Override public void run () {download usingThreads (länk); }}Och i mRunn -klass vi lägger till detta före metoden run ():
Så vi kan skicka länkvariabeln från textfältet till metoden som utför nedladdningen. Programmet är fullt fungerande, även om det saknar lite användarvänlighet, så vi kommer att försöka fixa detta med hjälp av den förloppsindikator som vi deklarerade i början.
I mRunn -klassen i körningen () -metoden kommer vi att inkludera:
MainActivity.this.runOnUiThread (new Runnable () {@Override public void run () {progressLayout.setVisibility (View.VISIBLE);}});Innan samtalet till downloadusandoThreads. Detta gör att laddningsfältet visas när vi trycker på knappen, i den sista klausulen i nedladdningsmetod med hjälp av Threads.
Vi kommer att lägga till:
this.runOnUiThread (new Runnable () {@Override public void run () {progressLayout.setVisibility (View.GONE);}});Så när nedladdningen är klar försvinner fältet igen. Detta sker oavsett om nedladdningen lyckades eller inte.
Och det här har varit allt, ett kort implementering av flera trådarDetta är lite tråkigt och medför vissa komplikationer för mer komplexa applikationer.Det mest effektiva sättet att utföra denna uppgif.webpt, i vårt fall att ladda ner några bilder, är att använda AsyncTasks.