web-dev-qa-db-fra.com

Quel est le but de Looper et comment l'utiliser?

Je suis nouveau sur Android. Je veux savoir ce que fait la classe Looper et aussi comment l'utiliser. J'ai lu la documentation de la classe Android Looper mais je suis incapable de la comprendre complètement… .. Je l'ai vue dans de nombreux endroits, mais incapable de comprendre son objectif Quelqu'un peut-il m'aider en définissant le but de Looper et en donnant un exemple simple, si possible?

414
Khawar Raza

Qu'est-ce que Looper?

Looper est une classe utilisée pour exécuter les messages (Runnables) dans une file d'attente. Les threads normaux n'ont pas de file d'attente de ce type, par exemple. Le fil simple n'a pas de file d'attente. Il s'exécute une fois et une fois l'exécution de la méthode terminée, le thread n'exécutera pas d'autre message (Runnable).

Où pouvons-nous utiliser la classe Looper?

Si quelqu'un veut exécuter plusieurs messages (Runnables), il doit utiliser la classe Looper, responsable de la création d'une file d'attente dans le thread . Par exemple, lors de l'écriture d'une application téléchargeant des fichiers depuis Internet, nous pouvons utiliser la classe Looper mettre les fichiers à télécharger dans la file d'attente.

Comment ça marche?

Il existe une méthode prepare() pour préparer le Looper. Ensuite, vous pouvez utiliser la méthode loop() pour créer une boucle de message dans le thread actuel. Votre Looper est maintenant prêt à exécuter les demandes de la file d'attente jusqu'à ce que vous quittiez la boucle.

Voici le code par lequel vous pouvez préparer le Looper.

class LooperThread extends Thread {
      public Handler mHandler;

      @Override
      public void run() {
          Looper.prepare();

          mHandler = new Handler() {
              @Override
              public void handleMessage(Message msg) {
                  // process incoming messages here
              }
          };

          Looper.loop();
      }
  }
374
Dharmendra

Vous pouvez mieux comprendre ce qu'est Looper dans le contexte de la structure d'interface graphique. Looper est fait pour faire 2 choses.

1) Looper transforme un thread normal, qui se termine lorsque sa méthode run () est renvoyée, en un élément exécuté en continu jusqu'à ce que l'application Android s'exécute, ce qui est nécessaire dans le cadre d'interface graphique (Techniquement, elle se termine toujours () méthode return. Mais laissez-moi préciser ce que je veux dire ci-dessous).

2) Looper fournit une file d'attente dans laquelle les tâches à effectuer sont mises en file d'attente, ce qui est également nécessaire dans le cadre d'interface graphique.

Comme vous le savez peut-être, lorsqu'une application est lancée, le système crée un thread d'exécution, appelé «main», et les applications Android s'exécutent normalement entièrement sur un seul thread, par défaut, le «thread principal». Mais main thread n'est pas un secret, un fil spécial. C'est juste un thread normal semblable aux threads que vous créez avec le code new Thread(), ce qui signifie qu'il se termine lorsque sa méthode run () est de retour! 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();
    }
}

Appliquons maintenant ce principe simple aux applications Android. Que se passerait-il si une application Android fonctionnait sur un fil normal? Un fil appelé "main" ou "UI" ou tout ce qui commence votre application et dessine toute l'interface utilisateur. Ainsi, le premier écran est affiché pour les utilisateurs. Alors quoi maintenant? Le fil principal se termine? Non, ça ne devrait pas. Il faut attendre que les utilisateurs fassent quelque chose, non? Mais comment pouvons-nous obtenir ce comportement? Eh bien, nous pouvons essayer avec Object.wait() ou Thread.sleep(). Par exemple, le thread principal termine son travail initial pour afficher le premier écran et se met en veille. Cela 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 semblable à une file d'attente pour contenir plusieurs travaux. Pensez au cas où un utilisateur touche l'écran en série et qu'une tâche prend plus de temps. Il nous faut donc une structure de données pour que les travaux soient exécutés du premier au premier sorti. En outre, vous pouvez imaginer que l'implémentation d'une interruption par un thread toujours en cours d'exécution et de processus en cours d'exécution à l'aide d'une 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 de cela dont parle Looper}. Le document officiel de la classe Looper dit: "Les threads ne sont associés à aucune boucle de message par défaut", et Looper est une classe "utilisée pour exécuter une boucle de message pour un thread". Maintenant, vous pouvez comprendre ce que cela signifie. 

