41 votes

Les gestionnaires, MessageQueue, Looper, s'exécutent-ils tous sur le thread d'interface utilisateur?

Je suis d'essayer d'envelopper ma tête autour de thread, et je sais que je peut utiliser un Handler de poster des messages/runnables à l' MessageQueue, qui à son tour est ramassé par l' Looper et envoyé au Gestionnaire pour le traitement.

Ce que je ne suis pas entièrement sûr de est là que ça se passe. Plus precicely: Si je poste à un gestionnaire, dans mon activité, est l' Activity, Handler, MessageQueue et Looper tous en cours d'exécution sur le thread d'INTERFACE utilisateur? Si non, quelqu'un pourrait-il expliquer comment tout cela se rassemble? :)

71voto

user634618 Points 1965

Réponse courte: elles fonctionnent toutes sur le même thread. Si instancié à partir d'une Activity du cycle de vie de rappel, ils sont tous exécutés sur le main thread d'INTERFACE utilisateur.

Réponse longue:

Un thread peut avoir un Looper, qui contient un MessageQueue. Pour utiliser cette fonction, vous devez créer un Looper sur le thread en cours en appelant le (statique) Looper.prepare(), puis de commencer la boucle en appelant le (la aussi statique) Looper.loop(). Ces sont statiques, car il est supposé être un Looper par thread.

L'appel à l' loop() n'a en général pas de retour pour un certain temps, mais continue à prendre de messages ("tâches", "commandes" ou ce que vous voulez l'appeler) de l' MessageQueue et les gère individuellement (par exemple, en appelant au retour d'un Runnable contenues dans le message). Quand il y a pas de messages dans la file d'attente, le thread se bloque jusqu'à ce qu'il y a de nouveaux messages. Pour arrêter un Looper, vous appelez quit() "(qu'il n'a probablement pas arrêter immédiatement la boucle, mais établit plutôt un indicateur privé qui est vérifié périodiquement à partir de la boucle, la signalisation de l'il à l'arrêt).

Cependant, vous ne pouvez pas ajouter des messages à la file d'attente directement. Au lieu de cela, vous vous inscrivez à un MessageQueue.IdleHandler à attendre une queueIdle() rappel, dans lesquels vous pouvez décider si vous voulez quelque chose ou pas. Tous les gestionnaires sont appelés à tour de rôle. (Donc la "file d'attente" n'est pas vraiment une file d'attente, mais plutôt une collection de rappels d'être appelé régulièrement.)

Remarque concernant le paragraphe précédent: Ce que je fait deviner. Je ne pouvais pas trouver toute la documentation sur ce point, mais il aurait du sens.

mise à jour: voir ahcox' commentaire et de sa réponse.

Parce que c'est beaucoup de travail, le cadre fournit l' Handler classe pour simplifier les choses. Lorsque vous créez un Handler de l'instance, il est (par défaut) lié à l' Looper déjà attaché au thread courant. (L' Handler sait ce qu' Looper pour attacher parce que nous avons appelé prepare() précédemment, ce qui est probablement stocké une référence à l' Looper en ThreadLocal.)

Avec un Handler, vous pouvez les appeler post() pour "mettre un message dans le thread de message de la file d'attente" (façon de parler). L' Handler prendra soin de tous les IdleHandler rappel des trucs et assurez-vous que votre posté Runnable est exécutée. (Il peut également vérifier si le moment est venu déjà, si vous avez publiés avec un retard.)

Juste pour être clair: la seule façon de réellement faire une boucle de fil de faire quelque chose est de poster un message en boucle. Ceci est valable jusqu'à ce que vous appelez quit() sur le looper.


Concernant l'INTERFACE utilisateur android thread: À un certain point (et sans doute avant tout des activités et la comme sont créés) le cadre a mis en place un Looper (contenant un MessageQueue) et l'a commencée. À partir de ce moment, tout ce qui se passe sur le thread de l'INTERFACE utilisateur à travers cette boucle. Cela inclut l'activité de gestion du cycle de vie et ainsi de suite. Tous les rappels que vous override (onCreate(), onDestroy()...) sont au moins indirecty expédiés à partir de cette boucle. Vous pouvez voir que, par exemple, dans la trace de la pile d'exception. (Vous pouvez l'essayer, il suffit d'écrire int a = 1 / 0; quelque part en onCreate()...)


J'espère que cela a du sens. Désolé d'être peu clair auparavant.

12voto

ahcox Points 1781

Le suivi sur le "comment tout cela s'assemble" partie de la question. Comme user634618 a écrit, le looper prend plus d'un thread, le principal thread d'INTERFACE utilisateur dans le cas d'une principal de l'application Looper.

  • Looper.loop() extrait les Messages de son message de la file d'attente. Chaque Message a une référence à l'associé un Gestionnaire qu'il est pour être donné (les membres de cible).
  • À l'intérieur d' Looper.loop() pour chaque message reçu de la file d'attente:
    • loop() des appels public void Handler.dispatchMessage(Message msg) en utilisant le Gestionnaire qui est stocké dans le Message que ses membres de cible.
    • Si le message a un Praticable de rappel membre, qui est exécuté.
    • Autre chose, si le Gestionnaire a un rappel, qui est exécuté.
    • Autre chose, Gestionnaire de l' handleMessage() est appelée avec le Message comme un argument. (Remarque, si vous sous-classe Gestionnaire comme AsyncTask, vous pourriez remplacer handleMessage() comme elle le fait.)

Sur votre question à propos de tous les collaborateurs d'objets sur le même thread de l'INTERFACE utilisateur, un Handler doit être créé sur le même thread que l' Looper qu'il va envoyer les messages d'. Son constructeur de la recherche de l'actuel Looper et le stocker en tant que membre, en les liant l' Handler que Looper. Il référence également qu' Loopers'message queue directement dans son propre membre. L' Handler peut être utilisé pour envoyer le travail à l' Looper à partir de n'importe quel thread, mais ce l'identité des files d'attente de messages routes du travail à faire sur l' Looper's thread.

Lorsque nous sommes en cours d'exécution du code sur un autre thread et que vous voulez envoyer un Exécutable à exécuter sur le thread de l'INTERFACE utilisateur, nous pourrions faire comme ceci:

// 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();
}

2voto

julianjing Points 21

J'essaie de mettre en œuvre ces interfaces moi-même afin de comprendre le concept. Par simplicité, il suffit d'utiliser l'interface par nécessaire. Voici mon code de test:

 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();
    }

}
 

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X