Introduction au traitement multithread et aux rappels

Le cours Développer des applications Android en Kotlin suppose que vous connaissez le concept et la terminologie du traitement multithread. Cette page fait office de présentation générale et de rappel.

Les appareils mobiles sont dotés d'un processeur. La plupart des appareils modernes en intègrent même plusieurs, chacun exécutant simultanément différents processus. C'est ce qu'on appelle le multitraitement.

Pour utiliser les processeurs plus efficacement, le système d'exploitation peut permettre à une application de créer plusieurs threads d'exécution au cours d'un processus. On appelle ce concept le multithread.

image

C'est un peu comme si vous lisiez plusieurs livres en même temps et passiez d'un livre à l'autre après chaque chapitre avant de les terminer tous, mais sans pouvoir lire plus d'un livre au même moment.

La gestion de tous ces threads demande un peu d'infrastructure.

image

Le programmeur prend en compte des éléments tels que les priorités, et s'assure que tous les threads s'exécutent et se terminent. Aucun livre n'est autorisé à rester indéfiniment sur l'étagère et à prendre la poussière, mais si un livre est très long ou peut attendre, il mettra un certain temps avant de vous être envoyé.

Le coordinateur configure les threads, c'est-à-dire qu'il vous envoie les livres que vous devez lire, et spécifie un contexte pour cette opération. Ce contexte pourrait s'apparenter à une salle de lecture dédiée. Certains contextes conviennent aux opérations liées à l'interface utilisateur, tandis que d'autres sont spécialisés dans la gestion des opérations d'entrée/sortie.

L'autre chose à savoir est qu'une application destinée aux utilisateurs dispose généralement d'un thread principal qui s'exécute au premier plan et peut coordonner d'autres threads fonctionnant en arrière-plan.

Sur Android, le thread principal est un thread unique qui gère toutes les mises à jour de l'interface utilisateur. Aussi appelé thread UI, c'est lui qui appelle tous les gestionnaires de clics, ainsi que d'autres rappels d'interface utilisateur et de cycle de vie. Le thread UI est le thread par défaut. Il englobe toutes les tâches effectuées par votre application, sauf si elle change explicitement de thread ou utilise une classe qui s'exécute sur un autre thread.

Ce mécanisme peut poser problème. En effet, le thread UI doit fonctionner de manière fluide pour garantir une expérience utilisateur de qualité. Pour que votre application s'affiche sur l'appareil de l'utilisateur sans aucune pause visible, le thread principal doit mettre à jour l'écran toutes les 16 millisecondes au plus, soit environ 60 images par seconde. À cette vitesse, le changement d'images est parfaitement fluide à l'œil. Cela fait beaucoup d'images dans un intervalle très court. Par conséquent, sur Android, l'important est d'éviter de bloquer le thread UI. Dans ce contexte, le terme blocage désigne le fait que le thread UI n'exécute aucune tâche alors qu'il attend un événement (par exemple, la fin de la mise à jour d'une base de données).

image

La plupart des tâches courantes (comme récupérer des données sur Internet, lire un fichier volumineux, ou écrire des données dans une base de données) prennent plus de 16 millisecondes. Par conséquent, appeler du code pour effectuer des tâches comme celles du thread principal peut entraîner la mise en pause, le stuttering voire le blocage de l'application. Si vous bloquez le thread principal trop longtemps, l'application risque même de planter et d'afficher une boîte de dialogue "L'application ne répond pas" (ANR).

Rappels

Vous disposez de plusieurs options pour effectuer des tâches à partir du thread principal.

Les rappels sont un modèle permettant de réaliser des tâches de longue durée sans bloquer le thread principal. Grâce aux rappels, vous pouvez démarrer ces tâches sur un thread en arrière-plan. Une fois la tâche terminée, le rappel (fourni en tant qu'argument) est appelé pour informer le code du résultat sur le thread principal.

Bien qu'ils soient pratiques, les rappels présentent cependant quelques inconvénients. Un code utilisant beaucoup de rappels peut devenir difficile à lire et peut compliquer le raisonnement. En effet, même si le code semble séquentiel, le code de rappel s'exécutera ultérieurement de manière asynchrone. De plus, les rappels ne permettent pas d'utiliser certaines fonctionnalités de langage, comme les exceptions.

Coroutines

En langage Kotlin, les coroutines permettent de gérer les tâches de longue durée de manière élégante et efficace. Les coroutines Kotlin permettent de convertir en code séquentiel un code basé sur des rappels. Le code écrit de manière séquentielle est généralement plus facile à lire et peut même utiliser des fonctionnalités de langage telles que les exceptions. Au final, les coroutines et les rappels remplissent la même fonction : attendre que le résultat d'une tâche de longue durée soit disponible et poursuivre l'exécution.