Utiliser une ProgressDialog dans ses applications Android

| Axon de Tuto Mobile | Djug |

Le but de ce tutoriel est d'expliquer comment utiliser une ProgressDialog dans son application Android. Il peut être parfois utile d'afficher une barre de progression renseignant l'utilisateur sur l'avancement. Android fournit un moyen via la ProgressDialog pour les phases d'attente.
Commentez ♪

- Titre : Utiliser une ProgressDialog dans ses applications Android

- Auteurs : Axon de Tuto Mobile, Djug

- Parution : 16 mars 2011

- Licence : Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2011 Axon de Tuto Mobile. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Présentation

- II. Fonctionnement

- II-A. Cas Simple

- II-B. Afficher un Message

- III. Remerciements

- IV. Lien

I - Présentation

Qui dit longue opération dit souvent Thread. Or Android est connu pour bien différencier l'affichage du traitement (Modèle MVC) et seul le Thread propriétaire des objets graphiques (View) peut les modifier. En gros, lorsque votre Activity charge le layout, elle est la seule à pouvoir agir dessus. C'est ce que nous verrons dans la partie Fonctionnement.

II - Fonctionnement

II-A - Cas Simple

Prenons l'exemple d'une application ayant à effectuer deux opérations assez longues consécutives (doLongOperation1 et doLongOperation2). Elles sont exécutées dans un nouveau Thread, en plus du Thread de l'Activity, qui sera lui, démarré depuis l'Activity.
Au moment où ce second Thread est démarré, on obtient quelque chose du genre :

Ce qui correspond à un code comme celui-ci :

Exemple

private void compute() {
mProgressDialog = ProgressDialog.show(this, "Please wait",
"Long operation starts...", true);

new Thread((new Runnable() {
@Override
public void run() {
doLongOperation1();
doLongOperation2();
}
})).start();
// ...
}

II-B - Afficher un Message

Durant l'exécution des opérations, on peut imaginer que l'on ait besoin d'informer l'utilisateur sur le type d'opération en cours et donc mettre à jour le message de la ProgressDialog. Pour ce faire, on pourrait utiliser la méthode setMessage de ProgressDialog.

Ce qui correspond à un code comme celui-ci :

Exemple

private void compute() {
mProgressDialog = ProgressDialog.show(this, "Please wait",
"Long operation starts...", true);

new Thread((new Runnable() {
@Override
public void run() {
mProgressDialog.setMessage("Doing long operation 1...");
doLongOperation1();
mProgressDialog.setMessage("Doing long operation 2...");
doLongOperation2();
}
})).start();
// ...
}

Mais, comme nous l'avons indiqué précédemment, seul le Thread propriétaire des objets graphiques peut les modifier. L'objet mProgressDialog, créé dans le Thread de l'Activity, ne peut être modifié par un Thread annexe, au risque de lancer une CalledFromWrongThreadException avec le message au combien explicite :

Only the original thread that created a view hierarchy can touch its views.

Pour réaliser notre opération, il faut donc que le second Thread soit capable d'informer le Thread de l'Activity. C'est ce que propose la classe Handler avec le principe des objets Message qu'elle véhicule. En gros un Handler, possède une queue (une file) de messages qu'il dépile un à un. Ces messages sont constitués d'un identifiant (int what) et peuvent contenir des données. Il suffit de créer un objet de type Handler, et de l'utiliser pour préparer (appel à obtainMessage), envoyer (appel à sendMessage) et recevoir (override de handleMessage) les objets de type Message.

Pour notre exemple, on définira trois types de messages avec les trois identifiants suivants :

1. MSG_ERR : une erreur s'est produite, la ProgressDialog disparait et on informe l'utilisateur ;
2. MSG_IND : une indication en cours de traitement a besoin d'être donnée à l'utilisateur (via la ProgressDialog) ;
3. MSG_CNF : le traitement est terminé, on confirme à l'utilisateur que tout s'est bien passé (la ProgressDialog disparait).