Pour clarifier les choses, vérifions le code dans lequel le thread principal est transformé. Tout se passe dans Classe ActivityThread . Dans sa méthode main (), vous trouverez ci-dessous le code qui transforme un thread principal normal en quelque chose dont nous avons besoin.

public final class ActivityThread {
    ...
    public static void main(String[] args) {
        ...
        Looper.prepareMainLooper();
        Looper.loop();
        ...
    }
}

et la méthode Looper.loop() effectue une boucle infinie, retire un message du message et le traite un par un:

public static void loop() {
    ...
    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }
        ...
        msg.target.dispatchMessage(msg);
        ...
    }
}

Donc, fondamentalement, Looper est une classe conçue pour résoudre un problème qui survient dans le cadre d’une interface graphique. Mais ce genre de besoins peut également se produire dans d'autres situations. En fait, il s’agit d’un motif assez connu pour les applications multi-threads, et vous en apprendrez plus à ce sujet dans "Programmation concurrente en Java" "de Doug Lea (le chapitre 4.1.4" Threader "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 d'interface graphique peuvent nécessiter quelque chose de similaire. Vous pouvez trouver presque le même mécanisme dans le cadre Java Swing.

239
김준호

Looper permet d'exécuter des tâches de manière séquentielle sur un seul thread. Et gestionnaire définit les tâches qui doivent être exécutées. C’est un scénario typique que j’essaie d’illustrer dans cet exemple:

class SampleLooper extends Thread {
@Override
public void run() {
  try {
    // preparing a looper on current thread     
    // the current thread is being detected implicitly
    Looper.prepare();

    // now, the handler will automatically bind to the
    // Looper that is attached to the current thread
    // You don't need to specify the Looper explicitly
    handler = new Handler();

    // After the following line the thread will start
    // running the message loop and will not normally
    // exit the loop unless a problem happens or you
    // quit() the looper (see below)
    Looper.loop();
  } catch (Throwable t) {
    Log.e(TAG, "halted due to an error", t);
  } 
}
}

Nous pouvons maintenant utiliser le gestionnaire dans d'autres threads (par exemple, le thread ui) pour poster la tâche sur Looper à exécuter.

handler.post(new Runnable()
{
public void run() {
//This will be executed on thread using Looper.
    }
});

Sur le fil de l'interface utilisateur, nous avons un Looper implicite qui nous permet de gérer les messages sur le fil de l'interface utilisateur.

73
user2834274

Android Looper est un wrapper à attacher MessageQueue à Thread et gère le traitement de la file d'attente. Cela semble très énigmatique dans la documentation Android et il peut arriver que nous ayons à faire face à des problèmes d’accès à l’interface utilisateur Looper. Si nous ne comprenons pas les bases, cela devient très difficile à gérer. 

Voici un article qui explique le cycle de vie de Looper, son utilisation et l'utilisation de Looper dans Handler

 enter image description here

Looper = Thread + MessageQueue

31
user542954

Définition de Looper & Handler:

Looper est une classe qui transforme un fil en Pipeline Thread et Handler vous donne un mécanisme pour y insérer des tâches à partir de tout autre thread. 

Détails:

Ainsi, un PipeLine Thread est un thread qui peut accepter plus de tâches d'autres threads via un gestionnaire.

Le Looper est nommé ainsi parce qu'il implémente la boucle - prend la tâche suivante, l'exécute, puis la suivante, etc. Le gestionnaire est appelé gestionnaire, car il est utilisé pour gérer ou accepter chaque fois la tâche suivante à partir de tout autre thread et le transmettre à Looper (thread ou filetage PipeLine).

Exemple:

L’exemple parfait d’un Looper and Handler ou d’un PipeLine Thread est de télécharger plusieurs images ou de les télécharger sur un serveur (Http) l’une après l’autre dans un seul thread au lieu de démarrer un nouveau Thread pour chaque appel réseau en arrière-plan.

En savoir plus sur Looper et Handler et sur la définition du filetage de pipeline:

Android Guts: Introduction aux boucles et manipulateurs

11
ahmadalibaloch

Un Looper a une synchronizedMessageQueue utilisée pour traiter les messages placés dans la file d'attente.

Il implémente un modèle de stockage spécifique Thread.

Un seul Looper par Thread. Les méthodes clés incluent prepare(), loop() et quit().

