Grundlegendes zu Parallelität unter Android mit HaMeR

1. Einleitung

Jeder, der Android-Entwicklung versucht, entdeckt die Bedeutung der Parallelität. Die einzige Möglichkeit, eine responsive App zu erstellen, besteht darin, den UI-Thread so frei wie möglich zu lassen und die harte Arbeit asynchron von Hintergrund-Threads ausführen zu lassen.

Aufgrund des Designs von Android können Threads nur mit der java.lang.thread und java.util.concurrent Pakete könnten wirklich schwierig sein. Die Verwendung der Low-Level-Threading-Pakete mit Android bedeutet, dass Sie sich um viele knifflige Synchronisationen sorgen müssen, um Race-Bedingungen zu vermeiden. Glücklicherweise haben die Leute bei Google die harte Arbeit geleistet und einige großartige Tools entwickelt, um unsere Arbeit zu erleichtern: AsyncTaskIntentServiceLaderAsyncQueryHandler und CursorLoader sind alle nützlich, ebenso wie die HaMeR-Klassen Handler, Botschaft, und Lauffähig. Es gibt viele großartige Möglichkeiten, aus denen Sie wählen können, jede mit ihren Vor- und Nachteilen.

Über das ist viel gesagt worden AsyncTask Objekt, und viele Leute verwenden es als Silberkugel Lösung für Parallelität auf Android. Es ist äußerst nützlich für kurze Operationen, einfach zu implementieren und wahrscheinlich der beliebteste Ansatz für Parallelität auf Android. Wenn Sie mehr darüber erfahren möchten AsyncTask, Schauen Sie sich die folgenden Envato Tuts + Beiträge an.

  • Android From Scratch: Hintergrundoperationen

    Das Einfädeln in Programmiersprachen oder Plattformen ist schwierig und Android ist keine Ausnahme. In diesem Tutorial lernen Sie einige Werkzeuge kennen ...
    Paul Trebilcox-Ruiz
    Android SDK
  • Informationen zu AsyncTask-Werten in 60 Sekunden

    Unter Android wird die AsyncTask-Klasse normalerweise verwendet, um Vorgänge an einem Hintergrundthread auszuführen. In diesem Video zeige ich Ihnen, wie ein AsyncTask funktioniert und wie Sie…
    Paul Trebilcox-Ruiz
    Android

jedoch, AsyncTask sollte nicht das einzige Werkzeug an Ihrem Werkzeuggürtel sein.

Bei lang andauernden Vorgängen, bei komplexen Parallelitätsproblemen oder zur Erzielung einer höheren Effizienz in bestimmten Situationen sollten Sie eine andere Lösung wählen. Wenn Sie mehr Flexibilität oder Effizienz benötigen als AsyncTask bietet, können Sie den HaMeR (Handler, Botschaft & Lauffähig) Rahmen.In diesem Lernprogramm werden wir das HaMeR-Framework, eines der leistungsfähigsten Parallelitätsmodelle für Android, kennenlernen und erfahren, wann und wie es verwendet wird. In einem anschließenden Tutorial zeige ich Ihnen, wie Sie eine Anwendung programmieren, um einige Möglichkeiten von HaMeR auszuprobieren.

Im folgenden Abschnitt wird die Bedeutung von Hintergrundthreads für das Android-System erläutert. Wenn Sie mit diesem Konzept vertraut sind, überspringen Sie es und gehen Sie direkt zur Diskussion des HaMeR-Rahmens in Abschnitt 3.

2. Reaktionsfähigkeit durch Hintergrundthemen

Wenn eine Android-Anwendung gestartet wird, ist der erste Thread, der durch den Prozess erzeugt wird, der Haupt-Thread, der auch als UI-Thread bezeichnet wird und für die Handhabung der gesamten Benutzeroberflächenlogik verantwortlich ist. Dies ist der wichtigste Thread einer Anwendung. Es ist dafür verantwortlich, alle Benutzerinteraktionen zu handhaben und die beweglichen Teile der Anwendung zusammenzubinden. Android nimmt dies sehr ernst und wenn Ihr UI-Thread länger als ein paar Sekunden an einer Aufgabe arbeitet, stürzt die App ab.

[Der UI-Thread] ist sehr wichtig, da er für das Versenden von Ereignissen an die entsprechenden Benutzeroberflächen-Widgets zuständig ist, einschließlich Zeichnungsereignisse. Es ist auch der Thread, in dem Ihre Anwendung mit Komponenten aus dem Android-UI-Toolkit (Komponenten aus dem android.widget und android.view Pakete). Daher wird der Haupt-Thread manchmal auch als UI-Thread bezeichnet. - Prozesse und Threads, Android Developer Guide

