Réponses:
A Looper
est une boucle de gestion de messages: il lit et traite les éléments d'un fichier MessageQueue
. La Looper
classe est généralement utilisée en conjonction avec une HandlerThread
(une sous-classe de Thread
).
A Handler
est une classe utilitaire qui facilite l'interaction avec un - Looper
principalement en publiant des messages et des Runnable
objets dans le thread MessageQueue
. Lorsqu'une Handler
est créée, elle est liée à un Looper
thread spécifique (et associé à une file de discussion et de messages).
Dans une utilisation classique, vous créez et démarrez un HandlerThread
, puis créez un Handler
objet (ou des objets) par lesquels d'autres threads peuvent interagir avec l' HandlerThread
instance. Le Handler
doit être créé lors de l'exécution sur le HandlerThread
, bien qu'une fois créé, il n'y ait aucune restriction sur les threads pouvant utiliser les Handler
méthodes de planification de s ( post(Runnable)
, etc.)
Le thread principal (aka thread UI) dans une application Android est configuré en tant que thread de gestionnaire avant la création de votre instance d'application.
En plus des documents de classe, il y a une belle discussion sur tout cela ici .
PS Toutes les classes mentionnées ci-dessus sont dans le package android.os
.
MessageQueue
Android pour indiquer que a MessageQueue
est une " classe de bas niveau contenant la liste des messages à envoyer par a Looper
. "
Il est bien connu qu'il est illégal de mettre à jour les composants de l'interface utilisateur directement à partir de threads autres que le thread principal dans Android. Ce document Android ( Gestion des opérations coûteuses dans le thread d'interface utilisateur ) suggère les étapes à suivre si nous devons démarrer un thread séparé pour effectuer un travail coûteux et mettre à jour l'interface utilisateur une fois terminé. L'idée est de créer un objet Handler associé au thread principal et d'y publier un Runnable au moment opportun. Cela Runnable
sera appelé sur le thread principal . Ce mécanisme est implémenté avec les classes Looper et Handler .
La Looper
classe gère un MessageQueue , qui contient une liste de messages . Un caractère important de Looper est qu'il est associé au thread dans lequel le Looper
est créé . Cette association est conservée pour toujours et ne peut être ni rompue ni modifiée. Notez également qu'un fil ne peut pas être associé à plus d' un Looper
. Afin de garantir cette association, Looper
est stocké dans le stockage local du thread, et il ne peut pas être créé directement via son constructeur. La seule façon de le créer est d'appeler la méthode prepare static on Looper
. préparer la méthode examine d'abord ThreadLocaldu thread actuel pour vous assurer qu'il n'y a pas déjà un Looper associé au thread. Après l'examen, un nouveau Looper
est créé et enregistré dans ThreadLocal
. Après avoir préparé le Looper
, nous pouvons appeler la méthode de boucle dessus pour vérifier les nouveaux messages et les Handler
traiter.
Comme son nom l'indique, la Handler
classe est principalement chargée de gérer (ajouter, supprimer, distribuer) les messages des threads actuels MessageQueue
. Une Handler
instance est également liée à un thread. La liaison entre Handler et Thread est réalisée via Looper
et MessageQueue
. A Handler
est toujours lié à a Looper
, puis lié au thread associé au Looper
. Contrairement à Looper
, plusieurs instances de Handler peuvent être liées au même thread. Chaque fois que nous appelons post ou toute autre méthode similaire sur le Handler
, un nouveau message est ajouté au fichier associé MessageQueue
. Le champ cible du message est défini sur l' Handler
instance actuelle . Quand leLooper
a reçu ce message, il invoque dispatchMessage sur le champ cible du message, afin que le message soit renvoyé vers l'instance Handler à traiter, mais sur le thread correct. Les relations entre Looper
, Handler
et MessageQueue
est présenté ci - dessous:
Commençons par le Looper. Vous pouvez comprendre plus facilement la relation entre Looper, Handler et MessageQueue lorsque vous comprenez ce qu'est Looper. Vous pouvez également mieux comprendre ce qu'est Looper dans le contexte du framework GUI. Looper est fait pour faire 2 choses.
1) Looper transforme un thread normal , qui se termine lorsque sa run()
méthode retourne, en quelque chose qui s'exécute en continu jusqu'à ce que l'application Android soit en cours d'exécution , ce qui est nécessaire dans le cadre de l'interface graphique (techniquement, il se termine toujours lorsque la run()
méthode revient. Mais laissez-moi clarifier ce que je veux dire, au dessous de).
2) Looper fournit une file d'attente dans laquelle les travaux à effectuer sont mis en file d'attente, ce qui est également nécessaire dans le cadre de l'interface graphique.
Comme vous le savez peut-être, lorsqu'une application est lancée, le système crée un thread d'exécution pour l'application, appelé «main», et les applications Android fonctionnent normalement entièrement sur un seul thread par défaut le «thread principal». Mais le fil principal n'est pas un fil spécial et secret . C'est juste un thread normal que vous pouvez également créer avec du new Thread()
code, ce qui signifie qu'il se termine lorsque sa run()
méthode revient! Pensez à l'exemple ci-dessous.
public class HelloRunnable implements Runnable {
public void run() {
System.out.println("Hello from a thread!");
}
public static void main(String args[]) {
(new Thread(new HelloRunnable())).start();
}
}
Maintenant, appliquons ce principe simple à l'application Android. Que se passerait-il si une application Android était exécutée sur un thread normal? Un thread appelé "main" ou "UI" ou quoi que ce soit démarre l'application, et dessine toute l'interface utilisateur. Ainsi, le premier écran est affiché aux utilisateurs. Et maintenant? Le thread principal se termine? Non, ça ne devrait pas. Il devrait attendre que les utilisateurs fassent quelque chose, non? Mais comment parvenir à ce comportement? Eh bien, nous pouvons essayer avec Object.wait()
ouThread.sleep()
. Par exemple, le thread principal termine son travail initial pour afficher le premier écran et se met en veille. Il se réveille, ce qui signifie interrompu, lorsqu'un nouveau travail à faire est récupéré. Jusqu'ici tout va bien, mais pour le moment, nous avons besoin d'une structure de données en forme de file d'attente pour contenir plusieurs tâches. Pensez à un cas où un utilisateur touche l'écran en série et qu'une tâche prend plus de temps à se terminer. Nous avons donc besoin d'une structure de données pour conserver les tâches à effectuer de la manière premier entré, premier sorti. De plus, vous pouvez imaginer que l'implémentation d'un thread toujours en cours d'exécution et de traitement à l'arrivée à l'aide d'interruption n'est pas facile et conduit à un code complexe et souvent impossible à maintenir. Nous préférerions créer un nouveau mécanisme à cette fin, et c'est ce qu'est Looper . Le document officiel de la classe Looperdit, "Les threads par défaut n'ont pas de boucle de message associée", et Looper est une classe "utilisée pour exécuter une boucle de message pour un thread". Vous pouvez maintenant comprendre ce que cela signifie.
Passons à Handler et MessageQueue. Tout d'abord, MessageQueue est la file d'attente que j'ai mentionnée ci-dessus. Il réside dans un Looper, et c'est tout. Vous pouvez le vérifier avec le code source de la classe Looper . La classe Looper a une variable membre de MessageQueue.
Alors, qu'est-ce que Handler? S'il y a une file d'attente, alors il devrait y avoir une méthode qui devrait nous permettre de mettre une nouvelle tâche en file d'attente dans la file d'attente, non? C'est ce que fait Handler. Nous pouvons mettre en file d'attente une nouvelle tâche dans une file d'attente (MessageQueue) en utilisant diverses post(Runnable r)
méthodes. C'est tout. Tout cela concerne Looper, Handler et MessageQueue.
Mon dernier mot est, donc fondamentalement, Looper est une classe conçue pour résoudre un problème qui se produit dans le cadre de l'interface graphique. Mais ce type de besoins peut également se produire dans d'autres situations. En fait, c'est un modèle assez célèbre pour les applications multi threads, et vous pouvez en apprendre plus à ce sujet dans "Programmation simultanée en Java" par Doug Lea (en particulier, le chapitre 4.1.4 "Worker Threads" serait utile). En outre, vous pouvez imaginer que ce type de mécanisme n'est pas unique dans le cadre Android, mais tous les cadres GUI peuvent nécessiter un peu similaire à celui-ci. Vous pouvez trouver presque le même mécanisme dans le framework Java Swing.
MessageQueue
: C'est une classe de bas niveau contenant la liste des messages à envoyer par un Looper
. Les messages ne sont pas ajoutés directement à un MessageQueue
, mais plutôt via des Handler
objets associés à Looper
. [ 3 ]
Looper
: Il boucle sur un MessageQueue
qui contient les messages à envoyer. La tâche réelle de gestion de la file d'attente est effectuée par le Handler
qui est chargé de gérer (ajouter, supprimer, distribuer) les messages dans la file d'attente de messages. [ 2 ]
Handler
: Il vous permet d'envoyer et de processus Message
et des Runnable
objets associés à un fil de MessageQueue
. Chaque instance de gestionnaire est associée à un seul thread et à la file d'attente de messages de ce thread. [ 4 ]
Lorsque vous créez un nouveau Handler
, il est lié au thread / file d'attente de messages du thread qui le crée - à partir de ce moment, il remettra les messages et les exécutables à cette file d'attente de messages et les exécutera lorsqu'ils sortent de la file d'attente de messages .
Veuillez consulter l'image ci-dessous [ 2 ] pour une meilleure compréhension.
Étendre la réponse, par @K_Anas, avec un exemple, comme indiqué
Il est largement connu qu'il est illégal de mettre à jour les composants de l'interface utilisateur directement à partir de threads autres que le thread principal dans Android.
par exemple, si vous essayez de mettre à jour l'interface utilisateur à l'aide de Thread.
int count = 0;
new Thread(new Runnable(){
@Override
public void run() {
try {
while(true) {
sleep(1000);
count++;
textView.setText(String.valueOf(count));
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
).start();
votre application plantera avec exception.
android.view.ViewRoot $ CalledFromWrongThreadException: seul le thread d'origine qui a créé une hiérarchie de vues peut toucher ses vues.
en d'autres termes, vous devez utiliser Handler
qui garde la référence à la tâche MainLooper
ie Main Thread
ou UI Thread
et passez en tant que Runnable
.
Handler handler = new Handler(getApplicationContext().getMainLooper);
int count = 0;
new Thread(new Runnable(){
@Override
public void run() {
try {
while(true) {
sleep(1000);
count++;
handler.post(new Runnable() {
@Override
public void run() {
textView.setText(String.valueOf(count));
}
});
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
).start() ;