Handler, MessageQueue, Looper, laufen sie alle auf dem UI-Thread?

Lesezeit: 9 Minuten

Handler MessageQueue Looper laufen sie alle auf dem UI Thread
rogerkk

Ich versuche, meinen Kopf um das Threading zu wickeln, und ich weiß, dass ich a verwenden kann Handler um Nachrichten/Runnables zu posten MessageQueue, die wiederum von der abgeholt wird Looper und an die zurückgeschickt Handler zum Bearbeiten.

Wenn ich an a poste Handler in meiner Tätigkeit, ist die Activity, Handler, MessageQueue und Looper läuft alles auf dem UI-Thread? Wenn nicht, könnte mir bitte jemand erklären, wie das alles zusammenhängt? 🙂

Handler MessageQueue Looper laufen sie alle auf dem UI Thread
Benutzer634618

Kurze Antwort: Sie laufen alle auf demselben Thread. Wenn von einem instanziiert Activity Lebenszyklus-Callback, sie werden alle im Haupt-UI-Thread ausgeführt.

Lange Antwort:

Ein Thread kann eine haben Looper, die eine enthält MessageQueue. Um diese Funktion nutzen zu können, müssten Sie eine erstellen Looper im aktuellen Thread durch Aufrufen (das statische) Looper.prepare(), und starten Sie dann die Schleife durch Aufrufen (das auch statisch) Looper.loop(). Diese sind statisch, weil es nur einen geben soll Looper pro Faden.

Der Aufruf an loop() kehrt normalerweise nicht zurück seit einiger Zeit, nimmt aber immer wieder Nachrichten (“Tasks”, “Commands” oder wie man sie nennen mag) aus dem heraus MessageQueue und behandelt diese individuell (z. B. durch Rückruf einer Runnable in der Nachricht enthalten). Wenn keine Nachrichten mehr in der Warteschlange vorhanden sind, blockiert der Thread, bis neue Nachrichten vorhanden sind. A stoppen Looper, du musst anrufen quit() darauf (was die Schleife wahrscheinlich nicht sofort stoppt, sondern ein privates Flag setzt, das regelmäßig von der Schleife überprüft wird und ihr signalisiert, dass sie anhalten soll).

Sie können der Warteschlange jedoch keine Nachrichten direkt hinzufügen. Stattdessen registrieren Sie sich a MessageQueue.IdleHandler auf ein warten queueIdle() Rückruf, bei dem Sie entscheiden können, ob Sie etwas tun möchten oder nicht. Alle Handler werden nacheinander aufgerufen. (So Die “Warteschlange” ist nicht wirklich eine Warteschlange, sondern eine Sammlung von Rückrufen, die regelmäßig angerufen werden.)

Hinweis zum vorherigen Absatz: Das habe ich tatsächlich vermutet. Ich konnte keine Dokumentation dazu finden, aber es würde Sinn machen.

aktualisieren: sehen ahcox“ Kommentar und seine Antwort.

Da dies viel Arbeit ist, bietet das Framework die Handler Klasse, um die Dinge zu vereinfachen. Beim Erstellen eines Handler Beispielsweise ist es (standardmäßig) an die gebunden Looper bereits an den aktuellen Thread angehängt. (Der Handler weiß was Looper zu befestigen, weil wir angerufen haben prepare() früher, die wahrscheinlich einen Verweis auf die gespeichert Looper in einem ThreadLocal.)

Mit einer Handler, du kannst einfach anrufen post() um “eine Nachricht in die Nachrichtenwarteschlange des Threads zu stellen” (sozusagen). Der Handler wird sich um alles kümmern IdleHandler Callback-Sachen und stellen Sie sicher, dass Sie gepostet haben Runnable wird ausgeführt. (Es kann auch prüfen, ob die Zeit bereits richtig ist, wenn Sie mit Verspätung gepostet haben.)

Nur um es klar zu sagen: Der einzige Weg, um tatsächlich einen Schleifenfaden zu machen tun etwas soll eine Nachricht an seine Schleife senden. Dies gilt, bis Sie quit() auf dem Looper aufrufen.


Bezüglich des Android-UI-Threads: Irgendwann (vermutlich bevor irgendwelche Aktivitäten und ähnliches erstellt werden) hat sich das Framework a eingerichtet Looper (enthält ein MessageQueue) und gestartet. Von diesem Punkt an läuft alles, was im UI-Thread passiert, durch diese Schleife. Dazu gehören Aktivitätslebenszyklusmanagement und so weiter. Alle Rückrufe, die Sie überschreiben (onCreate(), onDestroy()…) werden zumindest indirekt von dieser Schleife abgesetzt. Das sieht man zum Beispiel im Stacktrace einer Exception. (Sie können es versuchen, schreiben Sie einfach int a = 1 / 0; irgendwo in onCreate()…)


