În acest tutorial vom explica cum acționează Android atunci când rulează un serviciu, vom descrie în ce constau firele de execuție și despre ce sunt procesele. Acest lucru ne va permite să înțelegem modul în care rulează aplicațiile noastre, oferindu-ne un control și o stabilitate mai mari pe dispozitivele mobile pe care vor fi instalate.
Fir
Când utilizatorul rulează o aplicație, Android creează un fir numit main (main). Acest fir este foarte important deoarece este responsabil de gestionarea evenimentelor pe care utilizatorul le declanșează la componentele corespunzătoare și include, de asemenea, evenimentele care atrag ecranul. Un fir de execuție, cea mai mică parte care poate fi procesată de un programator într-un sistem de operare, în acest caz Android (cu kernel Linux).
implementarea mai multor fire care sunt procesate în același timp în aceeași aplicație, (numiți-o concurență, care se referă la simultaneitatea execuției), este cunoscut sub numele de multithreading. Multithreading este aplicat astfel încât aceste fire să partajeze resurse Și asta este ceea ce cuprinde un proces, amintiți-vă că acest lucru poate fi aplicat programatic în codul aceleiași aplicații, implementarea multithreading-ului la nivelul sistemului de operare nu depinde de noi.
Începutul ciclului de viață al unei aplicații include generarea unui nou proces Linux căruia i se atribuie un fir principal sau un fir UI (firul care este responsabil pentru procesarea grafică a aplicației, firul interfeței utilizator în limba engleză).
NotăCiclul de viață include executarea metodelor: onCreate (), onStart () și onResume (); la început și la sfârșit: onPause (), onStop () și onDestroy ().
Un proces poate fi forțat să se închidă de Android din cauza lipsei de memorie, acest tip de carcasă este rar datorită progresului tehnologic, dar se întâmplă totuși.
Întrebarea este: Ce procese Android decide să închidă?
Acestea sunt închise prin compararea nivelului lor de importanță, se rezumă după cum urmează:
Cel mai important: procesele din prim planUtilizatorul interacționează cu procesul menționat (metoda onResume () a procesului menționat rulează în prezent). Există un serviciu care rulează metodele sale de ciclu de viață. Sau există o BroadcastReceiver rularea lui metoda onReceive ().
Al doilea cel mai important: Procese vizibileActivitate cu apel la metoda onPause (). Serviciu legat de o activitate vizibilă (serviciu legat).
Al treilea cel mai important: Procesul cu un serviciuUtilizatorul nu interacționează direct cu procesul. Procesul are un serviciu care rulează în fundal.
Al doilea cel mai puțin important: procesul de fundalNu există niciun tip de interacțiune cu utilizatorul. Cel mai recent proces vizualizat de utilizator va fi ultimul care va fi distrus.
Cel mai puțin important: Procesul golNu are componente active. Procesul este încă în viață în scop cache, împiedicând utilizatorul să revină la utilizarea acestui proces.
Acesta din urmă, procesul gol, este primul care se termină în caz de lipsă de memorie. Astfel, o aplicație care implementează un serviciu în care este creat un fir pentru a descărca conținut de pe internet, va fi mai importantă decât o aplicație care creează firul fără a implementa un serviciu, astfel încât este mai probabil să fie terminat înainte de finalizarea descărcării. , deoarece sunt procese de lungă durată.
Pentru a înțelege multhreading să vedem cum Android gestionează firul său principal.
PROCESUL A are o interfață de utilizare sau Fir PRINCIPAL, acest fir tratează un coadă de mesaje sau coada de mesaje, care rulează pe măsură ce firul devine inactiv, cine se ocupă de acest lucru? Looper.
Looper este o clasă de interfață utilizator Android Java că, împreună cu Clasa de manipulare, procesează evenimente de interfață cu utilizatorul, cum ar fi apăsarea butoanelor, ecranele redesenate și comutatoarele de orientare. Evenimentele pot fi, de asemenea, utilizate pentru a încărca conținut într-un serviciu HTTP, a redimensiona imaginile și a executa cereri de la distanță. Caracteristica cheie a acestor clase este că sunt capabile să implementeze un model de concurență.
Clasa Android Looper conține o MessageQueue (coadă de mesaje) și este asociat doar cu subiectul din care a fost creat. Vă rugăm să rețineți că această conexiune nu poate fi întreruptă și că lLoper nu poate fi atașat la niciun alt fir. De asemenea, Looper se află în spațiul de stocare local și poate fi apelat numai dintr-o metodă statică. O metodă de etapizare verifică dacă un Looper este deja asociat cu un fir, iar apoi metoda statică creează Looper. Apoi, o buclă poate fi utilizată pentru a verifica mesajele de pe coadă.
Până acum înțelegem mai multe concepte: proces, fir, fir UI, looper, dar încă nu știm de ce multithreading.
Operațiuni pe termen lung
Se consideră durată lungă pentru orice metodă a cărei execuție depășește 5 secunde, ceea ce declanșează mesajul tipic „aplicația nu răspunde. Vrei să o închizi?
Care pot fi aceste operațiuni?: Acces la Internet, Interogări SQL, analiză XML / HTML / JSON, procesare grafică complexă. Oricare dintre aceste operații care se execută în firul principal îl va bloca și, din moment ce este cea care gestionează interfața grafică a utilizatorului, este interpretată ca o înghețare pe care Android decide să o închidă.
Să ne imaginăm doar că oricare dintre aceste operații durează 7 secunde și utilizatorul decide să scrie ceva într-o anumită intrare de text, astfel încât, în timp ce aceste 7 secunde nu au trecut, firul de interfață nu poate actualiza vizualizarea, astfel încât utilizatorul să aprecieze că scrie, și deci generează o înghețare, se declanșează mesajul „fără răspuns” cu care aveți două opțiuni, așteptați sau distrugeți, deși nu puteți ști niciodată cât să așteptați, ar putea fi câteva secunde sau chiar minute în funcție de coada de mesaje care au firul principal.
Cum evităm înghețarea?
Folosind fire sau servicii, în funcție de faptul dacă sarcina necesită modificarea vizualizării, în acest caz este implementat un serviciu deoarece vizualizarea unei aplicații nu poate fi modificată în afara firului UI. Cea mai bună modalitate de a evita înghețarea este să utilizați Sarcini asincrone cu clasa AsyncTask, în acest tutorial vom implementa mai multe fire pentru a înțelege comportamentul arhitecturii Android.
Cod și dezvoltare
Proiectul pe care îl vom crea în continuare se va baza pe o descărcare a imaginii cu care trebuie să creăm un fir care ne permite să gestionăm accesul și să descărcăm pe internet deoarece PRINCIPAL sau Fir UI nu permite această acțiune.
Vom începe prin crearea unui nou proiect cu o activitate goală, am denumit acest proiect „MultiThreadExample”, cu o singură activitate simplă vom crea structura fișierului XML care aparține acestei activități.
Avem un câmp de text, un buton, un aspect liniar care corespunde unei bare de încărcare nedeterminate pe care le vom folosi ulterior și o vizualizare listă care conține o serie de adrese URL ale imaginilor găzduite pe internet. În fișierul care conține clasa Java pentru activitatea noastră (unică), este scris cu următorul cod:
pachet com.omglabs.multithreaexample; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.AdapterView; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.ProgressBar; public class MainActivity extinde AppCompatActivity implementează AdapterView.OnItemClickListener {private EditText editText; ListView privat listView; private String [] adrese URL; private ProgressBar progressBar; private LinearLayout progressLayout; @Override protected void onCreate (Bundle savedInstanceState) {super.onCreate (savedInstanceState); setContentView (R.layout.activity_main); editText = (EditText) findViewById (R.id.downloadURL); listView = (ListView) findViewById (R.id.listurls); listView.setOnItemClickListener (aceasta); urls = getResources (). getStringArray (R.array.URLs); ProgressBar = (ProgressBar) findViewById (R.id.progressbar); progressLayout = (LinearLayout) findViewById (R.id.progresslayout); } public void download (View view) {} @Override public void onItemClick (AdapterView adapterView, View view, int i, long l) {editText.setText (urls [i]); }}Până acum aplicația poate fi compilată fără nicio problemă, în această clasă declarăm variabilele:
- editează textul
- listView
- urluri
- bara de progres
- progressLayout
Un câmp de text, o listă, un aranjament de șiruri, o bară de progres și un aspect liniar.
În metoda onCreate Le atribuim vizualizarea respectivă care le aparține și care a fost creată în fișierul XML al activității, cu excepția adreselor URL care îi atribuie valorile din folderul de valori din fișierul șir și al cărui aranjament este declarat după cum urmează:
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.webpMetoda goală de descărcare (Vizualizare vizualizare) va fi completată cu codul care va efectua descărcarea și care este legat de Buton de descărcare Bot prin atributul onclick. În cele din urmă metoda onitemclick care aparține listview, umple câmpul de text când faceți clic pe oricare dintre adresele URL conținute în listă. Odată compilat acest cod va arăta astfel:
În pasul următor vom crea metodele care vor continua la descărcare, urmând acești pași:
- Creați un obiect din clasa URL (java.net) care va reprezenta adresa URL de descărcat.
- Deschideți conexiunea folosind acel obiect.
- Citiți datele (prin web) folosind clasa fluxului de intrare într-o matrice de octeți.
- Deschideți / creați un fișier de flux de ieșire în care datele URL vor fi salvate pe cardul SD.
- Scrieți datele în fișierul respectiv.
- Și în cele din urmă închideți conexiunea.
Deocamdată va arăta astfel:
descărcare publică booleană folosind Threads (String link) {confirmare booleană = falsă; URL downloadLink = nul; HttpURLConnection conne = nul; InputStream inputStream = nul; încercați {downloadLink = URL nou (link); conexiune = (HttpURLConnection) downloadLink.openConnection (); inputStream = conne.getInputStream (); } catch (MalformedURLException e) {e.printStackTrace (); } catch (IOException e) {e.printStackTrace (); } în cele din urmă {if (conex! = null) {connex.disconnect (); } if (inputStream! = null) {try {inputStream.close (); } catch (IOException e) {e.printStackTrace (); }}} confirmare returnare; }Această metodă pe care am construit-o va avea nevoie doar de un Şir care va fi adresa URL de descărcat este boolean Pentru a confirma descărcarea, downloadLink este obiectul URL, conexiunea este conexiunea care va fi făcută pentru a accesa obiectul și inputStream este cea care va continua să citească datele, dacă încercăm să folosim această metodă pe buton downloadBot aplicația s-ar opri din cauza imposibilității de a rula pe fir principal.
Aici mergem cu utilizarea thread-urilor, există două moduri de a face acest lucru cu o clasă și este prin extinderea acelei clase la Thread sau implementarea clasei Runnable, această clasă nu este un thread, vă permite pur și simplu să creați o metodă pe care poate rula într-un moment specific și dacă creați un fir separat, rulați-l în el.
În interiorul butonului de descărcare vom scrie acest cod și va arăta astfel:
public void download (View view) {Fir mThread = fir nou (mRunn nou ()); mThread.start (); }Aici creăm un fir nou care are nevoie de un obiect Runnable pe care îl creăm într-o clasă privată ca aceasta:
clasa privată mRunn implementează Runnable {@Override public void run () {download usingThreads (urls [0]); }}Creați curs privat
NotăAmintiți-vă că toate acestea fac parte din clasa Java a singurei noastre activități.
Cu linia:
descărcare folosind Threads (urls [0]);Apelăm la funcția pe care am creat-o în cazul în care am deschis conexiunea, un element din matricea de adrese URL îi este transmis, astfel încât să poată citi datele de pe acea adresă. Mai târziu va fi modificat.
Dacă am încerca să rulăm această aplicație apăsând butonul, aplicația s-ar opri, deoarece avem nevoie de o permisiune specială pentru a accesa internetul, care este solicitat prin manifestul aplicației noastre. Adăugarea liniei, înainte de etichetă:
Acum, pentru a verifica dacă aplicația efectuează efectiv descărcarea, vom adăuga câteva linii de cod la metoda de descărcare folosind Threads, va arăta astfel:
descărcare publică booleană folosind Threads (String link) {confirmare booleană = falsă; URL downloadLink = nul; HttpURLConnection conne = nul; InputStream inputStream = nul; FileOutputStream archOutputStream = nul; Fișier fișier = nul; încercați {downloadLink = URL nou (link); conexiune = (HttpURLConnection) downloadLink.openConnection (); inputStream = conne.getInputStream (); file = new File (Environment.getExternalStoragePublicDirectory (Environment.DIRECTORY_DOWNLOADS) + "/" + Uri.parse (link) .getLastPathSegment ()); archOutputStream = nou FileOutputStream (fișier); int Citit = -1; octet [] tampon = octet nou [1024]; while ((Read = inputStream.read (buffer))! = -1) {archOutputStream.write (buffer, 0, Read); } confirmare = adevărat; } catch (MalformedURLException e) {e.printStackTrace (); } catch (IOException e) {e.printStackTrace (); } în cele din urmă {if (conex! = null) {connex.disconnect (); } if (inputStream! = null) {try {inputStream.close (); } catch (IOException e) {e.printStackTrace (); }} if (archOutputStream! = null) {try {archOutputStream.close (); } catch (IOException e) {e.printStackTrace (); }}} confirmare returnare; } FileOutputStream archOutputStream = nul; Fișier fișier = nul;Declarațiile acestor obiecte reprezintă scrierea fișierului care este citit și fișierul gol în care va fi salvată citirea.
file = new File (Environment.getExternalStoragePublicDirectory (Environment.DIRECTORY_DOWNLOADS) + "/" + Uri.parse (urls [0]). getLastPathSegment ()); archOutputStream = nou FileOutputStream (fișier); int Citit = -1; octet [] tampon = octet nou [1024]; while ((Read = inputStream.read (buffer))! = -1) {archOutputStream.write (buffer, 0, Read); } confirmare = adevărat;„Fișier” este obiectul Fișier gol a cărui adresă este construită accesând cardul SD „Environment.getExternalStoragePublicDirectory (Environment.DIRECTORY_DOWNLOADS)” și adăugând o bară „/” și ultimul segment al adresei URL care reprezintă în general numele fișierului descărcare, realizăm acest lucru cu metoda getLastPathSegment ().
Înainte de a testa aplicația, vom adăuga o ultimă permisiune în manifest:
După rularea aplicației pe emulator sau dispozitiv Android, la apăsarea butonului vom vedea că aparent nu se întâmplă nimic, dar dacă verificăm folderul Descărcare cu un explorator de fișiere ne vom da seama că primul articol din listă a fost descărcat; o fotografie numită 1.jpg.webp.
Să o facă aplicație dinamică și implementați adresele URL ale listview, vom actualiza metoda de descărcare (Vizualizare vizualizare) și vom adăuga acest lucru, ca prima linie:
String link = editText.getText (). ToString ();Și în clasa mRunn vom adăuga acest lucru, înainte de metoda run ():
clasa privată mRunn implementează Runnable {private String link; public mRunn (String link) {this.link = link; } @Override public void run () {descărcare folosind Threads (link); }}Și în clasa mRunn vom adăuga acest lucru, înainte de metoda run ():
Deci, putem trece variabila link din câmpul text către metoda care efectuează descărcarea. Aplicația în acest moment este complet funcțională, deși îi lipsește un pic de ușurință în utilizare, așa că vom încerca să remediem acest lucru, folosind bara de progres pe care am declarat-o la început.
În clasa mRunn în metoda run () vom include:
MainActivity.this.runOnUiThread (new Runnable () {@Override public void run () {progressLayout.setVisibility (View.VISIBLE);}});Înainte de apelul către downloadusandoThreads. Acest lucru va face ca bara de încărcare să apară atunci când apăsăm butonul, în clauza finală a metoda de descărcare folosind Threads.
Vom adăuga:
this.runOnUiThread (new Runnable () {@Override public void run () {progressLayout.setVisibility (View.GONE);}});Deci, când descărcarea este finalizată, bara dispare din nou. Acest lucru se va întâmpla indiferent dacă descărcarea are succes sau nu.
Și asta a fost tot, unul implementarea pe scurt a mai multor fireAcest lucru este puțin obositor și aduce unele complicații pentru aplicații mai complexe.Cel mai eficient mod de a realiza această sarcină, în cazul nostru descărcarea unor imagini, este utilizarea AsyncTasks.