Android programmieren: Prozesse, Threads und Dienste

In diesem Tutorial erklären wir die wie sich Android verhält, wenn ein Dienst ausgeführt wird, beschreiben wir, woraus die Ausführungs-Threads bestehen und worum es bei den Prozessen geht. Auf diese Weise können wir verstehen, wie unsere Anwendungen ausgeführt werden, was uns mehr Kontrolle und Stabilität auf den mobilen Geräten gibt, auf denen sie installiert werden.

Gewinde


Wenn der Benutzer eine Anwendung ausführt, Android erstellt einen Thread namens main (main). Dieser Thread ist sehr wichtig, da er für die Verwaltung der Ereignisse verantwortlich ist, die der Benutzer an die entsprechenden Komponenten auslöst, und umfasst auch die Ereignisse, die den Bildschirm zeichnen. Ein Ausführungsthread, der kleinste Teil, der von einem Scheduler in einem Betriebssystem verarbeitet werden kann, in diesem Fall Android (mit Linux-Kernel).

Das Implementierung mehrerer Threads die gleichzeitig in derselben Anwendung verarbeitet werden (nennen Sie es Parallelität, was sich auf die Gleichzeitigkeit der Ausführung bezieht), es ist als Multithreading bekannt. Multithreading wird angewendet, damit sich diese Threads Ressourcen teilen Und dies umfasst ein Prozess. Denken Sie daran, dass dies programmgesteuert innerhalb des Codes derselben Anwendung angewendet werden kann, die Implementierung von Multithreading auf Betriebssystemebene hängt nicht von uns ab.

Am Anfang des Lebenszyklus einer Anwendung steht die Generierung eines neuen Linux-Prozesses, dem ein Main-Thread oder UI-Thread (der Thread, der für die grafische Verarbeitung der Anwendung zuständig ist, User-Interface-Thread auf Englisch) zugewiesen wird.

NotizDer Lebenszyklus umfasst die Ausführung der Methoden: onCreate(), onStart() und onResume(); am Anfang und am Ende: onPause (), onStop () und onDestroy ().

Ein Prozess kann aufgrund von Speichermangel von Android zum Beenden gezwungen werden. Diese Art von Fall ist aufgrund des technologischen Fortschritts selten, aber es kommt immer noch vor.

Die Frage ist: Welche Prozesse will Android schließen?

Diese werden durch den Vergleich ihrer Wichtigkeit geschlossen, es wird wie folgt zusammengefasst:

Das Wichtigste: VordergrundprozesseDer Benutzer interagiert mit dem Prozess (das onResume()-Verfahren des Prozesses läuft gerade). Es gibt einen Dienst, der seine Lebenszyklusmethoden ausführt. Oder gibt es eine Rundfunkempfänger seine laufen lassen onReceive()-Methode.

Das Zweitwichtigste: Sichtbare ProzesseAktivität mit Anruf an onPause()-Methode. Dienst, der mit einer sichtbaren Aktivität verknüpft ist (gebundener Dienst).

Der drittwichtigste: Prozess mit einem ServiceDer Benutzer interagiert nicht direkt mit dem Prozess. Der Prozess hat einen Dienst, der im Hintergrund ausgeführt wird.

Das zweitwichtigste: HintergrundprozessEs gibt keine Art der Interaktion mit dem Benutzer. Der zuletzt vom Benutzer angezeigte Prozess wird als letzter gelöscht.

Das unwichtigste: Leerer ProzessEs hat keine aktiven Komponenten. Der Prozess ist für Caching-Zwecke noch aktiv und verhindert, dass der Benutzer diesen Prozess wieder verwendet.

Letzterer, der leere Prozess, wird bei Speichermangel als erster beendet. Somit ist eine Anwendung, die einen Dienst implementiert, bei dem ein Thread erstellt wird, um Inhalt aus dem Internet herunterzuladen, wichtiger als eine Anwendung, die den Thread erstellt, ohne einen Dienst zu implementieren, so dass es wahrscheinlicher ist, dass er beendet wird, bevor der Download abgeschlossen ist. , weil es sich um lang anhaltende Prozesse handelt.

Um das zu verstehen multhreading mal sehen, wie Android mit seinem Hauptthread umgeht.

PROZESS A hat eine Benutzeroberfläche oder Haupt-Bedroung, dieser Thread behandelt a Nachrichtenwarteschlange oder Nachrichtenwarteschlange, die ausgeführt wird, wenn der Thread inaktiv wird, wer behandelt das? Das Looper.

Looper ist eine Benutzeroberflächenklasse von Android-Java das zusammen mit dem Handlerklasse, verarbeitet Benutzeroberflächenereignisse wie Tastendrücke, neu gezeichnete Bildschirme und Ausrichtungswechsel. Ereignisse können auch verwendet werden, um Inhalte in einen HTTP-Dienst zu laden, die Größe von Bildern zu ändern und Remote-Anforderungen auszuführen. Das Hauptmerkmal dieser Klassen besteht darin, dass sie ein Parallelitätsmuster implementieren können.

Das Android Looper-Klasse enthält ein MessageQueue (Nachrichtenwarteschlange) und ist nur dem Thema zugeordnet, aus dem sie erstellt wurde. Bitte beachten Sie, dass diese Verbindung nicht unterbrochen werden kann und die lLooper Es kann nicht an einen anderen Thread angehängt werden. Außerdem befindet sich der Looper im lokalen Speicher und kann nur von einer statischen Methode aufgerufen werden. Eine Staging-Methode prüft, ob ein Looper bereits mit einem Thread verknüpft ist, und dann erstellt die statische Methode den Looper. Anschließend kann eine Schleife verwendet werden, um die Nachrichten in der Warteschlange zu überprüfen.

Bisher verstehen wir mehrere Konzepte: Prozess, Thread, UI-Thread, Looper, aber wir wissen immer noch nicht, warum die Multithreading.

Langfristiger Betrieb


Es gilt als lange Dauer für jede Methode, deren Ausführung 5 Sekunden überschreitet, was die typische Meldung auslöst „Die Anwendung antwortet nicht. Möchten Sie es schließen?

Was können diese Operationen sein?: Internetzugang, SQL-Abfragen, XML / HTML / JSON-Parsing, komplexe Grafikverarbeitung. Jede dieser Operationen, die im Hauptthread ausgeführt werden, blockiert sie, und da sie die grafische Benutzeroberfläche verwaltet, wird sie als einfrieren interpretiert, das Android schließt.

Stellen wir uns vor, dass eine dieser Operationen 7 Sekunden dauert und der Benutzer beschließt, etwas in eine Texteingabe zu schreiben es wird also ein Einfrieren erzeugt, die "Keine Antwort"-Nachricht wird ausgelöst, mit der Sie zwei Optionen haben, warten oder zerstören, obwohl Sie nie wissen können, wie lange Sie warten müssen, kann es je nach Nachrichtenwarteschlange einige Sekunden oder sogar Minuten dauern die den Main-Thread haben.

Wie vermeiden wir das Einfrieren?


Bei Verwendung von Threads oder Diensten wird in diesem Fall je nachdem, ob die Aufgabe eine Änderung der Ansicht erfordert, ein Dienst implementiert, da die Ansicht einer Anwendung außerhalb des UI-Threads nicht geändert werden kann. Die beste Möglichkeit, das Einfrieren zu vermeiden, besteht darin, asynchrone Aufgaben mit der AsyncTask-Klasse zu verwenden. In diesem Tutorial werden wir mehrere Threads implementieren, um das Verhalten der Android-Architektur zu verstehen.

Code und Entwicklung


Das Projekt, das wir als nächstes erstellen werden basiert auf einem Bilddownload mit dem wir einen Thread erstellen müssen, der es uns ermöglicht, den Zugriff und das Herunterladen über das Internet zu verwalten, da die HAUPTSÄCHLICH oder UI-Thread lässt diese Aktion nicht zu.

Wir beginnen damit, ein neues Projekt mit einer leeren Aktivität zu erstellen, wir haben dieses Projekt betitelt "MultiThread-Beispiel", mit einer einzigen einfachen Aktivität wir erstellen die Struktur der XML-Datei das gehört zu dieser Tätigkeit.

 
Wir haben ein Textfeld, eine Schaltfläche, ein lineares Layout, das einer unbestimmten Ladeleiste entspricht, die wir später verwenden werden, und eine Listenansicht, die ein Array von URLs von Bildern enthält, die im Internet gehostet werden. In der Datei, die die Java-Klasse für unsere (einzigartige) Aktivität enthält, wird sie mit folgendem Code geschrieben:
 Paket com.omglabs.multithreaexample; android.support.v7.app.AppCompatActivity importieren; android.os.Bundle importieren; android.view.View importieren; import android.widget.AdapterView; android.widget.EditText importieren; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.ProgressBar; öffentliche Klasse MainActivity erweitert AppCompatActivity implementiert AdapterView.OnItemClickListener {private EditText editText; private ListView listView; private String []-URLs; private ProgressBar progressBar; privates 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 (diese); 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]); }} 
Bisher lässt sich die Anwendung problemlos kompilieren, in dieser Klasse deklarieren wir die Variablen:
  • Text bearbeiten
  • Listenansicht
  • URLs
  • Fortschrittsanzeige
  • FortschrittLayout