Das Problem ist, dass fast der gesamte Code in einer Android-Anwendung standardmäßig auf dem UI-Thread ausgeführt wird. Da die Aufgaben in einem Thread sequenziell ausgeführt werden, bedeutet dies, dass Ihre Benutzeroberfläche "einfrieren" kann und nicht mehr reagiert, während andere Aufgaben bearbeitet werden.

Langfristige Aufgaben, die auf der Benutzeroberfläche aufgerufen werden, sind für Ihre App wahrscheinlich fatal und es erscheint ein ANR-Dialogfeld (Application Not Responding). Selbst kleine Aufgaben können die Benutzererfahrung beeinträchtigen. Daher ist es die richtige Vorgehensweise, so viel Arbeit wie möglich aus dem Benutzeroberflächenthread mit Hintergrundthreads zu entfernen. Wie bereits erwähnt, gibt es viele Möglichkeiten, dieses Problem zu lösen, und wir werden das HaMeR-Framework untersuchen, eine der Kernlösungen von Android, um dieser Situation zu begegnen.

3. Das HaMeR-Framework

Das HaMeR-Framework ermöglicht es Hintergrund-Threads, Nachrichten zu senden oder Runnables an den UI-Thread und an andere Threads zu senden MessageQueue über Handler. HaMeR bezieht sich auf Handler, Botschaft, & Lauffähig. Es gibt auch einige andere wichtige Klassen, die mit dem HaMeR zusammenarbeiten: Looper und MessageQueue. Zusammen sind diese Objekte dafür verantwortlich, das Thread-Management auf Android zu vereinfachen, für die Synchronisierung zu sorgen und einfache Methoden für Hintergrund-Threads bereitzustellen, um mit der Benutzeroberfläche und mit anderen Threads zu kommunizieren.

So passen die Klassen im HaMeR-Framework zusammen.

  • Looper Führt eine Meldungsschleife für einen Thread aus, der die MessageQueue.
  • MessageQueue enthält eine Liste von Nachrichten, die vom gesendet werden sollen Looper.
  • Handler ermöglicht das Senden und Verarbeiten von Botschaft und Lauffähig zum MessageQueue. Es kann zum Senden und Verarbeiten von Nachrichten zwischen Threads verwendet werden.
  • Botschaft enthält eine Beschreibung und Daten, die an einen Handler gesendet werden können.
  • Lauffähig repräsentiert eine auszuführende Aufgabe.

Mit dem HaMeR-Framework können Threads Nachrichten senden oder ausführbare Objekte entweder an sich selbst oder an den UI-Thread senden. HaMeR fördert auch Hintergrund-Thread-Interaktionen über Handler.

3.1. Die Handler-Klasse

Handler ist das HaMeR-Arbeitstier. Es ist für das Senden verantwortlich Botschaft (Datennachricht) und Post Lauffähig (Aufgabennachricht) wendet den an MessageQueue verbunden mit einem Faden. Nachdem die Aufgaben an die Warteschlange übergeben wurden, erhält der Handler die Objekte vom Looper und verarbeitet die Meldungen zu gegebener Zeit mit der Handler mit ihr verbundenen.

EIN Handler kann zum Senden oder Posten verwendet werden Botschaft und Lauffähig Objekte zwischen Threads, sofern diese Threads den gleichen Prozess verwenden. Andernfalls muss ein Inter Process Communication (IPC) erstellt werden, eine Methodik, die den Rahmen dieses Tutorials sprengt.

Einen Handler instanziieren

EIN Handler muss immer mit einem verbunden sein Looper, und diese Verbindung muss während der Instantiierung hergestellt werden. Wenn Sie keine angeben Looper zum Handler, es wird an den Strom gebunden Faden's Looper.

// Handler verwendet den Looper-Handler des aktuellen Threads. Handler = new Handler (); // Handler verwendet den Looper-Handler-Handler = new Handler (Looper);

Denken Sie daran, dass a Handler ist immer mit einem verbunden Looper, und diese Verbindung ist dauerhaft und kann nicht einmal geändert werden. A Looper's Thread kann Assoziationen mit mehreren haben Handlers. Es ist auch wichtig zu beachten, dass a Looper muss vor seiner Verknüpfung mit einem aktiv sein Handler.

3.2. Looper und MessageQueue

