Ich habe den offiziellen Android - Dokumentation / Guide geprüft Looper
, Handler
und MessageQueue
. Aber ich konnte es nicht verstehen. Ich bin neu in Android und war sehr verwirrt mit diesen Konzepten.
quelle
Ich habe den offiziellen Android - Dokumentation / Guide geprüft Looper
, Handler
und MessageQueue
. Aber ich konnte es nicht verstehen. Ich bin neu in Android und war sehr verwirrt mit diesen Konzepten.
A Looper
ist eine Nachrichtenbehandlungsschleife: Sie liest und verarbeitet Elemente aus a MessageQueue
. Die Looper
Klasse wird normalerweise in Verbindung mit a HandlerThread
(einer Unterklasse von Thread
) verwendet.
A Handler
ist eine Dienstprogrammklasse, die die Interaktion mit a erleichtert - Looper
hauptsächlich durch das Posten von Nachrichten und Runnable
Objekten an den Thread MessageQueue
. Wenn a Handler
erstellt wird, ist es an eine bestimmte Looper
(und zugehörige Thread- und Nachrichtenwarteschlange) gebunden .
In der typischen Verwendung erstellen und starten Sie ein HandlerThread
und erstellen dann ein Handler
Objekt (oder Objekte), über das andere Threads mit der HandlerThread
Instanz interagieren können . Das Handler
muss während der Ausführung auf dem erstellt werden HandlerThread
, obwohl es nach dem Erstellen keine Einschränkung dafür gibt, welche Threads die Handler
Planungsmethoden des Threads verwenden können (post(Runnable)
usw.) des .
Der Hauptthread (auch als UI-Thread bezeichnet) in einer Android-Anwendung wird als Handler-Thread eingerichtet, bevor Ihre Anwendungsinstanz erstellt wird.
Abgesehen von den Klassen docs, dann ist es eine schöne Diskussion über all dies hier .
PS Alle oben genannten Klassen sind im Paket enthalten android.os
.
MessageQueue
Zustand , dass einMessageQueue
ein „ Low-Level - Klasse , um die Liste der Nachrichten halten durch ein abgefertigt werdenLooper
. “Es ist allgemein bekannt, dass es illegal ist, UI-Komponenten direkt von anderen Threads als dem Haupt-Thread in Android zu aktualisieren . In diesem Android-Dokument ( Umgang mit teuren Vorgängen im UI-Thread ) werden die folgenden Schritte vorgeschlagen, wenn ein separater Thread gestartet werden muss, um teure Arbeiten auszuführen und die UI nach Abschluss zu aktualisieren. Die Idee ist, ein Handler- Objekt zu erstellen, das dem Hauptthread zugeordnet ist , und zu gegebener Zeit eine ausführbare Datei an dieses zu senden . Dies
Runnable
wird im Hauptthread aufgerufen . Dieser Mechanismus wird mit Looper- und Handler- Klassen implementiert .Die
Looper
Klasse unterhält eine Message , die eine Liste enthält Nachrichten . Ein wichtiges Merkmal des Looper ist , dass es assoziiert mit dem Gewinde in dem dieLooper
erstellt wird . Diese Zuordnung bleibt für immer erhalten und kann weder gebrochen noch geändert werden. Beachten Sie auch, dass ein Thread nicht mehr als einem zugeordnet werden kannLooper
. Um diese Zuordnung zu gewährleisten,Looper
wird sie im threadlokalen Speicher gespeichert und kann nicht direkt über ihren Konstruktor erstellt werden. Die einzige Möglichkeit, es zu erstellen, besteht darin, die statische Methode prepare aufzurufenLooper
. Die Vorbereitungsmethode untersucht zuerst ThreadLocaldes aktuellen Threads, um sicherzustellen, dass dem Thread noch kein Looper zugeordnet ist. Nach der Prüfung wird eine neueLooper
erstellt und gespeichertThreadLocal
. NachdemLooper
wir das vorbereitet haben , können wir die Schleifenmethode aufrufen , um nach neuen Nachrichten zu suchen und zu habenHandler
zu suchen und diese zu verarbeiten.Wie der Name schon sagt, ist die
Handler
Klasse hauptsächlich für die Verarbeitung (Hinzufügen, Entfernen, Versenden) von Nachrichten aktueller Threads verantwortlichMessageQueue
. EineHandler
Instanz ist auch an einen Thread gebunden. Die Bindung zwischen Handler und Thread erfolgt überLooper
undMessageQueue
. AHandler
ist immer gebunden einLooper
, und anschließend an den gebundenen zugehörigen Gewinde mit derLooper
. Im Gegensatz dazuLooper
können mehrere Handler-Instanzen an denselben Thread gebunden werden. Immer wenn wir post oder eine andere Methode auf dem aufrufenHandler
, wird dem zugehörigen eine neue Nachricht hinzugefügtMessageQueue
. Das Zielfeld der Nachricht wird auf die aktuelleHandler
Instanz gesetzt. Wenn derLooper
empfangenen Nachricht dispatchMessage aufruft im Zielfeld der Nachricht aufgerufen, sodass die Nachricht an die zu behandelnde Handler-Instanz zurückgeleitet wird, jedoch im richtigen Thread. Die Beziehungen zwischenLooper
,Handler
undMessageQueue
ist unten gezeigt:quelle
Beginnen wir mit dem Looper. Sie können die Beziehung zwischen Looper, Handler und MessageQueue leichter verstehen, wenn Sie verstehen, was Looper ist. Außerdem können Sie besser verstehen, was Looper im Kontext des GUI-Frameworks ist. Looper ist dafür gemacht, zwei Dinge zu tun.
1) Looper wandelt einen normalen Thread , der bei der
run()
Rückkehr seiner Methode beendet wird , in einen Thread um , der kontinuierlich ausgeführt wird, bis die Android-App ausgeführt wird , was im GUI-Framework erforderlich ist (technisch gesehen wird er immer noch beendet, wennrun()
ist. unten).2) Looper stellt eine Warteschlange bereit bereit, in der zu erledigende Jobs in die werden, was auch im GUI-Framework erforderlich ist.
Wie Sie vielleicht wissen, erstellt das System beim Start einer Anwendung einen Ausführungsthread für die Anwendung, der als "Hauptthread" bezeichnet wird, und Android-Anwendungen werden normalerweise vollständig auf einem einzelnen Thread ausgeführt, dem "Hauptthread". Aber der Haupt-Thread ist kein geheimer, spezieller Thread . Es ist nur ein normaler Thread, den Sie auch mit
new Thread()
Code erstellen können. Dies bedeutet, dass er beendet wird, wenn er ausgeführt wirdrun()
Methode zurückgegeben wird! Denken Sie an das folgende Beispiel.Wenden wir dieses einfache Prinzip nun auf die Android-App an. Was würde passieren, wenn eine Android-App auf einem normalen Thread ausgeführt wird? Ein Thread namens "main" oder "UI" oder was auch immer startet die Anwendung und zeichnet alle UI. Daher wird den Benutzern der erste Bildschirm angezeigt. So was nun? Der Haupt-Thread endet? Nein, das sollte es nicht. Es sollte warten, bis Benutzer etwas tun, oder? Aber wie können wir dieses Verhalten erreichen? Nun, wir können es mit
Object.wait()
oder versuchenThread.sleep()
. Beispielsweise beendet der Hauptthread seinen anfänglichen Job zum Anzeigen des ersten Bildschirms und schläft. Es erwacht, was bedeutet, unterbrochen, wenn ein neuer Job abgerufen wird. So weit so gut, aber in diesem Moment brauchen wir eine warteschlangenartige Datenstruktur, um mehrere Jobs zu halten. Denken Sie an einen Fall, in dem ein Benutzer den Bildschirm seriell berührt und eine Aufgabe länger dauert. Wir brauchen also eine Datenstruktur, um Jobs zu speichern, die First-In-First-Out ausgeführt werden. Sie können sich auch vorstellen, dass die Implementierung eines Threads, der immer ausgeführt wird und einen Job bei Ankunft verarbeitet, mithilfe von Interrupt nicht einfach ist und zu komplexem und häufig nicht wartbarem Code führt. Wir möchten lieber einen neuen Mechanismus für diesen Zweck schaffen, und darum geht es bei Looper . Das offizielle Dokument der Looper-Klassesagt: "Threads sind standardmäßig keine Nachrichtenschleife zugeordnet", und Looper ist eine Klasse, "die zum Ausführen einer Nachrichtenschleife für einen Thread verwendet wird". Jetzt können Sie verstehen, was es bedeutet.Wechseln wir zu Handler und MessageQueue. Erstens ist MessageQueue die Warteschlange, die ich oben erwähnt habe. Es befindet sich in einem Looper und das war's. Sie können dies mit dem Quellcode der Looper-Klasse überprüfen . Die Looper-Klasse hat eine Mitgliedsvariable von MessageQueue.
Was ist dann Handler? Wenn es eine Warteschlange gibt, sollte es eine Methode geben, mit der wir eine neue Aufgabe in die Warteschlange einreihen können, oder? Das macht Handler. Wir können eine neue Aufgabe mit verschiedenen
post(Runnable r)
Methoden in eine Warteschlange (MessageQueue) einreihen . Das ist es. Hier geht es um Looper, Handler und MessageQueue.Mein letztes Wort ist, also ist Looper im Grunde eine Klasse, die gemacht wird, um ein Problem anzugehen, das im GUI-Framework auftritt. Diese Art von Bedürfnissen kann aber auch in anderen Situationen auftreten. Tatsächlich ist es ein ziemlich berühmtes Muster für Anwendungen mit mehreren Threads, und Sie können mehr darüber in "Concurrent Programming in Java" von Doug Lea erfahren (insbesondere Kapitel 4.1.4 "Worker Threads" wäre hilfreich). Sie können sich auch vorstellen, dass diese Art von Mechanismus im Android-Framework nicht eindeutig ist, aber alle GUI-Frameworks benötigen möglicherweise etwas Ähnliches. Sie finden fast den gleichen Mechanismus im Java Swing Framework.
quelle
MessageQueue
: Es handelt sich um eine untergeordnete Klasse, die die Liste der von a zu versendenden Nachrichten enthältLooper
. Nachrichten werden nicht direkt zu a hinzugefügtMessageQueue
, sondern überHandler
Objekte, die mitLooper
. [ 3 ]Looper
: Es durchläuft eine Schleife,MessageQueue
die die zu versendenden Nachrichten enthält. Die eigentliche Aufgabe der Verwaltung der WarteschlangeHandler
übernimmt derjenige, der für die Verarbeitung (Hinzufügen, Entfernen, Versenden) von Nachrichten in der Nachrichtenwarteschlange verantwortlich ist. [ 2 ]Handler
: Es ermöglicht Ihnen , und Prozess zu sendenMessage
undRunnable
Objekte mit einem Thread zugeordnetMessageQueue
. Jede Handler-Instanz ist einem einzelnen Thread und der Nachrichtenwarteschlange dieses Threads zugeordnet. [ 4 ]Wenn Sie eine neue erstellen
Handler
, wird sie an die Thread- / Nachrichtenwarteschlange des Threads gebunden, der sie erstellt. Ab diesem Zeitpunkt werden Nachrichten und ausführbare Dateien an diese Nachrichtenwarteschlange gesendet und ausgeführt, sobald sie aus der Nachrichtenwarteschlange kommen .Bitte gehen Sie zum besseren Verständnis das folgende Bild [ 2 ] durch.
quelle
Erweiterung der Antwort um @K_Anas um ein Beispiel, wie angegeben
Zum Beispiel, wenn Sie versuchen, die Benutzeroberfläche mithilfe von Thread zu aktualisieren.
Ihre App stürzt mit Ausnahme ab.
Mit anderen Worten, Sie müssen verwenden,
Handler
was den Verweis auf die AufgabeMainLooper
ieMain Thread
oderUI Thread
und als Aufgabe weitergibtRunnable
.quelle