Ein Textfeld, eine Liste, eine Zeichenfolgenanordnung, ein Fortschrittsbalken und ein lineares Layout.

Im onCreate-Methode Diesen ordnen wir die jeweilige View zu, die ihnen gehört und die in der XML-Datei der Aktivität erstellt wurden, mit Ausnahme der URLs, die ihre Werte aus dem Werteordner in der String-Datei zuweist und deren Anordnung deklariert ist wie folgt:

 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 
Die leere Methode download (Ansicht anzeigen) wird mit dem Code gefüllt, der den Download durchführt und mit dem verknüpft ist Download-Button Bot über das onclick-Attribut. Endlich, das onitemclick-Methode was gehört zu Listenansicht, füllt es das Textfeld aus, wenn Sie auf eine der in der Liste enthaltenen URLs klicken. Nach der Kompilierung sieht dieser Code so aus:

Im nächsten Schritt erstellen wir die Methoden, die zum Download führen, indem wir diesen Schritten folgen:

  • Erstellen Sie ein Objekt der URL-Klasse (java.net), das die herunterzuladende URL darstellt.
  • Öffnen Sie die Verbindung mit diesem Objekt.
  • Lesen Sie die Daten (über das Web) mithilfe der Eingabestream-Klasse in einem Byte-Array.
  • Öffnen / erstellen Sie eine Ausgabe-Stream-Datei, in der die URL-Daten auf der SD-Karte gespeichert werden.
  • Schreiben Sie die Daten in diese Datei.
  • Und schließlich die Verbindung schließen.

Vorerst wird es so aussehen:

 öffentlicher boolescher Download usingThreads (String-Link) {boolesche Bestätigung = false; URL-DownloadLink = null; HttpURLConnection conne = null; InputStream inputStream = null; try {downloadLink = neue URL (Link); Verbindung = (HttpURLConnection) downloadLink.openConnection (); inputStream = conne.getInputStream (); } catch (MalformedURLException e) {e.printStackTrace (); } catch (IOException e) {e.printStackTrace (); } schließlich {if (connex! = null) {connex.disconnect (); } if (inputStream! = null) {try {inputStream.close (); } catch (IOException e) {e.printStackTrace (); }}} Bestätigung zurück; } 
Diese Methode, die wir gebaut haben, benötigt nur a Zeichenfolge das ist die URL zum Herunterladen, ist boolesch Um den Download zu bestätigen, ist downloadLink das URL-Objekt, connection ist die Verbindung, die hergestellt wird, um auf das Objekt zuzugreifen, und inputStream ist diejenige, die mit dem Lesen der Daten fortfährt, wenn wir versuchen, diese Methode auf der Schaltfläche zu verwenden herunterladenBot die Anwendung würde anhalten, weil sie auf dem nicht ausgeführt werden konnte Haupt-Bedroung.

Hier gehen wir mit der Verwendung von Threads. Es gibt zwei Möglichkeiten, dies mit einer Klasse zu tun, und zwar durch die Erweiterung dieser Klasse auf Thread oder die Implementierung der Runnable-Klasse. Diese Klasse ist kein Thread, sondern ermöglicht es Ihnen einfach, eine Methode zu erstellen, die Sie kann in einem bestimmten Moment ausgeführt werden und wenn Sie einen separaten Thread erstellen, führen Sie ihn darin aus.

In den Download-Button schreiben wir diesen Code und er sieht so aus:

 public void download (Ansicht anzeigen) {Thread mThread = new Thread (new mRunn()); mThread.start(); } 
Hier erstellen wir einen neuen Thread, der ein Runnable-Objekt benötigt, das wir in einer privaten Klasse wie folgt erstellen:
 private Klasse mRunn implementiert Runnable {@Override public void run() {download usingThreads (urls [0]); }} 
Private Klasse erstellen

NotizDenken Sie daran, dass dies alles in der Java-Klasse unserer einzigen Aktivität ist.

Mit der Zeile:

 herunterladen mitThreads (URLs [0]);
Wir rufen die Funktion auf, die wir dort erstellt haben, wo wir die Verbindung geöffnet haben, ein Element des URL-Arrays wird ihr übergeben, damit sie die Daten von dieser Adresse lesen kann. Später wird es geändert.