prepare() initialise la Thread actuelle en tant que Looper. prepare() est la méthode static qui utilise la classe ThreadLocal comme indiqué ci-dessous.

   public static void prepare(){
       ...
       sThreadLocal.set
       (new Looper());
   }
  1. prepare() doit être appelé explicitement avant d'exécuter la boucle d'événement. 
  2. loop() exécute la boucle d'événement qui attend que les messages arrivent dans la file de messages d'un thread spécifique. Une fois le message suivant reçu, la méthode loop() le distribue à son gestionnaire cible.
  3. quit() ferme la boucle d'événement. Il ne termine pas la boucle, mais met en file d'attente un message spécial.

Looper peut être programmé dans une Thread en plusieurs étapes

  1. Prolonger Thread

  2. Appelez Looper.prepare() pour initialiser Thread en tant que Looper

  3. Créez une ou plusieurs Handler (s) pour traiter les messages entrants

  4. Appelez Looper.loop() pour traiter les messages jusqu'à ce que la boucle soit communiquée à quit()
7
Theo

La durée de vie de Java Thread est terminée après l'achèvement de la méthode run(). Le même fil ne peut pas être redémarré. 

Looper transforme une Thread normale en boucle de message. Les méthodes clés de Looper sont:

void prepare ()

Initialise le thread actuel en tant que boucleur. Cela vous donne la possibilité de créer des gestionnaires qui référencent ensuite ce boucleur avant de commencer réellement la boucle. Assurez-vous d'appeler loop () après avoir appelé cette méthode et terminez-la en appelant quit ().

void loop ()

Exécutez la file de messages dans ce fil. Assurez-vous d'appeler quit () pour terminer la boucle.

void quit()

Quitte le looper.

Force la méthode loop () à se terminer sans traiter d'autres messages dans la file d'attente.

Cet article mindorks de Janishar explique les concepts de base à la manière de Nice.

 enter image description here

Looper est associé à un thread. Si vous avez besoin de Looper sur le thread d'interface utilisateur, Looper.getMainLooper() renverra le thread associé. 

Il faut que Looper soit associé à un Handler .

Looper, Handler, et HandlerThread sont le moyen utilisé par Android pour résoudre les problèmes de programmation asynchrone.

Une fois que vous avez Handler, vous pouvez appeler les API ci-dessous.

post (Runnable r)

Rend le Runnable r être ajouté à la file de messages. Le runnable sera exécuté sur le thread auquel ce gestionnaire est attaché.

boolean sendMessage (Message msg)

Pousse un message à la fin de la file d'attente après tous les messages en attente avant l'heure actuelle. Il sera reçu dans handleMessage (Message), dans le fil attaché à ce gestionnaire.

HandlerThread est une classe pratique pour démarrer un nouveau thread avec une boucle. Le looper peut ensuite être utilisé pour créer des classes de gestionnaires

Dans certains scénarios, vous ne pouvez pas exécuter de tâches Runnable sur le thread d'interface utilisateur. par exemple. Opérations réseau: envoyez un message sur un socket, ouvrez une URL et récupérez le contenu en lisant InputStream

Dans ces cas, HandlerThread est utile. Vous pouvez obtenir un objet Looper à partir de HandlerThread et créer un Handler sur HandlerThread au lieu du thread principal.

Le code HandlerThread sera comme ceci:

@Override
public void run() {
    mTid = Process.myTid();
    Looper.prepare();
    synchronized (this) {
        mLooper = Looper.myLooper();
        notifyAll();
    }
    Process.setThreadPriority(mPriority);
    onLooperPrepared();
    Looper.loop();
    mTid = -1;
}

Reportez-vous à l'article ci-dessous pour un exemple de code:

Android: Toast dans un fil de discussion

5
Ravindra babu

Cette réponse n'a rien à voir avec la question, mais l'utilisation de looper et la façon dont les gens ont créé le handler et le looper dans TOUTES les réponses sont une mauvaise pratique (certaines explications sont correctes), je dois poster ceci:

HandlerThread thread = new HandlerThread(threadName);
thread.start();
Looper looper = thread.getLooper();
Handler myHandler = new Handler(looper);

et pour une mise en œuvre complète

4
TacB0sS

Traiter plusieurs éléments vers le bas ou télécharger dans un Service est un meilleur exemple.

Handler et AsnycTask sont souvent utilisés pour propager Events/Messages entre le UI (thread) et un thread de travail ou pour retarder des actions. Donc, ils sont plus liés à l'interface utilisateur.

