Note : Ce post fait office d'introduction à une suite d'articles traitant de la programmation concurrente ou multithreads sous Android. Contrairement aux prochaines parties, vous ne trouverez ici que peu de notions techniques. J'espère rendre disponible les parties suivantes très rapidement sur ce blog. Elles aborderont à la fois les techniques de programmation disponibles ainsi que les outils d'aide au développement.
Note : L'intégralité des techniques décrites ci-dessous et dans les articles de la série est expliqué de façon plus détaillée dans le livre Développez pour Android. Cet ouvrage, co-écrit avec Ludovic Perrier, aborde le développement Android de façon extrêmement pratique et ludique. On y retrouve, par exemple, de nombreuses portions de code permettant d'éclaircir les propos tenus dans le bouquin. Que vous soyez débutant ou expert Android, je vous encourage vivement à vous procurer ce livre sur le site de Digit Books_
Introduction
Quel utilisateur Android n'a jamais eu à faire à la boite de dialogue ci-dessous ?
Cette boite de dialogue, appelée Application Not Responding (qui, post en français oblige, se traduit en “application ne répondant pas”) ou de façon plus contractée ANR, informe l'utilisateur que l'application ne semble plus répondre. Dès lors que cette pop-up est affichée dans une application, deux choix se présentent à l'utilisateur :
Attendre en espérant que l'application “reprenne ses esprits” et commence à répondre …
Forcer la fermeture de l'application. Ce choix radical consiste à tuer le processus dans lequel s'exécute l'application. Les données non sauvegardées sont alors perdues et l'exécution du programme s'arrête purement et simplement.
En tant que développeur Android, vous vous devez de fuir l'ANR comme la peste ! Pourquoi ? Tout simplement parce que c'est le démon. Plus sérieusement, les ANRs sont générés par le système pour éviter les applications lentes et ne répondant pas. C'est en quelque sorte un système de protection contre les mauvaises applications et donc les mauvais développeurs… Une pop-up ANR s'affiche au bout de 5 secondes lorsque votre application ne répond pas à un évènement utilisateur (appui sur l'écran, une touche, etc.) ou si votre BroadcastReceiver
prend plus de 10 secondes à s'exécuter. Face à “l'agressivité” des ANR, on peut donc facilement conclure :
Plus les ANRs sont fréquents dans une application, moins elle a de chance d'être conservée par les utilisateurs
ANR
Pour mieux comprendre les raisons de l'apparition de cette horrible pop-up il est nécessaire de mieux appréhender le système de fonctionnement d'Android et notamment comment une application s'exécute. Lorsque l'utilisateur démarre une application, Android lance un nouveau processus (processus au sens système du terme). Dans ce processus une instance de Dalvik VM est démarrée et initialise, elle même, un Thread
(au sens Java du terme). Ce dernier, appelé souvent main thread, thread principal ou UI thread est en quelque sorte la racine de votre application…
Le thread principal est celui dans lequel la grande majorité de votre code ET du code du système s'exécute. A titre d'exemple, l'animation d'une ListView
venant d'être “jetée” s'effectue par appel successifs à la méthode onDraw(Canvas)
. Ces appels s'effectuant dans le main thread, un blocage de ce dernier provoque irrémédiablement une sensation de saccade ou de blocage de la ListView
pour l'utilisateur. De la même façon, une ProgressDialog
indéterminée stoppera son animation si un accès réseau s'effectue en même temps dans le UI thread.
Pour faire en sorte que votre application soit la plus fluide et la plus réactive possible, il est donc nécessaire de limiter au maximum l'impact sur le thread principal. Dès lors qu'une opération peut potentiellement être longue, il faut donc privilégier l'utilisation d'un nouveau Thread
distinct du main thread. C'est dans ce thread secondaire que votre opération s'exécutera.
Une opération longue c'est quoi ?
Définir la notion “d'opération longue” est quelque peu hasardeux. A vrai dire, il n'y a pas de définition précise de cette expression mais on considère généralement que les actions suivantes sont longues et ne doivent donc pas s'exécuter dans le thread principal :
Entrées/sorties réseau : Tout accès au réseau tel qu'une requête à des webservices doit se faire dans un thread séparé du main thread. En effet, l'ouverture d'une connexion HTTP suivi de la lecture des données peut prendre énormément de temps sur un terminal contraint. Il convient surtout de noter que l'ensemble des APIs disponibles dans le framework Android (
java.net
,org.apache.http
) effectuent les requêtes de façon synchrone et bloquante.Entrées/sorties disque : Face à la disparité des terminaux Android, il est préférable de ne pas faire d'hypothèses sur la performance du système de fichiers disponible. Les temps d'accès disque dépendent de nombreux paramètres : type de mémoire (RAM, SD-Card, etc.), type de système de fichier, etc. et sont alors extrêmement variables. De nombreux terminaux Android utilisent par exemple des mémoires aux accès concurrents limités. Un processus A utilisant la mémoire peut donc rapidement bloquer un processus B souhaitant y accéder. Considérez donc que TOUT accès disque doit se faire dans un thread séparé : lecture d'une image sur la carte SD, écriture d'un fichier de sauvegarde sur le disque, etc..
Calculs longs : C'est le cas lorsque vous souhaitez effectuer un calcul potentiellement couteux comme le calcul des 15000 premières décimales de pi. On pourrait imaginer que le parsing d'un fichier (XML, JSON, etc.) de taille importante fait également partie de cette catégorie.
Accès matériel : Certains modules matériels de votre terminal peuvent demander un temps d'initialisation important. C'est par exemple le cas pour la méthode
open(int)
de la classeCamera
. N'hésitez pas à effectuer l'appel à ce type de méthodes dans un thread secondaire.
De manière générale, l'utilisation de techniques de programmations concurrentielles dans les cas cités précédemment minimise et même supprime les problèmes d'ANR. Le seul point noir réside dans le fait que ce type de programmation (plus technique que la programmation mono-thread) fait souvent peur aux développeurs. Fort heureusement, le framework Android fournit un ensemble de solutions facilitant la vie du développeur dans la réalisation de ce genre d'algorithme … C'est ce que nous aborderons dans de futures parties. Repassez vite !