Ich hoffe das macht Sinn. Entschuldigung, dass ich vorher unklar war.

  • Aber in der Methode Looper.loop() ist eine Endlosschleife eingerichtet, in der Nachrichten aus der MessageQueue ausgewählt werden. Wie kommt es, dass dadurch nicht alles andere blockiert wird, was im UI-Thread passiert?

    – rogerkk

    4. März 11 um 13:39 Uhr


  • Es tut mir leid, dass meine Fragen vage sind, aber ich bin immer noch etwas verwirrt. Ich verstehe den Teil über das Verbinden mit einem vorhandenen Looper. Aber dieser bestehende Looper wird auch diese Endlosschleife enthalten, von der ich spreche. Ich verstehe nicht, wie dies in jedem Thread ausgeführt werden kann, ohne alles andere zu blockieren, was in demselben Thread passiert.

    – rogerkk

    7. März 11 um 8:10 Uhr


  • Super, vielen Dank für die Klärung! Der letzte Abschnitt über den UI-Thread bestätigte meinen schleichenden Verdacht.

    – rogerkk

    10. März 11 um 11:02 Uhr

  • Übrigens. Anstatt eine Ausnahme zu verursachen, können Sie einfach eine auslösen. throw new RuntimeException(“FooBar”); Sie können dies jedoch nicht mitten in einem Codeblock tun, da sich der Compiler dann beschweren würde, dass der Rest des Blocks niemals ausführbar ist. Sie können es jedoch austricksen, indem Sie es so machen: if (true) {throw new RuntimeException(“Got ya!”);} Ich verwende das gerne selbst, da Ihre Absichten klarer sind als mit etwas wie einer absichtlichen Arithmetik Error.

    –Timo

    25. Oktober 11 um 16:39 Uhr


  • Das ist eine sehr hilfreiche Antwort. Nur um Ihren Punkt der Unsicherheit zu verdeutlichen, es gibt eine echte Warteschlange in der MessageQueue. Message enthält einen Verweis auf a Message die MessageQueue verwendet, um eine aufdringliche Linked-List-Warteschlange zu bilden. Sie können sehen, wie Nachrichten von der Vorderseite aufgenommen werden MessageQueue.pullNextLocked() (nicht öffentliche Methode). Nur wenn diese Nachrichtenwarteschlange leer ist, werden die Idle-Handler aufgerufen. Der letzte Ausweg besteht darin, dass der Thread auf die nächste Verzögerung wartet Message‘s Frist, oder für eine neue Message hochdrehen.

    – ahcox

    9. März 12 um 17:27 Uhr

1642575069 707 Handler MessageQueue Looper laufen sie alle auf dem UI Thread
ahcox

Folgen Sie dem Teil der Frage “Wie alles zusammenkommt”. Wie user634618 schrieb, übernimmt der Looper einen Thread, den Haupt-UI-Thread im Fall des Mains einer Anwendung Looper.

  • Looper.loop() zieht Nachrichten aus seiner Nachrichtenwarteschlange. Jede Nachricht hat einen Verweis auf einen zugeordneten Handler, an den sie zurückgegeben werden soll (das Zielmitglied).
  • Innen Looper.loop() für jede Nachricht aus der Warteschlange:
    • loop() Anrufe public void Handler.dispatchMessage(Message msg) Verwenden des in der Nachricht gespeicherten Handlers als Zielelement.
    • Wenn die Nachricht ein Runnable-Callback-Member hat, wird dieses ausgeführt.
    • Andernfalls, wenn der Handler über einen gemeinsam genutzten Callback-Satz verfügt, wird dieser ausgeführt.
    • Sonst Handlers handleMessage() wird mit der Message als Argument aufgerufen. (Beachten Sie, wenn Sie Handler wie AsyncTask unterklassen, könnten Sie es überschreiben handleMessage() Wie es funktioniert.)

Zu Ihrer Frage, ob sich alle kollaborierenden Objekte im selben UI-Thread befinden, a Handler muss im selben Thread wie die erstellt werden Looper an die es Nachrichten senden wird. Sein Konstruktor wird den Strom nachschlagen Looper und speichern Sie es als Mitglied, binden Sie die Handler dazu Looper. Darauf wird auch verwiesen Looperdie Nachrichtenwarteschlange von direkt in ihrem eigenen Mitglied. Der Handler kann verwendet werden, um Arbeit an die zu senden Looper von jedem Thread, aber diese Identität der Nachrichtenwarteschlangen leitet die zu erledigende Arbeit an die weiter Looper‘s Faden.

Wenn wir Code auf einem anderen Thread ausführen und ein Runnable senden möchten, das auf dem UI-Thread ausgeführt werden soll, könnten wir es so machen:

// h is a Handler that we constructed on the UI thread.
public void run_on_ui_thread(final Handler h, final Runnable r)
{
   // Associate a Message with our Handler and set the Message's
   // callback member to our Runnable:
   final Message message = Message.obtain(h, r);

   // The target is the Handler, so this asks our Handler to put
   // the Message in its message queue, which is the exact same
   // message queue associated with the Looper on the thread on
   // which the Handler was created:
   message.sendToTarget();
}

  • Hervorragender Einblick! Danke.

    – Yogesh Umesh Vaity

    30. Oktober 17 um 6:15 Uhr

Ich versuche, diese Schnittstellen selbst zu implementieren, um das Konzept zu verstehen. Verwenden Sie der Einfachheit halber einfach die Schnittstelle nach Bedarf. Hier ist mein Testcode:

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

public class TestLooper {

    public static void main(String[] args) {
        UIThread thread = new UIThread();
        thread.start();

        Handler mHandler = new Handler(thread.looper);
        new WorkThread(mHandler, "out thread").run();
    }
}

class Looper {
    private BlockingQueue<Message> message_list = new LinkedBlockingQueue<Message>();

    public void loop() {

        try {
            while (!Thread.interrupted()) {
                Message m = message_list.take();
                m.exeute();
            }
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

    }

    public void insertMessage(Message msg) {
        message_list.add(msg);
    }

}

class Message {
    String data;
    Handler handler;

    public Message(Handler handler) {
        this.handler = handler;
    }

    public void setData(String data) {
        this.data = data;
    }

    public void exeute() {
        handler.handleMessage(this);
    }
}

class Handler {

    Looper looper;

    public Handler(Looper looper) {
        this.looper = looper;
    }

    public void dispatchMessage(Message msg) {
        System.out.println("Handler dispatchMessage" + Thread.currentThread());
        looper.insertMessage(msg);
    }

    public Message obtainMessage() {
        return new Message(this);
    }

    public void handleMessage(Message m) {
        System.out.println("handleMessage:" + m.data + Thread.currentThread());
    }
}

class WorkThread extends Thread {
    Handler handler;
    String tag;

    public WorkThread(Handler handler, String tag) {
        this.handler = handler;
        this.tag = tag;
    }

    public void run() {
        System.out.println("WorkThread run" + Thread.currentThread());
        Message m = handler.obtainMessage();
        m.setData("message " + tag);
        handler.dispatchMessage(m);
    }
}

class UIThread extends Thread {

    public Looper looper = new Looper();

    public void run() {

            //create handler in ui thread
        Handler mHandler = new Handler(looper);

        new WorkThread(mHandler, "inter thread").run();
        System.out.println("thead run" + Thread.currentThread());
        looper.loop();
    }

}

Wenn ich in meiner Aktivität an einen Handler poste, werden Aktivität, Handler, MessageQueue und Looper alle im UI-Thread ausgeführt? Wenn nicht, könnte mir bitte jemand erklären, wie das alles zusammenhängt? 🙂

Es hängt davon ab, wie Sie erstellen Handler

Fall 1:

Handler()

Der Standardkonstruktor ordnet diesen Handler dem zu Schleifer für den aktuellen Thread.

Wenn Sie erstellen Handler so im UI-Thread, Handler wird assoziiert mit Looper des UI-Threads. MessageQueue ist auch damit verbunden Looper mit UI-Thread.

Fall 2:

Handler (Looper looper)

Verwenden Sie den mitgelieferten Looper anstelle des Standard-Loopers.

Wenn ich eine erstelle HandlerThread und übergeben Sie den Looper von HandlerThread an Handler, Handler und Looper sind HandlerThread und nicht UI-Thread zugeordnet. Handler, MessageQueue und Looper verbunden sind HandlerThread.

Anwendungsfall: Sie möchten eine Netzwerk-ODER-IO-Operation ausführen. Sie können es nicht im UI-Thread ausführen und daher HandlerThread ist praktisch für Sie.

 HandlerThread handlerThread = new HandlerThread("NetworkOperation");
 handlerThread.start();
 Handler requestHandler = new Handler(handlerThread.getLooper());

Wenn Sie Daten von HandlerThread an den UI-Thread zurückgeben möchten, können Sie einen weiteren Handler (z. B. responseHandler ) mit erstellen Looper aus UI-Thread und Anruf sendMessage. Der UI-Thread responseHandler überschreiben soll handleMessage

Weitere Informationen finden Sie in diesen Beiträgen.

Was ist der Zweck von Looper und wie wird es verwendet? (Für Konzepte)

Android: Toast in einem Thread (z. B. Code durch Verknüpfung all dieser Konzepte)

.

544340cookie-checkHandler, MessageQueue, Looper, laufen sie alle auf dem UI-Thread?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy