Comprendre la concurrence sur Android avec HaMeR

1. Introduction

Tous ceux qui essaient de développement sur Android découvrent l’importance de la simultanéité. La seule façon de créer une application réactive est de laisser le fil de l'interface utilisateur aussi libre que possible, en laissant tout le travail difficile être effectué de manière asynchrone par les fils d'arrière-plan..

En raison de la conception d’Android, la gestion des threads en utilisant uniquement le java.lang.thread et java.util.concurrent les colis pourraient être très difficiles. L'utilisation des packages de threads de bas niveau avec Android signifie que vous devez vous soucier de la synchronisation complexe pour éviter les conditions de concurrence. Heureusement, les employés de Google ont travaillé d'arrache-pied et ont mis au point d'excellents outils pour faciliter notre travail: AsyncTaskIntentServiceChargeurAsyncQueryHandler et CursorLoader sont tous utiles, ainsi que les classes HaMeR Gestionnaire, Message, et Runnable. Vous avez le choix entre plusieurs options intéressantes, chacune avec ses avantages et ses inconvénients..

On a beaucoup parlé de la AsyncTask objet, et beaucoup de gens l'utilisent comme un balle en argent solution de simultanéité sur Android. Il est extrêmement utile pour les opérations courtes, facile à mettre en œuvre et probablement l’approche la plus populaire pour la concurrence sur Android. Si vous voulez en savoir plus sur AsyncTask, consultez les articles Envato Tuts suivants.

  • Android From Scratch: Opérations en arrière-plan

    Le threading dans n'importe quel langage ou plate-forme de programmation est difficile, et Android ne fait pas exception. Dans ce tutoriel, vous apprendrez certains des outils…
    Paul Trebilcox-Ruiz
    SDK Android
  • Comprendre les valeurs de tâches asynchrones en 60 secondes

    Sur Android, la classe AsyncTask est couramment utilisée pour effectuer des opérations sur un thread en arrière-plan. Dans cette vidéo, je vous montre comment fonctionne une AsyncTask et comment…
    Paul Trebilcox-Ruiz
    Android

toutefois, AsyncTask ne devrait pas être le seul outil sur votre ceinture à outils.

Pour les opérations de longue durée, pour des problèmes de simultanéité complexes ou pour gagner en efficacité dans certaines situations, vous devez choisir une autre solution. Si vous avez besoin de plus de flexibilité ou d'efficacité que AsyncTask fournit, vous pouvez utiliser le HaMeR (Gestionnaire, Message Et Runnable) cadre.Dans ce tutoriel, nous allons explorer le framework HaMeR, l'un des modèles de concurrence les plus puissants disponibles sur Android, et découvrirons quand et comment l'utiliser. Dans un tutoriel de suivi, je vais vous montrer comment coder une application pour tester certaines possibilités de HaMeR..

La section suivante présentera l’importance des threads d’arrière-plan pour le système Android. Si vous connaissez ce concept, n'hésitez pas à l'ignorer et à passer directement à la discussion du cadre HaMeR dans la section 3..

2. Réactivité à travers les fils de fond

Lors du démarrage d'une application Android, le premier thread généré par son processus est le thread principal, également appelé thread d'interface utilisateur, qui est responsable de la gestion de toute la logique de l'interface utilisateur. C'est le fil le plus important d'une application. Il est responsable de la gestion de toutes les interactions de l'utilisateur et de la "liaison" entre les pièces mobiles de l'application. Android prend cela très au sérieux et si votre thread d'interface utilisateur est bloqué sur une tâche pendant plus de quelques secondes, l'application se bloque..

[Le fil de l'interface utilisateur] 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. C’est également le fil dans lequel votre application interagit avec les composants de la boîte à outils de l’UI Android (composants de la android.widget et android.view paquets). En tant que tel, le thread principal est également parfois appelé le thread d'interface utilisateur. - Processus et threads, Guide du développeur Android

Le problème est que presque tout le code d'une application Android sera exécuté par défaut sur le thread d'interface utilisateur. Étant donné que les tâches sur un fil sont effectuées de manière séquentielle, cela signifie que votre interface utilisateur peut se figer, devenant insensible pendant le traitement d'un autre travail..

Les tâches de longue durée appelées sur l'interface utilisateur seront probablement fatales pour votre application et une boîte de dialogue ANR (Application non répondante) apparaîtra. Même les petites tâches peuvent compromettre l'expérience utilisateur. Par conséquent, la bonne approche consiste à supprimer le plus de travail possible du thread d'interface utilisateur à l'aide de threads d'arrière-plan. Comme indiqué précédemment, il existe de nombreuses façons de résoudre ce problème et nous allons explorer le cadre HaMeR, l'une des solutions clés fournies par Android pour remédier à cette situation..

3. Le cadre HaMeR

Le framework HaMeR permet aux threads d’arrière-plan d’envoyer des messages ou de publier des runnables au thread d’UI et à tout autre thread. MessageQueue via des handlers. HaMeR fait référence à Gestionnaire, Message, Et Runnable. Il y a aussi quelques autres classes importantes qui travaillent avec le HaMeR: Looper et MessageQueue. Ensemble, ces objets sont chargés de faciliter la gestion des threads sur Android, d'assurer la synchronisation et de fournir des méthodes simples permettant aux threads d'arrière-plan de communiquer avec l'interface utilisateur et avec d'autres threads..

Voici comment les classes du framework HaMeR s'emboîtent.

  • Looper exécute une boucle de message sur un fil en utilisant le MessageQueue.
  • MessageQueue contient une liste de messages à envoyer par le Looper.
  • Gestionnaire permet l'envoi et le traitement de Message et Runnable au MessageQueue. Il peut être utilisé pour envoyer et traiter des messages entre les threads.
  • Message contient une description et des données pouvant être envoyées à un gestionnaire.
  • Runnable représente une tâche à exécuter.

Avec le framework HaMeR, les threads peuvent envoyer des messages ou publier des objets exécutables soit pour eux-mêmes, soit pour le thread d'interface utilisateur. HaMeR favorise également les interactions de fil de fond via Gestionnaire.

3.1. La classe de handler

Gestionnaire est le cheval de bataille HaMeR. C'est responsable de l'envoi Message (message de données) et post Runnable (message de tâche) objets à la MessageQueue associé à un Fil. Après avoir livré les tâches à la file d’attente, le gestionnaire reçoit les objets de la Looper et traite les messages au moment opportun en utilisant le Gestionnaire associé avec.

UNE Gestionnaire peut être utilisé pour envoyer ou poster Message et Runnable objets entre les threads, tant que ces threads partagent le même processus. Sinon, il sera nécessaire de créer une communication IPC (Inter Process Communication), une méthodologie qui dépasse le cadre de ce tutoriel..

Instancier un gestionnaire

UNE Gestionnaire doit toujours être associé à un Looper, et cette connexion doit être faite pendant son instanciation. Si vous ne fournissez pas un Looper au Gestionnaire, il sera lié au courant Filde Looper.

// Handler utilise le handler actuel du Looper Handler = new Handler (); // Handler utilise le Looper fournit Handler handler = new Handler (Looper);

Gardez à l'esprit qu'un Gestionnaire est toujours associé à un Looper, et cette connexion est permanente et ne peut pas être changée une fois établie. Cependant, un LooperLe fil peut avoir des associations avec plusieurs Gestionnaires. Il est également important de noter qu’un Looper doit être actif avant son association avec un Gestionnaire.

3.2. Looper et MessageQueue

Le travail en coopération entre Looper et MessageQueue dans un thread Java crée une boucle de tâches traitées séquentiellement. Une telle boucle gardera le fil en vie pendant qu'il attend de recevoir plus de tâches. Un fil ne peut avoir qu'un seul Looper et une MessageQueue associé avec; Cependant, il peut y avoir plusieurs gestionnaires pour chaque thread. Les gestionnaires sont responsables du traitement des tâches de la file d'attente et chaque tâche sait quel gestionnaire est responsable de son traitement..

3.3. Préparer un fil pour le HaMeR

L’UI ou le fil principal est le seul type de fil qui, par défaut, possède déjà un Gestionnaire, une Looper, et un MessageQueue. D'autres threads doivent être préparés avec ces objets avant de pouvoir fonctionner avec le framework HaMeR. Nous devons d’abord créer un Looper qui comprend déjà un MessageQueue et attachez-le au fil. Vous pouvez le faire avec une sous-classe de Fil, comme suit.

// Préparation d'un thread pour la classe HaMeR LooperThread extend Thread public Handler mHandler; public void run () // ajouter et préparer le Looper Looper.prepare (); // l'instance du gestionnaire sera associée à la boucle d'exécution du thread mHandler = new Handler () public void handleMessage (Message msg) // traite les messages entrants ici; // Lancement de la boucle de messages en utilisant le Looper Looper.loop (); 

Cependant, il est plus simple d'utiliser une classe d'assistance appelée HandlerThread, qui a un Looper et un MessageQueue construit en Java Fil et est prêt à recevoir un gestionnaire.

// La classe HandlerThread inclut une classe publique Looper en fonctionnement. HamerThread extend HandlerThread // vous devez simplement ajouter le gestionnaire Handler private Handler handler; public HamerThread (nom de chaîne) super (nom); 

4. Posting Runnables

le Runnable est une interface Java qui a de nombreux usages. On pourrait le comprendre comme une tâche unique à exécuter sur Fil. Il a une seule méthode qui doit être implémentée, Runnable.run (), pour effectuer la tâche.

// Déclaration d'une runnable exécutable r = new Runnable () @Override public void run () // la tâche est lancée ici;

Il y a plusieurs options pour poster un Runnable sur un Gestionnaire.

  • Handler.post (Runnable r): ajouter le Runnable au MessageQueue.
  • Handler.postAtFrontOfQueue (Runnable r): ajouter le Runnable à l'avant de la MessageQueue.
  • Gestionnaire.postAtTime (Runnable r, long timeMillis): ajouter le Runnable sur le MessageQueue être appelé à une heure précise.
  • Gestionnaire.postDelayed (Runnable r, long delay): ajouter le Runnable dans la file d'attente à appeler après un laps de temps déterminé.
// publiant un fichier Runnable sur un gestionnaire Gestionnaire gestionnaire = new Handler (); handler.post (new Runnable () @Override public void run () // tâche va ici);

Il est également possible d’utiliser le gestionnaire d’interface utilisateur par défaut pour poster un message. Runnable appel Activity.runOnUiThread ().

// publication de Runnable à l'aide de l'activité de gestionnaire d'interface utilisateur.runOnUiThread (new Runnable () @Override public void run () // tâche à effectuer);

Il est important de garder à l’esprit certaines choses à propos de Runnables. Contrairement à un Message, une Runnable ne peut pas être recyclé - une fois que son travail est terminé, il est mort. Comme il fait partie d’un package Java standard, un Runnable ne dépend pas de Gestionnaire et peut être appelé sur une norme Fil en utilisant le Runnable.run () méthode. Cependant, cette approche n'a rien à voir avec le framework HaMeR et ne partage aucun de ses avantages..

5. Envoi de messages

le Message objet définit un message contenant une description et des données arbitraires pouvant être envoyées et traitées via Gestionnaire. le Message est identifié avec un int défini sur Message.what (). le Message peut contenir deux autres int arguments et un Objet pour stocker différents types de données.

  • Message.quel: int identifier le Message
  • Message.arg1: int argument arbitraire
  • Message.arg2: int argument arbitraire
  • Message.obj: Objet pour stocker différents types de données  

Lorsque vous devez envoyer un message, au lieu de créer un message à partir de rien, il est recommandé de récupérer un message recyclé directement dans le pool global avec le message suivant: Message.obtain () ou Handler.obtainMessage () commandes. Il existe différentes versions de ces méthodes qui vous permettent d’obtenir une Message selon vos besoins.

Un usage commun de Handler.obtainMessage () est le moment où vous devez envoyer un message à un fil d’arrière-plan. Vous utiliserez le Gestionnaire associé à ce fil Looper obtenir un Message et l'envoyer au fil de fond, comme dans l'exemple ci-dessous.

int quoi = 0; String hello = "Bonjour!"; // Obtention du message associé au message de discussion en arrière-plan msg = handlerBGThread.obtainMessage (what, hello); // Envoi du message au thread d'arrière-plan handlerBGThread.sendMessage (msg); 

Il y a beaucoup de méthodes sympas sur le Message classe, et je vous conseille de regarder de plus près la documentation.

5.1. envoyer le message() Les options

De la même manière que nous pouvons poster Runnables, il y a plusieurs options à envoyer Messages:

  • Handler.sendMessage (Message msg): ajouter un Message au MessageQueue.
  • Handler.sendMessageAtFrontOfQueue (Message msg): ajouter un Message à l'avant de la MessageQueue.
  • Handler.sendMessageAtTime (Message msg, long timeInMillis): ajouter un Message à la file d'attente à un moment précis.
  • Handler.sendMessageDelayed (Message msg, long timeInMillis): ajouter un Message à la file d'attente après un laps de temps spécifique.

5.2. Traitement des messages avec le gestionnaire

le Message objets expédiés par Looper sont traités par le gestionnaire avec la méthode Handler.handleMessage. Tout ce que vous avez à faire est d’étendre la Gestionnaire classe et substitue cette méthode pour traiter les messages.

classe publique MessageHandler extend Handler @Override public void handleMessage (Message msg) switch (msg.what) // handle 'Hello' msg case 0: String hello = (String) msg.obj; System.out.println (bonjour); Pause; 

6. Conclusion

Le framework HaMeR peut aider à améliorer le code simultané de votre application Android. Cela peut sembler déroutant au premier abord par rapport à la simplicité d’un AsyncTask, mais l'ouverture de HaMeR peut être un avantage, si elle est utilisée correctement. 

Rappelles toi:

  • Handler.post () les méthodes sont utilisées lorsque les expéditeurs savent quelles opérations effectuer.
  • Gestionnaire.sendMessage() les méthodes sont utilisées lorsque le récepteur sait quelle opération effectuer.

Pour en savoir plus sur le threading dans Android, le livre Efficient Android Threading: Techniques de traitement asynchrone pour les applications Android de Anders Goransson.

6.1. Et après?

Dans le prochain tutoriel, nous allons continuer à explorer le framework HaMeR avec une approche pratique en construisant une application qui illustre différentes manières d'utiliser ce framework de concurrence Android. Nous allons créer cette application à partir de la base, en essayant différentes possibilités comme la communication entre Les fils, parler avec le fil de l'interface utilisateur, envoyer des messages et poster Runnables avec des retards. 

À bientôt!