sept
24
2010

[Tutoriel Android]Partie 9 – Les threads et boites de progression

Dans une application, nous avons souvent à faire à des processus plus ou moins longs, qui demandent à l’utilisateur de patienter durant le chargement des informations. Si nous effectuons le traitement d’une information à durée déterminée ou indéterminée sur notre application selon le temps que cela prendra, l’interface utilisateur sera bloquée ; ce qui est plutôt gênant et fait penser à celui-ci que l’application a subit un plantage interne. Pour éviter cela, nous pouvons mettre en œuvre les Thread qui permettent d’effectuer une tâche parallèle sans bloquer l’utilisateur.

Définition d’un thread  (voir le lien Wikipedia) :

http://fr.wikipedia.org/wiki/Thread_(informatique)

Grâce à un thread, nous pouvons exécuter une fonction sans bloquer notre utilisateur. Si cette exécution est nécessaire pour travailler, nous pouvons bloquer l’utilisateur et l’informer que le chargement des données est en cours et qu’il faut patienter, c’est là que la boite de dialogue est nécessaire. Celle-ci peut avoir un chargement indéterminé, ou déterminé avec le pourcentage d’avancement de notre tâche.

Nous allons commencer par créer notre projet sous Android 1.6 ou supérieur.

Utilisation d’un thread

Dans un projet, il y a plusieurs façons d’implémenter un Thread : la première consiste à utiliser un Thread avec un Runnable intégré et la seconde en implémentant directement le Runnable à notre classe.

Dans le cas d’utilisation de plusieurs Thread, l’implémentation d’un Runnable dans notre Thread est plus facile pour la compréhension ; dans le cas d’un seul Thread rajouté, l’interface Runnable à notre classe ne change que peu de chose, c’est au bon vouloir de chaque développeur.

Nous allons commencer par modifier notre layout de base “Main.xml” pour lui ajouter un bouton sur lequel nous cliquerons pour lancer notre tâche, puis on ajoute le listener pour l’évenement du click sur celui-ci dans notre code.

public class MonActivite extends Activity {

private Button monBouton;

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

monBouton = (Button) findViewById(R.id.MonBouton);

monBouton.setOnClickListener(new OnClickListener() {

@Override

public void onClick(View v) {

}

});

}

}

Continuons en rajoutant un nouvelle méthode à notre classe pour traiter des données, nous l’appellerons traitementDesDonnees;

@Override

public void onClick(View v) {

traitementDesDonnees();

}

et pour la méthode :

private void traitementDesDonnees() {

}

Nous allons modifier cette méthode pour implémenter un faux processus de chargement des données.

