Android, A Complete Course, From Basics to Enterprise Edition

Threads, AsyncTask et Handler.

Android, A Complete Course, From Basics to Enterprise Edition. 1

Threads, AsyncTask et Handler. 1

Le cœur du système. 1

1       Gérer les activités. 1

1.1         Les Threads. 1

 Cet article est extraie du livre « Android, A Complete Course », disponible sur Android2ee.com.

Les exemples ou les programmes présents dans cet ouvrage sont fournis pour illustrer les descriptions théoriques. Ce code est libre de toute utilisation mais n'est pas distribuable.

La distribution du code est reservée au site :

http://android2ee.com.

La propriété intellectuelle du code appartient à :

Mathias Séguy

L’utilisation de ces codes est sous votre unique responsabilité et personne d’autre que vous ne pourra être tenu responsable des préjudices ou dommages de quelques natures que ce soit pouvant résulter de son utilisation.

Tous les noms de produits ou marques cités dans cet ouvrage sont des marques déposés par leurs propriétaires respectifs.

Publié par http://android2ee.com

Titre Original : Android, A Complete Course, From Basics to Enterprise Edition. Édition Française.

ISBN : 979-10-90388-00-0

Copyright © 2011 by Mathias Séguy

 

Aucune représentation ou reproduction, même partielle, autre que celles prévues à l’article L. 122-5 2° et 3° a) du code de la propriété intellectuelle ne peut être faite sans l’autorisation expresse de Mathias Seguy ou, le cas échéant, sans le respect des modalités prévues à l’article L. 122-10 dudit code

Le cœur du système

1         Gérer les activités

1.1       Les Threads

Si une activité réagit en plus de 5 secondes, elle sera tuée par l’ActivityManager qui la considèrera comme morte. Les IHMs de l’activité sont dans une Thread qui lui est propre (comme en swing). Plusieurs façons d’interagir entre les threads en arrière plan et la thread d’IHM sont possibles.

Mais surtout garder toujours en tête qu’aucun traitement ne doit être effectué dans cette thread !

 

1.1.1       Les Handlers

L’Handler est associé à l’activité qui le déclare et travaille au sein de la thread d’IHM. Ce qui signifie que tout traitement effectué par le Handler gèle l’IHM le temps qu’il soit effectué. Il faut donc considérer le Handler comme celui qui met à jour l’IHM, la thread qui appelle le Handler a à charge le traitement. Le Handler ne doit que mettre à jour l’IHM, tout autre comportement est une erreur de conception.

Une thread communique avec un Handler au moyen de message (ou de runnable mais ce ne sera pas ici expliqué). Pour cela :

·         La thread récupère l’objet Message du pool du Handler par handler.obtainedMessage. Cette méthode peut être surchargée de manière à envoyer plus d’informations au Handler (en lui passant d’autres paramètres).

·         La thread envoie le message au Handler en utilisant l’une des méthodes suivantes :

o   sendMessage (envoie le message et le place à la fin de la queue)

o   sendMessageAtFrontOfQueue (envoie le message et le place au début de la queue)

o   sendMessageAtTime (envoie le message au moment donné en paramètre et le place à la fin de la queue)

o   sendMessageDelayed (envoie le message après un temps d’attente passé en paramètre et le place à la fin de la queue)

·         Le Handler doit surcharger sa méthode handleMessage pour répondre aux messages qui lui sont envoyés.

 

Thread I.H.M

Autre Thread

Activity

 

sendMessage()

Handler

handleMessage()

Thread

 

sendMessage()

SendMessage

 

Exemple :

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

      android:orientation="vertical"

      android:layout_width="fill_parent"

      android:layout_height="fill_parent"

      >

      <ProgressBar android:id="@+id/progress"

            style="?android:attr/progressBarStyleHorizontal"

            android:layout_width="fill_parent"

            android:layout_height="wrap_content" />

</LinearLayout>

 

Le code java

/**

 * @author mSeguy

 * @goals This class aims to show how to use handler and thread

 */

public class HandlerTuto extends Activity {

    /**     * The progress bar to update     */

    ProgressBar bar;

    /**     * The progress bar increment stored in the bundle of the handler's message     */

    private final String PROGRESS_BAR_INCREMENT="ProgreesBarIncrementId";

    /**     * The handler that manage the communication from external threads to Gui's thread     */

    Handler handler = new Handler() {

        @Override

        public void handleMessage(Message msg) {

            int progress=msg.getData().getInt(PROGRESS_BAR_INCREMENT);

            // here, just increment the progress bar

            bar.incrementProgressBy(progress);

            // You can do quick stuff that update the Gui

           

        }

    };

    /**     * An atomic boolean to manage the external thread's destruction     */

    AtomicBoolean isRunning = new AtomicBoolean(false);

    /**     * An atomic boolean to manage the external thread's pausing     */

    AtomicBoolean isPausing = new AtomicBoolean(false);

 

    // Create the activity

    @Override

    public void onCreate(Bundle icicle) {

        super.onCreate(icicle);

        setContentView(R.layout.main);

        // Define the progress bar

        bar = (ProgressBar) findViewById(R.id.progress);

        bar.setMax(210);

    }

 

    /** Called method when launching the activity */

    public void onStart() {

        super.onStart();

        // initialize the progress bar

        bar.setProgress(0);

        // Thread definition, it could have been done in an external classes

        Thread background = new Thread(new Runnable() {

            /**

             * The bundle exchanged within the message between the tread and the handler

             */

            Bundle messageBundle=new Bundle();

            /**

             * The message exchanged between this thread and the handler

             */

            Message myMessage;

            // Overriden Run method

            public void run() {

                try {

                    // If isRunning is false, the run method will end

                    for (int i = 0; i < 20 && isRunning.get(); i++) {

                        // while on Pause but still alive

                        while (isPausing.get() && (isRunning.get())) {

                            // Make a pause

                            Thread.sleep(1000);

                        }

                        // Sleep one second

                        Thread.sleep(1000);

                        // Send the message to the handler (the handler.obtainMessage is more

                        // efficient that creating a message from scratch)

                        //create a message, the best way is to use that method:

                        myMessage=handler.obtainMessage();   

                        //then, if you want add data to your message (you could use arg0 and arg1 too)

                        messageBundle.putInt(PROGRESS_BAR_INCREMENT, i);

                        myMessage.setData(messageBundle);

                        //then send the message

                        handler.sendMessage(myMessage);

                    }

                } catch (Throwable t) {

                    // just end the background thread

                }

            }

        });

        //Initialize the threadSafe booleans

        isRunning.set(true);

        isPausing.set(false);

        //And launch the thread

        background.start();

    }

   

 /************************************************************************************/

/** Managing life cycle *******************************************************************/    /**************************************************************************************/

    //Method called when activity stop

    public void onStop() {

        super.onStop();

        //Update the boolean to kill the thread

        isRunning.set(false);

    }

 

    /* (non-Javadoc)

     * @see android.app.Activity#onPause()

     */

    @Override

    protected void onPause() {

        super.onPause();

        //Update the boolean to pause the thread

        isPausing.set(true);

    }

 

    /* (non-Javadoc)

     * @see android.app.Activity#onResume()

     */

    @Override

    protected void onResume() {

        super.onResume();

        //Update the boolean to resume the thread

        isPausing.set(false);

    }

 

}

1.1.2       Suis-je dans la thread de l’IHM ou pas ?

La classe Activity possède la méthode runOnUIThread pour lancer un traitement dans la thread de l’IHM.

1.1.3       La désynchronisation

Depuis la version 1.5 d’Android, l’AsyncTask permet une nouvelle façon d’effectuer des tâches en arrière plan.

Pour cela :

·         Créer une sous-classe d’AsyncTask (elle peut être interne et privée à l’activité).

·         Redéfinir une ou plusieurs de ces méthodes pour spécifier son travail.

·         La lancer au moyen de sa méthode execute

La dérivation d’une classe AsyncTask n’est pas triviale, elle est générique et à paramètres variables. Pour la généricité elle attend 3 paramètres :

·         Le type de l’information qui est nécessaire au traitement (dans l’exemple URL)

·         Le type de l’information qui est passé à sa tache pour indiquer sa progression (dans l’exemple : Integer)

·         Le type de l’information passé au code lorsque la tâche est finie (dans l’exemple Long).

 

Un exemple :

private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {

    protected Long doInBackground(URL... urls) {

        int count = urls.length;

        long totalSize = 0;

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

            totalSize += Downloader.downloadFile(urls[i]);

            publishProgress((int) ((i / (float) count) * 100));

        }

        return totalSize;

    }

 

    protected void onProgressUpdate(Integer... progress) {

        setProgressPercent(progress[0]);

    }

 

    protected void onPostExecute(Long result) {

        showDialog("Downloaded " + result + " bytes");

    }

}

Les étapes d’AsynchTask :

doInBackground est la méthode qui s’exécute dans une autre thread. Elle reçoit un tableau d’objets lui permettant ainsi d’effectuer un traitement en série sur ces objets. Seule cette méthode est exécutée dans une thread à part, les autres méthodes s’exécutent dans la thread de l’IHM.

onPreExecute est appelée par la thread de l’IHM avant l’appel à doInBackground, elle permet de pré-initialiser les éléments de l’IHM.

onPostExecute est appelée lorsque la méthode doInBackground est terminée.

onProgressUpdate est appelée par la méthode publishProgress à l’intérieur de la méthode doInBackground.

 

Other Thread

Thread I.H.M.

Activity

MyAsyncTask extends AsyncTask

doInBackground()

onProgressUpdate()

 

OnPostExecute()

 

update

Usual communication

update

Exemple :

/**

 * @author mathias seguy

 * @goals This class aims to show a simple usage of the AsyncTask

 */

public class AsyncTuto extends Activity {

    /**    * The progress bar to update     */

    ProgressBar bar;

 

    @Override

    public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.main);

        // Define the progress bar

        bar = (ProgressBar) findViewById(R.id.progress);

        bar.setMax(210);

        // Launch the asynchTask

        new MyAsyncTask().execute();

    }

 

    /**

     * This class aims to show a simple derivation of the AsyncTask

     */

    class MyAsyncTask extends AsyncTask<Void, Integer, String> {

        /**     * A simple counter */

        Integer count = 0;

 

        // override of the method doInBackground (the one which is running in a separate thread)

        @Override

        protected String doInBackground(Void... unused) {

            try {

                while (count < 20) {

                    //increment the counter

                    count++;

                    // Make a pause

                    Thread.sleep(1000);

                    //talk to the onProgressUpdate method

                    publishProgress(count);

                }

            } catch (InterruptedException t) {

                // just end the background thread

                return ("The sleep operation failed");

            }

            return ("return object when task is finished");

        }

 

        // override of the onProgressUpdate method (runs in the GUI thread)

        @Override

        protected void onProgressUpdate(Integer... diff) {

            bar.incrementProgressBy(diff[0]);

        }

 

        // override of the onPostExecute method (runs in the GUI thread)

        @Override

        protected void onPostExecute(String message) {

            Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();

        }

    }

}

 

1.1.4       AsyncTask ou Handler

Ces deux méthodes ont pour but d’effectuer des traitements liés aux IHMs dans des threads indépendantes de celle de l’IHM. La question est de savoir laquelle choisir dans quelle circonstance.

L’idée est que si vous faites un traitement lourd, spécifique, qui n’a besoin que de donner son avancement et sa fin (typiquement charger des données), le mieux est l’utilisation de l’AsyncTask. A contrario, si vous avez besoin d’établir une communication avec l’IHM, il est plus pertinent d’utiliser un Handler (l’exemple de la communication Bluetooth entre deux appareils utilise ce pattern).