Blog

Exécuter du code en arrière plan sous Android

Xavier2013-MD
Share Button

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.


 
 
 
Je teste mon niveau technique sur Android en quelques minutes!
 

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.

    // 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();
    }

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.

    /**
     * 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) {
        }
    }

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 :

    /**
     * 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);

        }
    }

Voici ensuite comment appeler ce service :

    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);

    }

 
Je teste mon niveau technique sur Android en quelques minutes!
 

Cet article vous a plu ? Vous aimerez sûrement aussi :

 

XavierXavier
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 +

 

Share Button

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Vous pouvez utiliser ces balises et attributs HTML : <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>