Programma Android: Processen, threads en services

In deze tutorial leggen we de hoe Android werkt bij het uitvoeren van een service, zullen we beschrijven waar de uitvoeringsthreads uit bestaan ​​en waar de processen over gaan. Hierdoor krijgen we inzicht in de manier waarop onze applicaties worden uitgevoerd, waardoor we meer controle en stabiliteit hebben op de mobiele apparaten waarop ze worden geïnstalleerd.

Draad


Wanneer de gebruiker een toepassing uitvoert, Android maakt een thread met de naam main (main). Deze thread is erg belangrijk omdat deze verantwoordelijk is voor het beheren van de gebeurtenissen die de gebruiker activeert naar de juiste componenten en ook de gebeurtenissen omvat die het scherm tekenen. Een uitvoeringsthread, het kleinste onderdeel dat door een planner in een besturingssysteem, in dit geval Android (met Linux-kernel), kan worden verwerkt.

De implementatie van meerdere threads die tegelijkertijd in dezelfde applicatie worden verwerkt (noem het concurrency, wat verwijst naar de gelijktijdigheid van uitvoering), het staat bekend als multithreading. Multithreading wordt toegepast zodat deze threads bronnen delen En dit is wat een proces inhoudt, onthoud dat dit programmatisch kan worden toegepast binnen de code van dezelfde applicatie, de implementatie van multithreading op het niveau van het besturingssysteem hangt niet van ons af.

Het begin van de levenscyclus van een applicatie omvat het genereren van een nieuw Linux-proces waaraan een hoofdthread of UI-thread is toegewezen (de thread die verantwoordelijk is voor de grafische verwerking van de applicatie, gebruikersinterfacethread in het Engels).

OpmerkingDe levenscyclus omvat de uitvoering van de methoden: onCreate (), onStart () en onResume (); aan het begin en aan het einde: onPause (), onStop () en onDestroy ().

Een proces kan door Android worden gedwongen te sluiten vanwege een gebrek aan geheugen, dit soort gevallen is zeldzaam vanwege technologische vooruitgang, maar het gebeurt nog steeds.

De vraag is: Welke processen Android besluit te sluiten?

Deze worden afgesloten door hun mate van belangrijkheid te vergelijken, het is als volgt samengevat:

De belangrijkste: voorgrondprocessenDe gebruiker heeft interactie met het proces (de methode onResume () van het proces is momenteel actief). Er is een service die zijn levenscyclusmethoden uitvoert. Of is er een Uitzending ontvanger het runnen van zijn onReceive () methode.

De tweede belangrijkste: zichtbare processenActiviteit met oproep naar onPause () methode. Dienst gekoppeld aan een zichtbare activiteit (gebonden dienst).

De derde belangrijkste: Proces met een serviceDe gebruiker heeft geen directe interactie met het proces. Het proces heeft een service die op de achtergrond draait.

Het op één na minst belangrijke: AchtergrondprocesEr is geen enkele vorm van interactie met de gebruiker. Het proces dat het laatst door de gebruiker is bekeken, is het laatste dat wordt vernietigd.

De minst belangrijke: leeg procesHet heeft geen actieve componenten. Het proces is nog steeds actief voor cachingdoeleinden, waardoor de gebruiker niet kan terugkeren naar het gebruik van dat proces.

Dit laatste, het lege proces, wordt als eerste beëindigd bij gebrek aan geheugen. Een applicatie die een service implementeert waarbij een thread wordt gemaakt om inhoud van internet te downloaden, zal dus belangrijker zijn dan een applicatie die de thread maakt zonder een service te implementeren, zodat de kans groter is dat deze wordt beëindigd voordat de download is voltooid. , omdat het langdurige processen zijn.

Om de te begrijpen multithreading laten we eens kijken hoe Android omgaat met zijn hoofdthema.

PROCES A heeft een UI of HOOFDdraad, deze draad behandelt een berichtenwachtrij of berichtenwachtrij, die wordt uitgevoerd als de thread inactief wordt, wie handelt dit af? De Looper.

Looper is een gebruikersinterfaceklasse van Android Java dat, samen met de handler klasse, verwerkt gebeurtenissen in de gebruikersinterface, zoals het indrukken van knoppen, opnieuw getekende schermen en oriëntatiewisselingen. Gebeurtenissen kunnen ook worden gebruikt om inhoud in een HTTP-service te laden, het formaat van afbeeldingen te wijzigen en externe verzoeken uit te voeren. Het belangrijkste kenmerk van deze klassen is dat ze een gelijktijdigheidspatroon kunnen implementeren.

De Android Looper-klasse bevat een BerichtWachtrij (berichtenwachtrij) en is alleen gekoppeld aan het onderwerp van waaruit het is gemaakt. Houd er rekening mee dat deze verbinding niet kan worden verbroken en dat de lLooper het kan niet aan een ander draad worden gekoppeld. De Looper staat ook op lokale opslag en kan alleen worden aangeroepen vanuit een statische methode. Een staging-methode controleert of een Looper al aan een thread is gekoppeld, en vervolgens maakt de statische methode de Looper. Daarna kan een lus worden gebruikt om de berichten in de wachtrij te controleren.

Tot nu toe begrijpen we verschillende concepten: proces, thread, UI-thread, looper, maar we weten nog steeds niet waarom de multithreading.

Langdurige operaties


Het wordt als lang beschouwd voor elke methode waarvan de uitvoering meer dan 5 seconden bedraagt, wat het typische bericht 'de toepassing reageert niet' activeert. Wil je hem sluiten?

Wat kunnen deze handelingen zijn?: Internettoegang, SQL-query's, XML / HTML / JSON-parsing, complexe grafische verwerking. Elk van deze bewerkingen die in de hoofdthread worden uitgevoerd, blokkeert het, en aangezien het degene is die de grafische gebruikersinterface afhandelt, wordt het geïnterpreteerd als een bevriezing, die Android besluit te sluiten.

Laten we ons voorstellen dat een van deze bewerkingen 7 seconden duurt en de gebruiker besluit iets in een tekstinvoer te schrijven, dus hoewel deze 7 seconden niet zijn verstreken, kan de UI-thread de weergave niet bijwerken, zodat de gebruiker waardeert dat hij aan het schrijven is, en dus het genereert een bevriezing, het bericht "geen reactie" wordt geactiveerd waarmee je twee opties hebt, wachten of vernietigen, hoewel je nooit kunt weten hoe lang je moet wachten, het kan een paar seconden of zelfs minuten zijn, afhankelijk van de berichtenwachtrij die de rode draad hebben.

Hoe voorkomen we bevriezing?


Met behulp van threads of services, afhankelijk van of de taak de weergave moet wijzigen, wordt in dit geval een service geïmplementeerd omdat de weergave van een toepassing niet kan worden gewijzigd buiten de UI-thread. De beste manier om bevriezing te voorkomen, is door Asynchronous Tasks te gebruiken met de AsyncTask-klasse. In deze tutorial zullen we meerdere threads implementeren om het gedrag van de Android-architectuur te begrijpen.

Code en ontwikkeling


Het project dat we hierna gaan maken zal gebaseerd zijn op een afbeeldingsdownload waarmee we een thread moeten maken waarmee we toegang en download via internet kunnen beheren, omdat de VOORNAAMST of UI-thread staat deze actie niet toe.

We beginnen met het maken van een nieuw project met een lege activiteit, we hebben dit project genoemd "MultiThreadVoorbeeld", met een enkele simpele activiteit we zullen de structuur van het XML-bestand maken dat hoort bij deze activiteit.

 
We hebben een tekstveld, een knop, een lineaire lay-out die overeenkomt met een onbepaalde laadbalk die we later zullen gebruiken, en een lijstweergave die een reeks URL's van afbeeldingen bevat die op internet worden gehost. In het bestand dat de Java-klasse voor onze (unieke) activiteit bevat, is het geschreven met de volgende code:
 pakket com.omglabs.multithreavoorbeeld; android.support.v7.app.AppCompatActivity importeren; android.os.Bundel importeren; android.view.View importeren; android.widget.AdapterView importeren; android.widget.EditText importeren; android.widget.LinearLayout importeren; android.widget.ListView importeren; android.widget.ProgressBar importeren; openbare klasse MainActivity breidt AppCompatActivity uit implementeert AdapterView.OnItemClickListener {private EditText editText; privé Lijstweergave lijstweergave; privé String [] URL's; privé ProgressBar voortgangsbalk; privé LinearLayout voortgangLayout; @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 (dit); urls = getResources (). getStringArray (R.array.URL's); 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]); }} 
Tot nu toe kan de applicatie probleemloos worden gecompileerd, in deze klasse declareren we de variabelen:
  • tekst bewerken
  • lijstweergave
  • URL's
  • voortgangsbalk
  • voortgangLay-out

