web-dev-qa-db-fra.com

Les gestionnaires, MessageQueue, Looper, fonctionnent-ils tous sur le thread d'interface utilisateur?

J'essaie de comprendre le filetage et je sais que je peux utiliser une variable Handler pour publier des messages/listes exécutables sur la MessageQueue, qui est à son tour récupérée par la Looper et renvoyée au gestionnaire pour traitement.

Si je poste un gestionnaire dans mon activité, les Activity, Handler, MessageQueue et Looper sont-elles toutes exécutées sur le thread d'interface utilisateur? Si non, quelqu'un pourrait-il s'il vous plaît expliquer comment tout cela se passe? :)

38
rogerkk

Suivi de la partie "de la façon dont tout cela se réunit" de la question. Comme l’a écrit user634618, le boucleur reprend un thread, le thread principal de l’interface utilisateur dans le cas du Looper d’une application Main principale.

  • Looper.loop() extrait les messages de sa file d'attente. Chaque message a une référence à un gestionnaire associé auquel il doit être rendu (au membre cible).
  • Inside Looper.loop() pour chaque message obtenu de la file d'attente:
    • loop() appelle public void Handler.dispatchMessage(Message msg) à l'aide du gestionnaire stocké dans le message en tant que membre cible.
    • Si le message a un membre de rappel Runnable, il est exécuté.
    • Sinon, si le gestionnaire a un jeu de rappel partagé, il est exécuté.
    • Sinon, la fonction handleMessage() du gestionnaire est appelée avec le message en tant qu'argument. (Remarque: si vous sous-classe Handler comme AsyncTask, vous pouvez remplacer handleMessage() comme il le fait.)

Sur votre question concernant le fait que tous les objets de collaboration se trouvent sur le même thread d'interface utilisateur, il faut créer un Handler sur le même thread que le Looper auquel il enverra des messages Son constructeur recherchera le Looper en cours. et enregistrez-le en tant que membre, en liant le Handler à ce Looper. Il fera également référence à la file de messages de Looper directement dans son propre membre . Le Handler peut également être utilisé pour envoyer du travail au Looper thread, mais cette identité des files de messages dirige le travail à effectuer sur le thread de Looper

Lorsque nous exécutons du code sur un autre thread et que nous voulons envoyer un Runnable à exécuter sur le thread d'interface utilisateur, nous pourrions le 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();
}
11
ahcox

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

}
2
julianjing

Si je poste sur un gestionnaire dans mon activité, les activités Activity, Handler, MessageQueue et Looper sont-elles toutes exécutées sur le thread d'interface utilisateur? Si non, quelqu'un pourrait-il s'il vous plaît expliquer comment tout cela se passe? :)

Cela dépend de la façon dont vous créez Handler

Cas 1: 

Handler()

Le constructeur par défaut associe ce gestionnaire au Looper pour le thread actuel.

Si vous créez Handler comme ceci dans le thread d'interface utilisateur, Handler est associé à Looper of UI Thread. MessageQueue est également associé à Looper avec le thread d'interface utilisateur. 

Cas 2:

Handler (Looper looper)

Utilisez le Looper fourni à la place de celui par défaut.

Si je crée un HandlerThread et passe le Looper de HandlerThread à Handler, Handler et Looper sont associés à HandlerThread et non au thread UI. Handler, MessageQueue et Looper sont associés à HandlerThread.

Cas d'utilisation: vous souhaitez exécuter une opération réseau OR IO. Vous ne pouvez pas l'exécuter sur le thread d'interface utilisateur et HandlerThread est donc pratique pour vous. 

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

Si vous souhaitez transmettre des données de HandlerThread à un thread d'interface utilisateur, vous pouvez créer un gestionnaire supplémentaire (par exemple, responseHandler) avec Looper à partir d'un thread d'interface utilisateur et appeler sendMessage. Le fil de l'interface utilisateur responseHandler doit remplacer handleMessage

Reportez-vous à ces messages pour plus de détails.

Quel est le but de Looper et comment l'utiliser? (Pour les concepts) 

Android: Griller dans un fil (Par exemple, codez en liant tous ces concepts)

1
Ravindra babu