Bref, dans le cas nominal, on obtient donc ce qui suit :

Exemple

package com.jde.tutorial;

import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.Toast;

public class ProgressBarActivity extends Activity {

protected ProgressDialog mProgressDialog;
private Context mContext;
private ErrorStatus status;

public static final int MSG_ERR = 0;
public static final int MSG_CNF = 1;
public static final int MSG_IND = 2;

public static final String TAG = "ProgressBarActivity";

enum ErrorStatus {
NO_ERROR, ERROR_1, ERROR_2
};

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mContext = this;

// registers the OnClickListener to our button
((Button) findViewById(R.id.btn))
.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
compute();
}
});
}

private void compute() {
mProgressDialog = ProgressDialog.show(this, "Please wait",
"Long operation starts...", true);

// useful code, variables declarations...
new Thread((new Runnable() {
@Override
public void run() {
Message msg = null;
String progressBarData = "Doing long operation 1...";

// populates the message
msg = mHandler.obtainMessage(MSG_IND, (Object) progressBarData);

// sends the message to our handler
mHandler.sendMessage(msg);

// starts the first long operation
status = doLongOperation1();

if (ErrorStatus.NO_ERROR != status) {
Log.e(TAG, "error while parsing the file status:" + status);

// error management, creates an error message
msg = mHandler.obtainMessage(MSG_ERR,
"error while parsing the file status:" + status);
// sends the message to our handler
mHandler.sendMessage(msg);
} else {
progressBarData = "Doing long operation 2...";
mProgressDialog.setMessage(progressBarData);

// populates the message
msg = mHandler.obtainMessage(MSG_IND,
(Object) progressBarData);

// sends the message to our handler
mHandler.sendMessage(msg);

// starts the second long operation
status = doLongOperation2();

if (ErrorStatus.NO_ERROR != status) {
Log.e(TAG, "error while computing the path status:"
+ status);
// error management,creates an error message
msg = mHandler.obtainMessage(MSG_ERR,
"error while computing the path status:"
+ status);
// sends the message to our handler
mHandler.sendMessage(msg);
} else {
msg = mHandler.obtainMessage(MSG_CNF,
"Parsing and computing ended successfully !");
// sends the message to our handler
mHandler.sendMessage(msg);
}
}
}
})).start();
// ...
}

/** fake operation for testing purpose */
protected ErrorStatus doLongOperation1() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
}
return ErrorStatus.NO_ERROR;
}

/** fake operation for testing purpose */
protected ErrorStatus doLongOperation2() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
}
return ErrorStatus.NO_ERROR;
}

final Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
String text2display = null;
switch (msg.what) {
case MSG_IND:
if (mProgressDialog.isShowing()) {
mProgressDialog.setMessage(((String) msg.obj));
}
break;
case MSG_ERR:
text2display = (String) msg.obj;
Toast.makeText(mContext, "Error: " + text2display,
Toast.LENGTH_LONG).show();
if (mProgressDialog.isShowing()) {
mProgressDialog.dismiss();
}
break;
case MSG_CNF:
text2display = (String) msg.obj;
Toast.makeText(mContext, "Info: " + text2display,
Toast.LENGTH_LONG).show();
if (mProgressDialog.isShowing()) {
mProgressDialog.dismiss();
}
break;
default: // should never happen
break;
}
}
};

}

C'est à travers la méthode surchargée handleMessage que le message est dépilé. On teste ensuite le type de message (what) et on applique le traitement désiré.

Note : dans la méthode handleMessage, on se trouve à nouveau dans le Thread de l'Activity, c'est donc pour ça que l'opération est possible.

Voilà ce que vous devriez avoir :

III - Remerciements

Je tiens à remercier tout particulièrement Feanorin qui a mis ce tutoriel au format Developpez.com.
Merci également à JF Jousseaume d'avoir pris le temps de le relire et de le corriger.

IV - Lien

Tutoriel origine sur Tuto Mobile