Un Looper gère les tâches (Runnables, Futures) dans une file d'attente liée à un thread en arrière-plan, même sans interaction de l'utilisateur ou interface utilisateur affichée (l'application télécharge un fichier en arrière-plan pendant un appel).

3
Thorsten

Comprendre les threads de Looper

Un thread Java est une unité d'exécution conçue pour exécuter une tâche dans sa méthode run () et se terminer après celle-ci:  enter image description here

Mais dans Android, il existe de nombreux cas d'utilisation nécessitant de garder un thread actif et d'attendre les entrées/événements de l'utilisateur, par exemple. Fil de l'interface utilisateur aka Main Thread.

Le fil principal d'Android est un fil Java qui est lancé pour la première fois par la JVM lors du lancement d'une application et continue à s'exécuter jusqu'à ce que l'utilisateur choisisse de la fermer ou rencontre une exception non gérée.

Lorsqu'une application est lancée, le système crée un fil d'exécution pour l'application, appelé "principal". Ce fil est très important car il est chargé de la distribution des événements aux widgets d'interface utilisateur appropriés, y compris les événements de dessin.

 enter image description here

Maintenant, il convient de noter que le fil principal est le fil Java, mais il continue d'écouter les événements des utilisateurs et dessine des images à 60 ips à l'écran. Il ne meurt toujours pas après chaque cycle. comment est-ce?

La réponse est Looper Class : Looper est une classe utilisée pour maintenir un thread actif et gérer une file de messages afin d'exécuter des tâches sur ce thread.

Les threads par défaut ne sont associés à aucune boucle de message, mais vous pouvez en affecter une en appelant Looper.prepare () dans la méthode d'exécution, puis appelez Looper.loop ().

Le but de Looper est de garder un thread actif et d'attendre le prochain cycle de l'objet input Message pour effectuer un calcul qui, sinon, sera détruit après le premier cycle d'exécution.

Si vous souhaitez mieux comprendre comment Looper gère la file d'attente Message, vous pouvez consulter le code source de Looperclass:

https://github.com/aosp-mirror/platform_frameworks_base/blob/master/core/Java/Android/os/Looper.Java

Vous trouverez ci-dessous un exemple montrant comment créer un Looper Thread et communiquer avec la classe Activity à l’aide de LocalBroadcast.

class LooperThread : Thread() {

    // sendMessage success result on UI
    private fun sendServerResult(result: String) {
        val resultIntent = Intent(ServerService.ACTION)
        resultIntent.putExtra(ServerService.RESULT_CODE, Activity.RESULT_OK)
        resultIntent.putExtra(ServerService.RESULT_VALUE, result)
        LocalBroadcastManager.getInstance(AppController.getAppController()).sendBroadcast(resultIntent)
    }

    override fun run() {
        val looperIsNotPreparedInCurrentThread = Looper.myLooper() == null

        // Prepare Looper if not already prepared
        if (looperIsNotPreparedInCurrentThread) {
            Looper.prepare()
        }

        // Create a handler to handle messaged from Activity
        handler = Handler(Handler.Callback { message ->
            // Messages sent to Looper thread will be visible here
            Log.e(TAG, "Received Message" + message.data.toString())

            //message from Activity
            val result = message.data.getString(MainActivity.BUNDLE_KEY)

            // Send Result Back to activity
            sendServerResult(result)
            true
        })

        // Keep on looping till new messages arrive
        if (looperIsNotPreparedInCurrentThread) {
            Looper.loop()
        }
    }

    //Create and send a new  message to looper
    fun sendMessage(messageToSend: String) {
        //Create and post a new message to handler
        handler!!.sendMessage(createMessage(messageToSend))
    }


    // Bundle Data in message object
    private fun createMessage(messageToSend: String): Message {
        val message = Message()
        val bundle = Bundle()
        bundle.putString(MainActivity.BUNDLE_KEY, messageToSend)
        message.data = bundle
        return message
    }

    companion object {
        var handler: Handler? = null // in Android Handler should be static or leaks might occur
        private val TAG = javaClass.simpleName

    }
}

Utilisation :

 class MainActivity : AppCompatActivity() {

    private var looperThread: LooperThread? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // start looper thread
        startLooperThread()

        // Send messages to Looper Thread
        sendMessage.setOnClickListener {

            // send random messages to looper thread
            val messageToSend = "" + Math.random()

            // post message
            looperThread!!.sendMessage(messageToSend)

        }   
    }

    override fun onResume() {
        super.onResume()

        //Register to Server Service callback
        val filterServer = IntentFilter(ServerService.ACTION)
        LocalBroadcastManager.getInstance(this).registerReceiver(serverReceiver, filterServer)

    }

    override fun onPause() {
        super.onPause()

        //Stop Server service callbacks
     LocalBroadcastManager.getInstance(this).unregisterReceiver(serverReceiver)
    }


    // Define the callback for what to do when data is received
    private val serverReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
            val resultCode = intent.getIntExtra(ServerService.RESULT_CODE, Activity.RESULT_CANCELED)
            if (resultCode == Activity.RESULT_OK) {
                val resultValue = intent.getStringExtra(ServerService.RESULT_VALUE)
                Log.e(MainActivity.TAG, "Server result : $resultValue")

                serverOutput.text =
                        (serverOutput.text.toString()
                                + "\n"
                                + "Received : " + resultValue)

                serverScrollView.post( { serverScrollView.fullScroll(View.FOCUS_DOWN) })
            }
        }
    }

    private fun startLooperThread() {

        // create and start a new LooperThread
        looperThread = LooperThread()
        looperThread!!.name = "Main Looper Thread"
        looperThread!!.start()

    }

    companion object {
        val BUNDLE_KEY = "handlerMsgBundle"
        private val TAG = javaClass.simpleName
    }
}

Pouvons-nous utiliser une tâche asynchrone ou des services d'intention?

  • Les tâches asynchrones sont conçues pour effectuer une courte opération en arrière-plan et donner des progrès et des résultats sur le fil de l'interface utilisateur. Les tâches asynchrones ont des limites car vous ne pouvez pas créer plus de 128 tâches asynchrones et ThreadPoolExecutor autoriseront uniquement jusqu'à 5 tâches asynchrones .

  • IntentServices sont également conçus pour effectuer des tâches en arrière-plan pendant un peu plus longtemps et vous pouvez utiliser LocalBroadcast pour communiquer avec Activity. Mais les services sont détruits après l'exécution de la tâche. Si vous voulez le laisser fonctionner pendant longtemps, il vous faudra faire des dingues comme while(true){...}.

Autres cas d'utilisation significatifs pour le fil de boucle:

  • Utilisé pour la communication de socket à 2 voies où le serveur continue d'écouter le socket client et de ré-écrire l'accusé

  • Traitement bitmap en arrière-plan. Transmettez l’URL de l’image au thread Looper, qui appliquera des effets de filtre et le stockera dans un emplacement temporaire, puis diffusera le chemin temporaire de l’image.

1
Hitesh Sahu

Qu'est-ce que Looper?

DE DOCS

Looper

Looper Classe utilisée pour exécuter une boucle de message pour un thread. Les threads par défaut ne sont pas associés à une boucle de message; pour en créer un, appelez prepare() dans le thread qui doit exécuter la boucle, puis loop() pour qu'il traite les messages jusqu'à ce que la boucle soit arrêtée. 

  • Une Looper est une boucle de traitement de message:
  • Un caractère important de Looper est qu’il est associé au fil dans lequel le Looper est créé.
  • La classe Looper gère une variable MessageQueue , qui contient une liste de messages. Un caractère important de Looper est qu’il est associé au fil dans lequel le Looper est créé.
  • La Looper est nommée ainsi parce qu'elle implémente la boucle - prend la tâche suivante, l'exécute, puis la suivante et ainsi de suite. La Handler s'appelle un gestionnaire parce que quelqu'un ne pourrait pas inventer un meilleur nom
  • Android Looper est une classe Java de l'interface utilisateur Android qui, conjointement avec la classe Handler, permet de traiter les événements d'interface utilisateur tels que les clics sur les boutons, les retraits d'écran et les changements d'orientation.

Comment ça marche?

 enter image description here

Créer un Looper

Un thread obtient Looper et MessageQueue en appelant Looper.prepare() après son exécution. Looper.prepare() identifie le thread appelant, crée un objet Looper et MessageQueue et associe le thread

SAMPLE CODE

class MyLooperThread extends Thread {

      public Handler mHandler; 

      public void run() { 

          // preparing a looper on current thread  
          Looper.prepare();

          mHandler = new Handler() { 
              public void handleMessage(Message msg) { 
                 // process incoming messages here
                 // this will run in non-ui/background thread
              } 
          }; 

          Looper.loop();
      } 
  }

Pour plus d'informations, vérifiez ci-dessous post

0
Nilesh Rathod