Die kooperative Arbeit zwischen Looper und MessageQueue In einem Java-Thread wird eine Schleife von Tasks erstellt, die nacheinander verarbeitet werden. Eine solche Schleife hält den Thread am Leben, während er auf weitere Aufgaben wartet. Ein Thread kann nur einen haben Looper und ein MessageQueue mit ihr verbundenen; Es können jedoch mehrere Handler für jeden Thread vorhanden sein. Die Handler sind für die Verarbeitung der Aufgaben in der Warteschlange verantwortlich, und jede Aufgabe weiß, welcher Handler für die Verarbeitung zuständig ist.

3.3. Einen Thread für den HaMeR vorbereiten

Die Benutzeroberfläche oder der Haupt-Thread ist die einzige Art von Thread, für die standardmäßig bereits ein Thread vorhanden ist Handler, ein Looper, und ein MessageQueue. Andere Threads müssen mit diesen Objekten vorbereitet werden, bevor sie mit dem HaMeR-Framework arbeiten können. Zuerst müssen wir eine erstellen Looper das beinhaltet schon ein MessageQueue und befestigen Sie es an den Faden. Sie können dies mit einer Unterklasse von tun Faden, wie folgt.

// Einen Thread für die HaMeR-Klasse vorbereiten LooperThread erweitert den Thread public Handler mHandler; public void run () // Hinzufügen und Vorbereiten des Looper Looper.prepare (); // Die Handler-Instanz wird mit dem Looper des Threads verknüpft. mHandler = new Handler () public void handleMessage (Message msg) // hier eingehende Nachrichten verarbeiten; // Starten der Nachrichtenwarteschlangenschleife mithilfe des Looper Looper.loop (); 

Es ist jedoch einfacher, eine sogenannte Helper-Klasse zu verwenden HandlerThread, die hat eine Looper und ein MessageQueue in ein Java eingebaut Faden und ist bereit, einen Handler zu erhalten.

// Die HandlerThread-Klasse enthält eine funktionierende öffentliche Looper-Klasse. HamerThread erweitert HandlerThread // Sie müssen nur den privaten Handler-Handler-Handler hinzufügen. public HamerThread (String-Name) Super (Name); 

4. Runnables buchen

Das Lauffähig ist eine Java-Schnittstelle, die viele Anwendungen hat. Es kann als eine einzige Aufgabe verstanden werden, die auf einem Computer ausgeführt werden muss Faden. Es gibt eine einzige Methode, die implementiert werden muss, Runnable.run (), um die Aufgabe auszuführen.

// Deklarieren einer ausführbaren ausführbaren Datei r = new Runnable () @Override public void run () // die Aufgabe geht hier hin;

Es gibt mehrere Möglichkeiten, ein zu posten Lauffähig auf einen Handler.

  • Handler.post (lauffähig r): Ergänzen Sie die Lauffähig zum MessageQueue.
  • Handler.postAtFrontOfQueue (lauffähig r): Ergänzen Sie die Lauffähig an der Vorderseite des MessageQueue.
  • Handler.postAtTime (lauffähig r, long timeMillis): Ergänzen Sie die Lauffähig auf der MessageQueue zu einem bestimmten Zeitpunkt angerufen werden.
  • Handler.postDelayed (lauffähig r, lange Verzögerung): Ergänzen Sie die Lauffähig an die Warteschlange, die nach Ablauf einer bestimmten Zeit aufgerufen werden soll.
// Posten eines Runable auf einem Handler Handler Handler = new Handler (); handler.post (new Runnable () @Override public void run () // Aufgabe geht hier hin));

Es ist auch möglich, den Standard-UI-Handler zum Buchen von a zu verwenden Lauffähig Berufung Activity.runOnUiThread ().

// Posten mit dem UI-Handler lauffähig Activity.runOnUiThread (new Runnable () @Override public void run () // auszuführende Aufgabe);

Es ist wichtig, einige Dinge zu beachten Lauffähigs. Im Gegensatz zu einem Botschaft, ein Lauffähig Kann nicht recycelt werden - sobald die Arbeit erledigt ist, ist sie tot. Da es Teil eines Standard-Java-Pakets ist, a Lauffähig hängt nicht von ab Handler und kann auf einem Standard aufgerufen werden Faden Verwendung der Runnable.run () Methode. Dieser Ansatz hat jedoch nichts mit dem HaMeR-Framework zu tun und teilt seine Vorteile nicht.

5. Nachrichten senden

Das Botschaft object definiert eine Nachricht, die eine Beschreibung und einige beliebige Daten enthält, die über gesendet und verarbeitet werden können Handler. Das Botschaft ist mit einem gekennzeichnet int definiert am Message.what (). Das Botschaft kann zwei andere halten int Argumente und ein Objekt verschiedene Arten von Daten zu speichern.

  • Nachricht.was: int Identifizierung der Botschaft
  • Nachricht.arg1: int beliebiges Argument
  • Nachricht.arg2: int beliebiges Argument
  • Message.obj: Objekt verschiedene Arten von Daten zu speichern  

Wenn Sie eine Nachricht senden müssen, anstatt sie von Grund auf neu zu erstellen, empfiehlt es sich, eine recycelte Nachricht direkt aus dem globalen Pool mit der Message.obtain () oder Handler.obtainMessage () Befehle. Es gibt verschiedene Versionen dieser Methoden, mit denen Sie eine Botschaft je nach Bedarf.

Eine häufige Verwendung von Handler.obtainMessage () ist, wenn Sie eine Nachricht an einen Hintergrund-Thread senden müssen. Sie werden das verwenden Handler mit diesem Thread verbunden Looper a erhalten Botschaft und senden Sie es an den Hintergrund-Thread, wie im Beispiel unten.

int was = 0; String hallo = "Hallo!"; // Abrufen einer mit dem Hintergrund verknüpften Nachricht Thread-Nachricht msg = handlerBGThread.obtainMessage (what, hallo); // Senden der Nachricht an den Hintergrund Thread handlerBGThread.sendMessage (msg); 

Es gibt viele coole Methoden auf der Botschaft Klasse, und ich rate Ihnen, sich die Dokumentation genauer anzusehen.

5.1. Nachricht senden() Optionen

Ähnlich wie wir posten können Lauffähigs gibt es mehrere Optionen zum Senden Botschafts:

  • Handler.sendMessage (Message msg): füge hinzu ein Botschaft zum MessageQueue.
  • Handler.sendMessageAtFrontOfQueue (Message msg): füge hinzu ein Botschaft zur Vorderseite des MessageQueue.
  • Handler.sendMessageAtTime (Nachricht msg, long timeInMillis): füge hinzu ein Botschaft zu einer bestimmten Zeit in die Warteschlange.
  • Handler.sendMessageDelayed (Nachricht msg, long timeInMillis): füge hinzu ein Botschaft in die Warteschlange ein, nachdem eine bestimmte Zeit vergangen ist.

5.2. Nachrichten mit Handler behandeln

Das Botschaft Objekte, die von Looper werden vom Handler mit der Methode verarbeitet Handler.handleMessage. Alles, was Sie tun müssen, ist die Erweiterung Handler Klasse und überschreiben Sie diese Methode, um die Nachrichten zu verarbeiten.

public class MessageHandler erweitert Handler @Override public void handleMessage (Nachricht msg) switch (msg.what) // Handle 'Hello' msg case 0: String hello = (String) msg.obj; System.out.println (Hallo); brechen; 

6. Schlussfolgerung

Das HaMeR-Framework kann dazu beitragen, den gleichzeitigen Code Ihrer Android-Anwendung zu verbessern. Es mag auf den ersten Blick verwirrend erscheinen, wenn man es mit der Einfachheit eines Systems vergleicht AsyncTask, Die Offenheit von HaMeR kann jedoch von Vorteil sein, wenn es richtig eingesetzt wird. 

Merken:

  • Handlerpost () Methoden werden verwendet, wenn Absender wissen, welche Vorgänge auszuführen sind.
  • Handler.sendMessage() Methoden werden verwendet, wenn der Empfänger weiß, welche Operation ausgeführt werden soll.

Um mehr über Threading in Android zu erfahren, können Sie sich für das Buch Effizientes Android-Threading: Asynchrone Verarbeitungstechniken für Android-Anwendungen von Anders Goransson interessieren.

6.1. Was kommt als nächstes?

Im nächsten Lernprogramm werden wir das HaMeR-Framework mit einem praktischen Ansatz erkunden, indem wir eine Anwendung entwickeln, die verschiedene Möglichkeiten der Verwendung dieses Android-Parallelitäts-Frameworks demonstriert. Wir erstellen diese App von Grund auf und probieren verschiedene Möglichkeiten aus, wie etwa die Kommunikation zwischen ihnen Fäden, mit dem UI-Thread sprechen sowie Nachrichten senden und posten Lauffähigs mit Verzögerungen. 

Bis bald!