Een tekstveld, een lijst, een tekenreeksrangschikking, een voortgangsbalk en een lineaire lay-out.

In de onCreate-methode We wijzen hieraan de respectieve weergave toe die bij hen hoort en die zijn gemaakt in het XML-bestand van de activiteit, met uitzondering van de url's die de waarden ervan toewijzen uit de waardenmap in het tekenreeksbestand en waarvan de rangschikking is gedeclareerd als volgt:

 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.webp 
De lege methode download (View view) wordt gevuld met de code die de download zal doen en die is gekoppeld aan de Downloadknop Bot via het attribuut onclick. Eindelijk, de onitemclick-methode die behoort tot lijstweergave, het vult het tekstveld wanneer u op een van de URL's in de lijst klikt. Eenmaal gecompileerd ziet deze code er als volgt uit:

In de volgende stap zullen we de methoden maken die naar de download gaan, door deze stappen te volgen:

  • Maak een object van de URL-klasse (java.net) dat de te downloaden URL vertegenwoordigt.
  • Open de verbinding met dat object.
  • Lees de gegevens (via internet) met behulp van de invoerstroomklasse in een bytearray.
  • Open / maak een uitvoerstroombestand waarin de url-gegevens op de SD-kaart worden opgeslagen.
  • Schrijf de gegevens naar dat bestand.
  • En sluit tenslotte de verbinding.

Voor nu ziet het er zo uit:

 openbare booleaanse download met behulp vanThreads (String-link) {booleaanse bevestiging = false; URL downloadLink = null; HttpURLConnection conne = null; InputStream inputStream = null; probeer {downloadLink = nieuwe URL (link); verbinding = (HttpURLConnection) downloadLink.openConnection (); inputStream = conne.getInputStream (); } catch (MalformedURLException e) {e.printStackTrace (); } catch (IOException e) {e.printStackTrace (); } tenslotte {if (connex! = null) {connex.disconnect (); } if (inputStream! = null) {probeer {inputStream.close (); } catch (IOException e) {e.printStackTrace (); }}} retourbevestiging; } 
Deze methode die we hebben gebouwd, heeft alleen een Draad wat de URL is om te downloaden, is booleaans Om de download te bevestigen, is downloadLink het URL-object, verbinding is de verbinding die wordt gemaakt om toegang te krijgen tot het object en inputStream is degene die doorgaat met het lezen van de gegevens, als we deze methode op de knop proberen te gebruiken downloadBot de toepassing zou stoppen vanwege het niet kunnen draaien op de rode draad.

Hier gaan we met het gebruik van threads, er zijn twee manieren om dit met een klasse te doen en het is door die klasse uit te breiden naar Thread of de Runnable-klasse te implementeren, deze klasse is geen thread, maar u kunt eenvoudig een methode maken die u kan op een specifiek moment worden uitgevoerd en als u een aparte thread maakt, voert u deze erin uit.

Binnen de downloadknop zullen we deze code schrijven, en het zal er als volgt uitzien:

 public void download (weergave bekijken) {Thread mThread = nieuwe thread (nieuwe mRunn ()); mThread.start (); } 
Hier maken we een nieuwe thread die een Runnable-object nodig heeft dat we in een privéklasse als deze maken:
 private class mRunn implementeert Runnable {@Override public void run () {download usingThreads (urls [0]); }} 
Privéles maken

OpmerkingOnthoud dat dit allemaal in de Java-klasse van onze enige activiteit zit.

Met de regel:

 downloaden viaThreads (urls [0]);
We noemen de functie die we hebben gemaakt waar we de verbinding hebben geopend, een item van de URL-array wordt eraan doorgegeven zodat het de gegevens van dat adres kan lezen. Later wordt het aangepast.

Als we probeerden deze applicatie uit te voeren door op de knop te drukken, zou de applicatie stoppen, omdat we een speciale toestemming nodig hebben om toegang te krijgen tot internet, die wordt aangevraagd via het manifest van onze applicatie. De regel toevoegen, vóór het label:

 
Om nu te verifiëren dat de applicatie de download daadwerkelijk uitvoert, zullen we een paar regels code toevoegen aan de downloadmethode met behulp van Threads, ziet het er als volgt uit:
 openbare booleaanse download met behulp vanThreads (String-link) {booleaanse bevestiging = false; URL downloadLink = null; HttpURLConnection conne = null; InputStream inputStream = null; FileOutputStream archOutputStream = null; Bestandsbestand = null; probeer {downloadLink = nieuwe URL (link); verbinding = (HttpURLConnection) downloadLink.openConnection (); inputStream = conne.getInputStream (); file = nieuw bestand (Environment.getExternalStoragePublicDirectory (Environment.DIRECTORY_DOWNLOADS) + "/" + Uri.parse (link) .getLastPathSegment ()); archOutputStream = nieuwe FileOutputStream (bestand); int Lezen = -1; byte [] buffer = nieuwe byte [1024]; while ((Read = inputStream.read (buffer))! = -1) {archOutputStream.write (buffer, 0, Read); } bevestiging = waar; } catch (MalformedURLException e) {e.printStackTrace (); } catch (IOException e) {e.printStackTrace (); } tenslotte {if (connex! = null) {connex.disconnect (); } if (inputStream! = null) {probeer {inputStream.close (); } catch (IOException e) {e.printStackTrace (); }} if (archOutputStream! = null) {probeer {archOutputStream.close (); } catch (IOException e) {e.printStackTrace (); }}} retourbevestiging; } FileOutputStream archOutputStream = null; Bestandsbestand = null; 
De declaraties van deze objecten vertegenwoordigen het schrijven van het bestand dat wordt gelezen en het lege bestand waarin de meting wordt opgeslagen.
 file = nieuw bestand (Environment.getExternalStoragePublicDirectory (Environment.DIRECTORY_DOWNLOADS) + "/" + Uri.parse (urls [0]). getLastPathSegment ()); archOutputStream = nieuwe FileOutputStream (bestand); int Lezen = -1; byte [] buffer = nieuwe byte [1024]; while ((Read = inputStream.read (buffer))! = -1) {archOutputStream.write (buffer, 0, Read); } bevestiging = waar; 
"File" is het lege File-object waarvan het adres is samengesteld door de SD-kaart "Environment.getExternalStoragePublicDirectory (Environment.DIRECTORY_DOWNLOADS)" te openen en een schuine streep "/" toe te voegen en het laatste segment van de URL dat over het algemeen de naam van het bestand vertegenwoordigt download , bereiken we dit met de methode getLastPathSegment ().

Voordat we de applicatie testen, voegen we een laatste toestemming toe in het manifest:

 
Nadat we de applicatie op de emulator of het Android-apparaat hebben uitgevoerd, zullen we bij het indrukken van de knop zien dat er schijnbaar niets gebeurt, maar als we de downloadmap controleren met een bestandsverkenner, zullen we ons realiseren dat het eerste item op de lijst is gedownload; een foto genaamd 1.jpg.webp.

Om het te doen dynamische applicatie en implementeer de URL's van de lijstweergave, we zullen de updaten downloadmethode (Bekijk weergave) en we zullen dit toevoegen, als de eerste regel:

 String link = editText.getText ().ToString ();
En in de mRun klasse we voegen dit toe, vóór de methode run ():
 private class mRunn implementeert Runnable {private String link; public mRunn (String link) {this.link = link; } @Override public void run () {download usingThreads (link); }}
En in de mRun klasse we voegen dit toe, vóór de methode run ():

We kunnen dus de link-variabele van het tekstveld doorgeven aan de methode die de download uitvoert. De applicatie is op dit moment volledig functioneel, hoewel het een beetje gebruiksvriendelijkheid mist, dus we zullen proberen dit op te lossen met behulp van de voortgangsbalk die we aan het begin hebben aangegeven.

In de mRunn-klasse in de methode run () zullen we het volgende opnemen:

 MainActivity.this.runOnUiThread (nieuwe Runnable () {@Override public void run () {progressLayout.setVisibility (View.VISIBLE);}}); 
Voordat de oproep naar de downloadusandoThreads. Hierdoor verschijnt de laadbalk wanneer we op de knop drukken, in de laatste clausule van de downloadmethode met behulp van Threads.

We zullen toevoegen:

 this.runOnUiThread (nieuwe Runnable () {@Override public void run () {progressLayout.setVisibility (View.GONE);}}); 
Dus als het downloaden is voltooid, verdwijnt de balk weer. Dit gebeurt ongeacht of het downloaden is gelukt.
En dit was alles, één korte implementatie van meerdere threadsDit is een beetje vervelend en brengt enkele complicaties met zich mee voor complexere toepassingen.De meest effectieve manier om deze taak te volbrengen, in ons geval het downloaden van enkele afbeeldingen, is het gebruik van de AsyncTaken.

wave wave wave wave wave