Dans quel thread faut-il exécuter ce code sous android ?

Lors de mes développements android, je suis tombé sur 3 exceptions levées par android pour indiquer l'exécution de code dans le mauvais thread.

Le premier cas concerne l'exécution d'une requête avec le client HTTP apache, fourni en standard avec android. La documentation android conseille d'utiliser la classe AsyncTask pour toutes les taches longues, afin de ne pas bloquer le thread principal (utilisé pour mettre à jour l'interface graphique). Une requête HTTP doit toujours être considérée comme une tache longue car une requête rapide à un instant donné peut prendre plus de temps en production (parce que le réseau ou le serveur peuvent avoir des charges plus importantes à certains moments). Android 3 (API 11) force cette bonne pratique en levant l'exception NetworkOnMainThreadException si vous utilisez le thread principal pour exécuter une requête HTTP.

Le second cas concerne la mise à jour de l'interface graphique dans le mauvais thread. Si vous êtes dans ce cas, alors android va lever l'exception CalledFromWrongThreadException (apparemment peu documentée dans la javadoc android) avec le message Only the original thread that created a view hierarchy can touch its views.. C'est ce qui m'est arrivé en appelant indirectement le code suivant depuis la méthode AsyncTask.doInBackground :

loginEditText.setText("");
passwordEditText.setText("");

Remarques :

  • loginEditText et passwordEditText sont des champs de type EditText
  • Le code s'exécute dans un thread créé par la classe AsyncTask. Si, au lieu d'utiliser AsyncTask, vous créez directement un nouveau thread ou faites appel à un composant qui le fait, vous aurez le même problème.

La solution est d'utiliser la méthode Activity.runOnUiThread de la manière suivante :

runOnUiThread(new Runnable() {
@Override
public void run() {
loginEditText.setText("");
passwordEditText.setText("");
}
});

Le troisième cas est une variante du précédent. Ici, il n'y a pas d'exception spécifique mais une RuntimeException avec le message moins explicite Can't create handler inside thread that has not called Looper.prepare() lorsque la méthode AlertDialog.Builder.html.create est appelé.

Lorsque cela m'est arrivé, j'ai remplacé le code suivant :

AlertDialog alertDialog = new AlertDialog.Builder(context).create();
...... (initialisation de la boite de dialogue)
alertDialog.show();

par celui-ci :

Activity activity = ......; // récupération de l'activité
activity.runOnUiThread(new Runnable() {
@Override
public void run() {
AlertDialog alertDialog = new AlertDialog.Builder(activity).create();
...... (initialisation de la boite de dialogue)
alertDialog.show();
}
});