Passer au contenu principal

Pour toute application interactive, quel que soit le support, il est toujours vital de ne jamais effectuer de longs calculs sur le thread principal, afin de ne pas geler l’interface.

Les applications Android étant la plupart du temps basées sur Java, il est tout a fait possible de ne se baser que sur les outils classiques prévus à cet effet. Cependant Android possède une architecture qui fournit de nombreux outils, notamment pour permettre une communication efficace entre les taches de fond et le thread principal, pour remonter des événements à l’utilisateur par exemple.

Loopers, Handlers et Messages

Les Loopers (littéralement boucleurs) sont des objets qui possèdent une liste de messages, et qui vont, sur un thread donné, distribuer les messages à leur destinataires.

Un Looper peut être créé sur n’importe quel thread (et notamment sur le thread principal).

Les Handlers sont les destinataires des messages gérés par les Loopers. Un Handler va toujours être lié à un Looper spécifique, et donc à un thread donné. On peut donc facilement créer un Handler qui recevra toujours ses messages sur le thread principal.

Il est possible, via le Handler, de distribuer un message immédiatement, mais aussi après un certain délai, ou à un moment donné, et enfin de poster un Runnable qui sera exécuté automatiquement.

Les Messages sont des objets simples, contenant principalement un identifiant (appelé what) numérique indiquant le type du message, ainsi qu’un Object, permettant de définir le contenu du message.

[java] // Les identifiants des messages
static final int WHAT_RESULT = 42;
static final int WHAT_ERROR = 666;

// Définition du Handler
Handler handler = new Handler {

@Override
public void handleMessage(final Message msg) {
super.handleMessage(msg);

switch (msg.what) {
case WHAT_RESULT:
// …
break;
case WHAT_ERROR:
// …
break;
}
}
}

// Le runnable qui va exécuter la tâche de fond
Runnable background = new Runnable() {

@Override
public void run() {

try {
// …
Message msg = handler.obtainMessage(WHAT_RESULT, "Result");
msg.sendToTarget();
}
catch (Exception e) {
Message msg = handler.obtainMessage(WHAT_ERROR, e);
msg.sendToTarget();
}
}
};

// La méthode pour démarrer la tâche de fond
void startProcess() {
new Thread(background).start();
}
[/java]

AsyncTask

La classe AsyncTask est un outil très pratique pour créer une tâche asynchrone sur Android. Une classe héritant d’AsyncTask définit un type d’entrée, un type de retour, et éventuellement un type utilisé comme progression. Cette classe fournit de nombreuses méthodes appelées à diverses étapes de la vie d’une AsyncTask, la plupart étant appelée sur le thread principal.

Une AsyncTask doit en général n’être utilisée que pour exécuter des tâches rapides. En effet, la tâche asynchrone va très souvent être liée à une activité qui peut disparaître assez rapidement (soit parce que l’utilisateur va changer d’application, parce qu’il va recevoir un appel, ou simplement en tournant son téléphone en mode paysage).

Un autre désavantage est qu’une AsyncTask, par défaut, n’a pas de Listener pour propager des événements en dehors de la classe elle même. Cela implique souvent de devoir créer soi-même les interfaces nécessaires, ou de manipuler l’UI directement depuis l’AsyncTask, ce qui n’est pas idéal.

[java] /**
* Une tâche asynchrone, prenant une liste de fichiers en paramètres,
* retournant un String, et ayant une progression sous forme de nombres entiers
*/
public class MyAsyncTask extends AsyncTask<File, Integer, String> {

/**
* La méthode effectuant le processus. Cette méthode est toujours appelée sur
* un thread en arrière plan.
*
* Pour l’exemple, cette méthode écrit juste le nom des fichiers dans un String
*/
@Override
protected String doInBackground(final File… params) {

int count = params.length;
StringBuilder builder = new StringBuilder();

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

publishProgress(i);

builder.append(params[i].getName());
builder.append("\n");
}

return builder.toString();
}

/**
* Méthode appelée sur le thread principal, avant d’appeler doInBackground
*/
@Override
protected void onPreExecute() {
}

/**
* Méthode appelée sur le thread principal, déclenché par l’appel à publishProgress
*/
@Override
protected void onProgressUpdate(final Integer… values) {

}

/**
* Méthode appelée sur le thread principal, après que l’exécution de doInBackground soit
* terminée
*/
@Override
protected void onPostExecute(final String result) {
}
}
[/java]

Service

L’un des points forts d’Android est son système d’intercommunication entre applications au moyen des Intents. Les Intents permettent de définir un besoin (une intention d’action), besoin qui peut être explicite (ouvrir l’application X) ou implicite (partager une image).

Les Intents permettent donc de démarrer des Activités (la partie visible d’une application) ou des Services (qui sont en général les tâches de fond). Enfin, les Intents peuvent servir à diffuser des événement, reçu par un BroadcastReceiver.

Un service va pouvoir se créer comme ceci :

[java] /**
* Un IntentService, contrairement a un Service classique, est toujours executé sur un nouveau
* thread.
*/
public class MyService extends IntentService {

public MyService() {
super("My Service");
}

@Override
protected void onHandleIntent(final Intent intent) {

// calculs en arrière plan
// …

// Diffusion d’un message global reçu par le BroadcastReceiver
Intent message = new Intent("com.example.myapp.EVENT_DONE");
sendBroadcast(message);

}
}
[/java]

Voici ensuite comment appeler ce service :

[java] BroadcastReceiver myBroadcastReceiver = new BroadcastReceiver() {

@Override
public void onReceive(final Context context, final Intent intent) {
// Faire quelquechose au résultat
Log.i("myBroadcastReceiver", "onReceive");
}
};

void startBackgroundService() {

// enregistrement du BroadcastReceiver
IntentFilter filter = new IntentFilter("com.example.myapp.EVENT_DONE");
context.registerReceiver(myBroadcastReceiver, filter);

// lancement du service
Intent intent = new Intent(context, MyService.class);
context.startService(intent);

}
[/java] Cet article vous a plu ? Vous aimerez sûrement aussi :

Xavier
Geek, Android-ophile, curieux de nature et aimant partager mes connaissances, je baigne dans le monde de la programmation depuis un bon moment, et aujourd’hui je suis principalement porté sur les technologies mobiles, et principalement Android.

J’apprécie autant découvrir de nouvelles techniques, développer des bouts de codes pour générer des images, échanger sur divers sujets techniques, que partager mes connaissances par quelque moyen que ce soit.

Aujourd’hui, je passe mon temps entre Deezer, où je fais partie de l’équipe Android, mes propres applications Android Open Sources, et mes trajets quotidiens en train, entre ma campagne percheronne et Paris.
En savoir +

Rejoignez la discussion Un commentaire

  • poldo dit :

    salut et merci a toi Xavier pour se merveilleux tuto ,en effet suis en train de bosser sur un projet androïde en rapport avec l’iot et j’aimerais pouvoir cliquer sur un bouton pour qu’il exécute une URL en arrière plan ,en effet jusqu’à présent quand je clique sur un bouton il ouvre un navigateur or j’aimerais qu’il puisse fais cella de sorte que l’utilisateur ne se rende pas conte c.a.d en arrière plan

Laisser un commentaire