Wenn wir versuchen würden, diese Anwendung durch Drücken der Schaltfläche auszuführen, würde die Anwendung stoppen, da wir eine spezielle Berechtigung für den Zugriff auf das Internet benötigen, die über das Manifest unserer Anwendung angefordert wird. Hinzufügen der Zeile vor dem Etikett:

 
Um nun zu überprüfen, ob die Anwendung den Download tatsächlich durchführt, fügen wir dem Download-Methode mitThreads, es wird so aussehen:
 öffentlicher boolescher Download usingThreads (String-Link) {boolesche Bestätigung = false; URL-DownloadLink = null; HttpURLConnection conne = null; InputStream inputStream = null; FileOutputStream archOutputStream = null; Dateidatei = null; try {downloadLink = neue URL (Link); Verbindung = (HttpURLConnection) downloadLink.openConnection (); inputStream = conne.getInputStream (); file = neue Datei (Environment.getExternalStoragePublicDirectory (Environment.DIRECTORY_DOWNLOADS) + "/" + Uri.parse (Link) .getLastPathSegment ()); archOutputStream = neuer FileOutputStream (Datei); int Lesen = -1; Byte [] Puffer = neues Byte [1024]; while ((Lesen = inputStream.read (Puffer))! = -1) {archOutputStream.write (Puffer, 0, Lesen); } Bestätigung = wahr; } catch (MalformedURLException e) {e.printStackTrace (); } catch (IOException e) {e.printStackTrace (); } schließlich {if (connex! = 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 (); }}} Bestätigung zurück; } FileOutputStream archOutputStream = null; Dateidatei = null; 
Die Deklarationen dieser Objekte repräsentieren das Schreiben der gelesenen Datei und die leere Datei, in der das Lesen gespeichert wird.
 file = neue Datei (Environment.getExternalStoragePublicDirectory (Environment.DIRECTORY_DOWNLOADS) + "/" + Uri.parse (urls [0]). getLastPathSegment ()); archOutputStream = neuer FileOutputStream (Datei); int Lesen = -1; Byte [] Puffer = neues Byte [1024]; while ((Lesen = inputStream.read (Puffer))! = -1) {archOutputStream.write (Puffer, 0, Lesen); } Bestätigung = wahr; 
"File" ist das leere File-Objekt, dessen Adresse durch Zugriff auf die SD-Karte "Environment.getExternalStoragePublicDirectory (Environment.DIRECTORY_DOWNLOADS)" und Hinzufügen eines Schrägstrichs "/" und des letzten Segments der URL, das im Allgemeinen den Namen der Datei darstellt, erstellt wird download , erreichen wir dies mit der Methode getLastPathSegment().

Vor dem Testen der Anwendung fügen wir dem Manifest eine letzte Berechtigung hinzu:

 
Nachdem wir die Anwendung auf dem Emulator oder Android-Gerät ausgeführt haben, sehen wir beim Drücken der Schaltfläche, dass anscheinend nichts passiert. Wenn wir jedoch den Download-Ordner mit einem Datei-Explorer überprüfen, stellen wir fest, dass das erste Element in der Liste heruntergeladen wurde. ein Foto namens 1.jpg.webp.

Es zu tun dynamische Anwendung und implementieren Sie die URLs der Listenansicht, wir aktualisieren die Download-Methode (Ansicht anzeigen) und wir werden dies als erste Zeile hinzufügen:

 String-Link = editText.getText().ToString();
Und in der mRunn-Klasse Wir fügen dies vor der Methode run() hinzu:
 private Klasse mRunn implementiert Runnable {private String link; public mRunn (String-Link) {this.link = link; } @Override public void run() {download usingThreads (link); }}
Und in der mRunn-Klasse Wir fügen dies vor der Methode run() hinzu:

So können wir die Linkvariable aus dem Textfeld an die Methode übergeben, die den Download durchführt. Die Anwendung ist zu diesem Zeitpunkt voll funktionsfähig, obwohl es ihr an Benutzerfreundlichkeit mangelt, daher werden wir versuchen, dies mithilfe des zu Beginn angegebenen Fortschrittsbalkens zu beheben.

In die mRunn-Klasse in die run()-Methode werden wir Folgendes aufnehmen:

 MainActivity.this.runOnUiThread (new Runnable () {@Override public void run () {progressLayout.setVisibility (View.VISIBLE);}}); 
Vor dem Aufruf der downloadusandoThreads. Dadurch wird der Ladebalken angezeigt, wenn wir die Taste drücken, in der finally-Klausel des Download-Methode usingThreads.

Wir werden hinzufügen:

 this.runOnUiThread (new Runnable() {@Override public void run() {progressLayout.setVisibility (View.GONE);}}); 
Wenn der Download abgeschlossen ist, verschwindet der Balken also wieder. Dies geschieht unabhängig davon, ob der Download erfolgreich war oder nicht.
Und das war alles, eins kurze Implementierung mehrerer ThreadsDies ist etwas mühsam und bringt bei komplexeren Anwendungen einige Komplikationen mit sich.Der effektivste Weg, diese Aufgabe zu erfüllen, in unserem Fall das Herunterladen einiger Bilder, ist die Verwendung der AsyncTasks.

wave wave wave wave wave