private void traitementDesDonnees() {

// Boucle de 1 a 10

for (int i = 0; i < 10; i++) {

try {

// Attends 500 millisecondes

Thread.sleep(500);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

Dans ce traitement, nous faisons une boucle de 1 à 10 et nous attendons 500 millisecondes entre chacune d’entre elle, exécutez cette application.

Lorsque l’on clique sur le bouton, le traitement s’effectue mais bloque l’interface de l’utilisateur et on ne peut rien faire, on a l’impression que le programme est planté.

Méthode bloquante

Nous allons donc utiliser un thread pour effectuer cette tâche, pour travailler en parallèle sans bloquer notre utilisateur. Pour cela, il suffit d’instancier un nouveau thread et de l’exécuter.

new Thread(new Runnable() {

@Override

public void run() {

}

}).start();

Voilà notre Thread qui implémente une interface Runnable et sa méthode surchargée run, puis on l’exécute avec un start().

Rajoutons dans notre run notre boucle de tout à l’heure.

private void traitementDesDonnees() {

new Thread(new Runnable() {

@Override

public void run() {

// Boucle de 1 a 10

for (int i = 0; i < 10; i++) {

try {

// Attends 500 millisecondes

Thread.sleep(500);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}).start();

}

Lancez l’application et voyez par vous même que l’interface utilisateur n’est plus bloquée.

Bon c’est bien, mais quand on reclique en boucle sur le bouton ça fait quoi ? et bien ça renvoie un traitement de données.

Nous allons avertir l’utilisateur qu’une tâche est donc en cours de traitement, grâce à la classe ProgressDialog.

On déclare notre ProgressDialog

private ProgressDialog progressDialog;

Dans notre méthode onCreate()

progressDialog = new ProgressDialog(this);

Puis dans notre méthode traitementDesDonnees()

private void traitementDesDonnees() {

// On ajoute un message à notre progress dialog

progressDialog.setMessage("Chargement en cours");

// On affiche notre message

progressDialog.show();

new Thread(new Runnable() {

@Override

public void run() {

// Boucle de 1 a 10

for (int i = 0; i < 10; i++) {

try {

// Attends 500 millisecondes

Thread.sleep(500);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

// A la fin du traitement, on fait disparaitre notre message

progressDialog.dismiss();

}

}).start();

}

Et voilà, lancez notre petit projet :

Chargement

Bon tout ça c’est bien mais il y a un hic : dans un thread, on ne peut pas modifier notre UI, vous pouvez toujours tenter de modifier le texte du bouton et vous vous retrouverez avec un Force Close et cette exception dans notre fenêtre DDMS :

ERROR/AndroidRuntime(993): Uncaught handler: thread Thread-9 exiting due to uncaught exception

ERROR/AndroidRuntime(993): android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

Comme le dit le message, on ne pas attaquer notre interface utilisateur lorsque l’on se retrouve dans un Thread.

Ne vous en faites pas, une méthode permet de changer ça facilement, une classe Handler existe, elle permet de prendre la main lorsqu’on lui demande pour afficher une donnée ou autre.

Rajoutons dans notre classe les données suivantes pour notre handler :

private Handler handler = new Handler() {

public void handleMessage(android.os.Message msg) {

if(msg.what == 0) {

monBouton.setText("C'est bon");

}

};

};

On déclare notre Handler, puis on ajoute la méthode handleMessage, celle-ci nous donne un message en retour dans lequel le what est notre valeur que nous lui avons envoyée, si celle-ci est égale à 0 alors nous modifions notre bouton.

On modifie notre méthode traitementDesDonnees()

private void traitementDesDonnees() {

// On ajoute un message à notre progress dialog

progressDialog.setMessage("Chargement en cours");

// On affiche notre message

progressDialog.show();

new Thread(new Runnable() {

@Override

public void run() {

// Boucle de 1 a 10

for (int i = 0; i < 10; i++) {

try {

// Attends 500 millisecondes

Thread.sleep(500);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

handler.sendEmptyMessage(0);

// A la fin du traitement, on fait disparaitre notre message

progressDialog.dismiss();

}

}).start();

}

Notez le handler.sendEmptyMessage(0); qui sera charger d’envoyer à notre handler la valeur 0 pour être traiter.

Lancez notre application.

Ca fonctionne

Et voilà notre bouton est bel et bien modifié.

Barre de progression

Dans notre projet, nous allons ajouter une barre de progression, cela peut être utile si nous connaissons la taille de l’information à charger.

Pour cela, nous allons rajouter une nouvelle méthode nommée traitementDesDonneesAvecPourcentage.

Nous allons reprendre le code de notre autre méthode traitementDesDonnees, mais nous allons modifier notre progressDialog comme ceci :

// On ajoute un message à notre progress dialog

progressDialog.setMessage("Chargement en cours");

// On donne un titre à notre progress dialog

progressDialog.setTitle("Mon chargement");

// On spécifie le style

progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);

// On spécifie le maximum

progressDialog.setMax(10);

// On affiche notre message

progressDialog.show();

Et après notre sleep, nous allons changer la valeur de la progression

// On change la valeur de la progression

progressDialog.setProgress(i+1);

Lancez l’application et voyez par vous même :

Barre de progression

Notre barre avance au fur et à mesure.

Nous en avons terminé pour aujourd’hui, bon codage à tous.

Les sources du projet : Sources

  • http://sitch.fr/ Igor

    Bonjour Pierre,
    Je crois que c’est la première fois que je post un commentaire, mais je veut te dire un grand merci pour tous tes tutos ! ça m’aide beaucoup !

    J’ai une petit problème (c’est pour ça le commentaire :P)
    je suis entrain de faire une application pour pouvoir écouter des musiques, et je bloque juste sur le bouton play, je veut que quand je clique sur play, le background devienne stop, et visse versa, mais aussi si j’appuye sur play l’action est mp.play(); et quand je reclique l’action soit mp.stop();

    Es-ce que vous pouvez m’aider ?

    Merci d’avance,
    Igor.
    PS: J’ai commencé a programmer sur java/Android samedi, et je suis encore un gros débutant !

  • http://sitch.fr/ Igor

    Ah! et pour les threads , c’est le meilleur tutoriel que j’ai trouvé ! En 2 minutes ! Et c’est déjà dans mon application !

  • http://www.ace-art.fr/wordpress/ Pierre-Emmanuel Mercier

    Ça a réglé ton soucis avec le MediaPlayer Android ?

  • http://sitch.fr/ Igor

    ?? j’ai pas compris ??

    J’ai toujours le même problème …

  • http://www.ace-art.fr/wordpress/ Pierre-Emmanuel Mercier

    Non je voulais savoir si les thread avaient réglés ton soucis que tu avais tout à l’heure.

  • http://sitch.fr/ Igor

    Non, ça n’a pas réglé mes problèmes, mais j’ai trouvé la solution ! :biggrin:

  • http://www.ace-art.fr/wordpress/ Pierre-Emmanuel Mercier

    Bon bah super alors si t’as trouvé la réponse :)

  • http://SiteWeb Raph

    Salut,

    comment arreter un thread ? Par exemple avec la touche Back?

    Merci

  • http://www.ace-art.fr/wordpress/ Pierre-Emmanuel Mercier

    Raph,

    Pour le bouton retour il existe une surcharge “onBackPressed”

    Pour stopper un thread on ne doit pas utiliser la méthode stop() de la classe Thread

    Ce lien te permettra de comprendre comment stopper un processus sans passer par cet méthode.

    http://java.developpez.com/faq/java/?page=langage_threads#LANGAGE_THREAD_terminer

  • http://constantimages.fr Tomsc

    Bonjour et merci pour ces tutos très pratiques.

    Mais j’ai une petite question : Comment faire pour passer un argument dans un thread?

    Merci ;)

  • http://www.ace-art.fr/wordpress/ Pierre-Emmanuel Mercier
  • Vivien-57

    Super Tuto encore une fois.

    Complet, facile d’utilisation et de compréhension.

    Chapeau.