Android UIThread Handler et Async Task jeanmichel Douin





































































































- Slides: 101
Android UIThread, Handler et Async. Task jean-michel Douin, douin au cnam point fr version : 13 Novembre 2019 Notes de cours Android_Thread et + 1
Bibliographie utilisée http: //developer. android. com/resources/index. html http: //developer. android. com/reference/android/os/Async. Task. html Un ensemble de tutoriels à lire http: //www. vogella. de/android. html http: //www. vogella. de/articles/Android. Performance/article. html Strict. Mode (intégré en 4. 0) http: //android-developers. blogspot. com/2010/12/new-gingerbread-api-strictmode. html http: //blog. akquinet. de/2010/02/17/android-activities-the-predominance-of-the-ui-thread/ Android_Thread et + 2
Pré-requis • Les threads en java – java. lang. Thread – java. lang. Runnable Android_Thread et + 3
Sommaire • UIThread déclenche une activity et gère l’IHM – Les appels des « on. XXXX » c’est lui • Concurrence et échanges avec l’UIThread – Thread, Handler, du java traditionnel – Async. Task, adapté et créé pour se passer du java traditionnel Android_Thread et + 4
UIThread • Gère l’affichage de l’Activity : – Prise en compte des évènements de l’utilisateur • Clic, déplacements, … • … – Exécution des Listeners associés • … • L’UIThread est le seul Thread agréé – Pour l’affichage au sein d’une activité • Comme l’accès à l’un des composants graphiques – Les évènements liés à l’affichage sont gérés par une file • Alors – Toutes les opérations de mises à jour, modifications de l’IHM doivent s’effectuer depuis cet UIThread Android_Thread et + 5
En « rappel » • Une application engendre un processus linux à ce processus est associé une DVM cette DVM installe des Threads • Parmi ces Threads, – Un thread est nommé main alias UIThread • Le thread main alias UIThread – – Crée, initialise l’activité, Déclenche les méthodes selon le cycle de vie, Et est chargé de l’affichage, De la prise en charge des actions de l’utilisateur Android_Thread et + 6
Exemples, et démonstration • Exemples à ne pas suivre … • Exemple 1 – La méthode on. Create contient un appel à Thread. sleep(30000) ! • Un affichage est souhaité • Thread. sleep(30000); – Ou System. Clock. sleep(30000); • -> ce n’est qu’ au bout de 30000 ms que l’affichage se produit !! – Cycle de vie … rappel Android_Thread et + 7
En « rappel » le cycle de vie • Thread. sleep(30000); Android_Thread et + 8
Exemples suite, et démonstrations • Exemple 2 – Au clic sur un bouton : • Thread. sleep est appelée … – Ou bien System. Clock. sleep • Un affichage est souhaité … Android_Thread et + 9
Exemple à ne pas suivre : une IHM au ralenti • Un clic déclenche la méthode faire. Quelque. Chose – L’UIThread s’endort … une seconde, deux secondes … et pas d’affichage… Android_Thread et + 10
IHM: affichage seulement au bout de 5 sec. • C’est seulement au bout de 5 secondes – Qu’un nouveau clic devient possible, – Que la jauge est accessible (répond aux sollicitations de l’utilisateur) – L’affichage n’est pas effectué • toutes les secondes comme la lecture du source le laisse supposer • Démonstration de l’exemple à ne pas suivre – Un seul affichage (visible) au temps écoulé : 5 sec ! Android_Thread et + 11
Démonstration • Ici 5 secondes et avec appels de Log. i – Toujours rien à l’écran, sauf au bout de 25 sec. – Les affichages ne sont pas pris en compte, l’UIThread passe son temps à s’endormir et en conséquence ne permet pas la prise en compte des souhaits d’affichage (set. Text) Android_Thread et + 12
L’UIThread gère l’IHM et seulement • Une première conclusion, par la force des choses… – Toutes les opérations coûteuses en temps d’exécution doivent être placées dans un autre Thread, • mais pas n’importe lequel…(l’UIThread est réservé à l’IHM) • Ce qui arrive quand l’UIThread est bloqué plus de 5 sec. , • Android considère que votre application ne répond plus cf. ANR, Application Not Responding Android_Thread et + 13
Rappel : les Threads en java • Rappels: les 5 prochaines diapositives peuvent être optionnelles • Un Thread en java, – une classe, une interface, deux méthodes essentielles • La classe java. lang. Thread • L’interface java. lang. Runnable • Les deux méthodes – start éligibilité du Thread, – run le code du Thread » issue de public interface Runnable{void run(); } Android_Thread et + 14
Contexte : Quasi-parallèle t 1 t 2 t 3 vue logique t 1 t 2 t 3 vue du processeur activité du processeur t 1, t 2, t 3 sont des « Threads » Android_Thread et + ordonnancement 15
Rappel : La classe java. lang. Thread Syntaxe : Création d ’une nouvelle instance Thread un. Thread = new T (); … T extends Thread • (un(e) Thread pour processus allégé. . . ) • « Démarrage » du thread – un. Thread. start(); • éligibilité de Un. Thread • « Exécution » du thread – L ’ordonnanceur interne déclenche la méthode run() (un. Thread. run(); ) Android_Thread et + 16
Rappel : java. lang. Thread, syntaxe public class T extends Thread{ public void run(){ // traitement } } Thread t = new T(); t. start(); // autre syntaxe, à l’aide d’une classe anonyme Thread t = new Thread( new Runnable(){ public void run(){ // traitement } }); t. start(); Android_Thread et + 17
Runnable, autre syntaxe Runnable r = new Runnable(){ public void run(){ // traitement } }; new Thread(r). start(); Android_Thread et + 18
Reprenons l’exemple à ne pas suivre • Une « solution » serait de créer un Thread qui s’endort pendant 5 * 1 sec • Au clic un Thread est créé UIThread Un Thread on. Click Android_Thread et + 19
Au clic un thread est créé UIThread on. Click Un Thread • faire quelque chose engendre un thread – UIThread devient disponible pour les sollicitations de l’utilisateur, Sur cet exemple le curseur de la jauge devient accessible Android_Thread et + 20
S’endormir dans un autre Thread Android_Thread et + 21
Démonstration • C’est mieux, – Le clic devient possible, – La jauge répond aux sollicitations avant les 5 secondes • Mais Android_Thread et + 22
Mais – Plus d’affichage du tout … – Cet autre Thread ne doit pas tenter de modifier l’IHM, » Nul ne peut se substituer à l’UIThread … » Comportement incertain … Android_Thread et + 23
Première solution Création d’un Thread + Accès prédéfinis à l’UIThread – envois de séquences d’affichage • run. On. UIThread – envois de messages • android. os. Handler, – Méthodes post, send, handle. Message … Android_Thread et + 24
Accès à l’UIThread • Envois de séquences d’affichage – run. On. Ui. Thread (Runnable r){… • Méthode héritée de la classe Activity – Ou bien une instance de la classe Handler • un. Handler. post( Runnable r){ … • Envois de messages – Avec une instance de la classe Handler • un. Handler. send( Message m){ … Ou le cumul des deux fonctionnalités Ce sera la classe Async. Task Android_Thread et + 25
Envoi de séquences d’affichage Dépôt dans la file d’attente de l’UIThread • run. On. UIThread(Runnable r), – C’est une méthode de la classe Activity • r est placé dans la file de l’UIThread, – Avec une exécution immédiate si l’appelant est l’UIThread on. Click ! run. On. UIThread(r) UIThread Android_Thread et + un. Thread r 26
run. On. UIThread • Tout semble correct Android_Thread et + 27
run. UIThread est héritée • Tout semble correct – run. On. UIThread est bien héritée de la classe Activity Android_Thread et + 28
Affichage de 1, 2, 3, 4 sec, enfin • Ok • Démonstration Android_Thread et + 29
Une autre façon de faire • android. os. Handler – Permet de poster des instructions dans la file de l’UIThread • Analogue à l’usage de run. On. UIThread – Permet aussi de poster des messages à l’attention de l’UIThread Android_Thread et + 30
Handler … idem à run. On. UIThread • android. os. Handler – Cf. run. On. UIThread – handler comme variable d’instance de l’activity Android_Thread et + 31
run. On. UIThread / Handler. post • Sans commentaire … Android_Thread et + 32
Handler en Progress : il n’y a qu’un pas • L’interface s’est enrichie d’un « Progress. Bar • <Progress. Bar – Progress. Bar p = (Progress. Bar) find. View. By. Id(…. – p. setprogress(value) Android_Thread et + 33
En progress, Android_Thread et + mais de moins en moins lisible… 34
Une autre façon de faire, le retour • Poster un message à destination de l’UIThread • Par exemple – informer l’utilisateur du déroulement d’un traitement • Un top toutes les secondes – Il faut • Obtenir une instance de message • Installer au sein de l’activité, – la gestion du message par une méthode du handler » handle. Message est redéfinie Android_Thread et + 35
Handler, Message et la classe Looper • http: //www. aviyehuda. com/2010/12/android-multithreading-in-a-ui-environment/ Android_Thread et + 36
Réception d’un message toutes les sec. • Un handler comme variable d’instance de l’activité – Redéfinition de la méthode handle. Message Android_Thread et + 37
L’Activity • Un Thread – Qui à chaque seconde, 1. Obtient un message identifié par un paramètre (i) 2. Envoie ce message – Le paramètre i correspond au champ msg. what Android_Thread et + 38
Au sein de l’Activity Android se charge de déclencher handle. Message • Envoi + réception du message – i == msg. what Android_Thread et + 39
Messages à destination de l’UIThread • Un exemple classique – Extrait de http: //www. tutomobile. fr/utiliser-une-progressdialog-dans-ses-applications-android-tutorielandroid-n%C 2%B 022/03/02/2011/ – Une Activity avec deux longs, très longs calculs • Un Thread se charge des deux calculs – Comment informer l’utilisateur tout au long des calculs ? • • Android_Thread et + Au démarrage, Entre deux calculs, D’une erreur, De la fin 40
Copie d’écran de ce que l’on souhaiterait • Deux Progress. Dialog et pour terminer un toast Android_Thread et + 41
Comment informer l’utilisateur ? • Attribut on. Click … de l’unique bouton de l’interface (start) – Méthode on. Click. Start public void on. Click. Start(View v){ m. Progress. Dialog = Progress. Dialog. show(this, "Patience", "de longs calculs commencent. . . ", true); Runnable r = new Runnable(){ public void run(){ // le premier calcul débute do. Long. Operation 1(); // et maintenant le deuxième calcul do. Long. Operation 2(); // voilà c’est fini }}; new Thread(r). start(); } } Android_Thread et + 42
Comment informer l’utilisateur ? • Un handler est créé • Des messages lui sont envoyés, Looper s’en charge public void on. Click. Start(View v){ m. Progress. Dialog = Progress. Dialog. show(…); Runnable r = new Runnable(){ public void run(){ // message = le premier calcul débute // envoi du message à destination du handler do. Long. Operation 1(); // message = maintenant le deuxième calcul // envoi du message à destination du handler do. Long. Operation 2(); // message = voilà c’est fini // envoi d’un message à destination du handler }}; new Thread(r). start(); } } Android_Thread et + 43
on. Click. Start est décoré, MSG_IND==1, MSG_END==2 public void on. Click. Start(View v){ m. Progress. Dialog = Progress. Dialog. show(this, "Patience", "de longs calculs commencent. . . ", true); Runnable r = new Runnable(){ public void run(){ String progress. Bar. Data = "le premier calcul débute. . . "; Message msg = m. Handler. obtain. Message(MSG_IND, (Object) progress. Bar. Data); m. Handler. send. Message(msg); do. Long. Operation 1(); progress. Bar. Data = "et maintenant le deuxième calcul. . . "; msg = m. Handler. obtain. Message(MSG_IND, (Object) progress. Bar. Data); m. Handler. send. Message(msg); do. Long. Operation 2(); progress. Bar. Data = "voilà c'est fini. . . "; msg = m. Handler. obtain. Message(MSG_END, (Object) progress. Bar. Data); m. Handler. send. Message(msg); }}; new Thread(r). start(); } Android_Thread et + 44
Un Handler est créé, redéfinition de handle. Message final Handler m. Handler = new Handler() { public void handle. Message(Message msg) { if (m. Progress. Dialog. is. Showing()) { if(msg. what==MSG_IND) m. Progress. Dialog. set. Message(((String) msg. obj)); if(msg. what==MSG_END){ Toast. make. Text(get. Base. Context(), "Info: " + (String)msg. obj, Toast. LENGTH_LONG ). show(); m. Progress. Dialog. dismiss(); } }}}; Android_Thread et + 45
Envoi et réception public void on. Click. Start(View v){ m. Progress. Dialog = Progress. Dialog. show(this, "Patience", "de longs calculs commencent. . . ", true); Runnable r = new Runnable(){ public void run(){ String progress. Bar. Data = "le premier calcul débute. . . "; Message msg = m. Handler. obtain. Message(MSG_IND, (Object) progress. Bar. Data); m. Handler. send. Message(msg); do. Long. Operation 1(); 1 1 msg. what==MSG_IND 2 msg. what==MSG_IND 3 msg. what==MSG_END progress. Bar. Data = "et maintenant le deuxième calcul. . . "; msg = m. Handler. obtain. Message(MSG_IND, (Object) progress. Bar. Data); m. Handler. send. Message(msg); do. Long. Operation 2(); 2 progress. Bar. Data = "voilà c'est fini. . . "; msg = m. Handler. obtain. Message(MSG_END, (Object) progress. Bar. Data); m. Handler. send. Message(msg); }}; new Thread(r). start(); } 3 final Handler m. Handler = new Handler() { public void handle. Message(Message msg) { if (m. Progress. Dialog. is. Showing()) { if(msg. what==MSG_IND) m. Progress. Dialog. set. Message(((String) msg. obj)); if(msg. what==MSG_END){ Toast. make. Text(get. Base. Context(), "Info: " + (String)msg. obj, Toast. LENGTH_LONG ). show(); m. Progress. Dialog. dismiss(); } }}}; Android_Thread et + 46
Copie d’écran obtenu … • à quel prix … mais avec l’habitude ? Android_Thread et + 47
Un premier résumé • UIThread : à préserver • Thread pas facile et ne suffit pas toujours – Rappel : Pas de thread autre que l’UIThread pour gérer l’affichage • Handler – Une interface avec l’UIThread • post( une instance de Runnable • send. Message( un message – Message obtenu depuis un handler • Existe-t-il une classe toute prête et simplificatrice ? – o. UI ! – Async. Task<Params, Progress, Result> Android_Thread et + 48
Sommaire • UIThread déclenche une activity et gère l’IHM – Les appels des « on. XXXX » c’est lui • Thread, Handler, du java traditionnel • Async. Task, adapté et créé pour se passer du traditionnel – Un Thread + Handler intégré Android_Thread et + 49
Async. Task<Params, Progress, Result> • Avec la classe, Async. Task<Params, Progress, Result> – http: //developer. android. com/reference/android/os/Async. Task. html • Nous avons un thread et un handler créés en interne • Un thread : pour le traitement en tâche de fond • Un handler : pour la mise à jour de l’UI – Params type des paramètres transmis au Thread (tâche de fond) – Progress type des paramètres en cours de traitement transmis au Handler/UIThread – Result type du résultat pour l’appelant Android_Thread et + 50
Schéma de programme, syntaxe, Async. Task<Params, Progress, Result> public class Mon. Activity extends Activity{ public void on. Start(){ … Work. Async. Task wt = new Work. Async. Task(); … } private class Work. Async. Task // ou private static class Work. Async. Task extends Async. Task<String, Long, Boolean>{ } } Android_Thread et + 51
Schéma de programme, syntaxe, Async. Task<Params, Progress, Result> public void on. Start(){ … … } Work. Async. Task wt = new Work. Async. Task(); // appels de méthodes publiques private class Work. Async. Task // ou private static class Work. Async. Task extends Async. Task<String, Long, Boolean>{ // méthodes redéfinies, protected } Android_Thread et + 52
Async. Task< Params, Progress, Result> • Une des méthodes publiques pour les clients – execute(param 1, param 2, param 3) Android_Thread et + 53
Schéma de programme, syntaxe, Async. Task<Params, Progress, Result> public void on. Start(){ … Work. Async. Task wt = new Work. Async. Task(); wt. execute(string 1, string 2, string 3); … } private class Work. Async. Task // ou private static class Work. Async. Task extends Async. Task<String, Long, Boolean>{ // méthodes redéfinies (déclarées protected) } Android_Thread et + 54
Async. Task< Params, Progress, Result> • Autres méthodes publiques : pour les clients – cancel, get, execute. On. Executor, … Android_Thread et + 55
Schéma de programme, syntaxe, Async. Task<Params, Progress, Result> public void on. Start(){ … … } this. wt = new Work. Async. Task(); wt. execute(string 1, string 2, string 3); public void onresult(){ … … } Boolean result = wt. get(); private class Work. Async. Task // ou private static class Work. Async. Task extends Async. Task<String, Long, Boolean>{ // méthodes redéfinies, protected } Android_Thread et + 56
Schéma de programme, syntaxe, Async. Task<Params, Progress, Result> public void on. Start(){ … … wt = new Work. Async. Task(); wt. execute(string 1, string 2, string 3); } private class Work. Async. Task extends Async. Task<String, Long, Boolean>{ // Quelles sont les méthodes à redéfinir protected void on. Pre. Execute() … protected Boolean do. In. Background(String… pendant protected void on. Progress. Update(Long… l) … pendant protected void on. Post. Execute(Boolean ) … avant s) … après } Android_Thread et + 57
class Work. Async. Task extends Async. Task<Params, Progress, Result> • Les méthodes héritées – En liaison directe avec l’UI Thread – do. In. Background doit être redéfinie dans Work. Async. Task – http: //developer. android. com/reference/android/os/Async. Task. html – publish. Progress déclenche, appelle on. Progress. Update • La quantité de sable du sablier … Android_Thread et + 58
Schéma de programme, syntaxe, Async. Task<Params, Progress, Result> public void on. Start(){ … this. wt = new Work. Async. Task(); … wt. execute( string 1, string 2, string 3 ); } private class Work. Async. Task extends Async. Task<String, Long, Boolean>{ void on. Pre. Execute() { // faire patienter l’utilisateur, affichage d’un sablier… Boolean do. In. Back. Ground(String. . . t){ // effectuer la tâche coûteuse en temps // t[0]/string 1, t[1]/string 2, … publish. Progress( l 1, l 2, l 3 ); // déclenche on. Progress. Update void on. Progress. Update(Long. . . v) { // informer l’utilisateur que le traitement est en cours void on. Post. Execute(Boolean b) { // le sablier disparaît, une éventuelle erreur est affichée } Android_Thread et + 59
Diagramme de séquences simplifié <Params, Progress, Result> : Activity new : Async. Task : UIThread : Back. Ground. Thread execute(p 1, p 2, p 3) on. Pre. Execute() do. In. Back. Ground(p 1, p 2, p 3) publish. Progress(i 1, i 2, i 3) on. Progress. Update(i 1, i 2, i 3) return r 1 on. Post. Execute(r 1) cancel() get() • Discussions Android_Thread et + 60
Exemple initial revu : affichage de 1, 2, 3, 4 sec • Async. Task<Void, Integer> Android_Thread et + 61
Async. Task<Void, Integer> À chaque clic : new Progress. Bar. Task(). execute(); private class Progress. Bar. Task extends Async. Task<Void, Integer>{ protected Integer do. In. Back. Ground(Void. . . v){ écoulée Pendant 5 fois s’endormir une seconde prévenir l’affichage à chaque seconde void on. Progress. Update(Integer. . . v) { afficher la seconde écoulée } Android_Thread et + 62
L’exemple initial avec Async. Task, plus simple… ? • Tout est bien qui finit bien, – L’UIThread gère l’IHM – Progress. Bar progresse, (c’est lisible, nous sommes en progrès (facile…)) • à chaque appel de publish. Progress (l’UIThread est réactualisé avec on. Progress. Update) Android_Thread et + 63
Une autre version, 5 Thread * 1 sec À chaque clic : for(int i=1; i<=5; i++) new Progress. Bar. Task(). execute(i); private class Progress. Bar. Task extends Async. Task<Integer, Void, Integer>{ protected Integer do. In. Back. Ground(Integer. . . t){ s’endormir une seconde prévenir l’affichage t[0] à chaque seconde écoulée void on. Post. Execute(Integer v) { afficher la seconde écoulée est appelée avec le résultat retourné par do. In. Back. Ground Android_Thread et + 64
L’exemple avec Async. Task, moins simple, en 5 Threads Un autre découpage en tâche élémentaire de 1 sec… Discussion. . . Démonstration Android_Thread et + 65
Reprenons l’exemple des longs calculs… • Progress. Dialog et pour terminer portons un toast Android_Thread et + 66
Simplissime … • Pas de Handler, pas de messages, … public void on. Click. Start(View v){ new Longs. Calculs(). execute(); } private class Longs. Calculs extends Async. Task<Void, String>{ // transparent suivant } Android_Thread et + 67
Async. Task<Void, String> private class Longs. Calculs extends Async. Task<Void, String>{ private Progress. Dialog m. Progress. Dialog; private Context thiss = Longs. Calculs. Activity. this; protected void on. Pre. Execute() { m. Progress. Dialog = Progress. Dialog. show(thiss, "Patience", "de longs calculs commencent. . . ", true); } protected String do. In. Background(Void. . . inutilisé) { publish. Progress("le premier calcul débute. . . "); do. Long. Operation 1(); publish. Progress("et maintenant le deuxième calcul. . . "); do. Long. Operation 2(); return "voilà c'est fini"; } protected void on. Progress. Update(String. . . result){ m. Progress. Dialog. set. Message(result[0]); } protected void on. Post. Execute(String s){ Toast. make. Text(get. Base. Context(), "Info: " + s, Toast. LENGTH_LONG). show(); m. Progress. Dialog. dismiss(); }} Android_Thread et + 68
En résumé Async. Task<Params, Progress, Result> • Depuis l’UIThread – création d’une instance et appel de la méthode execute • Exemple new Work. Async. Task(). execute(str 1, str 2, str 3); • Async. Task<Params, Progress, Result> – Réalise une encapsulation d’un Thread et d’un Handler Méthodes • on. Pre. Execute() – Préambule, l’UI exécute cette méthode • Result do. In. Background(Params…p) – Le contenu de cette méthode s’exécute dans un autre Thread • on. Progress. Update(Progress…p) – Mise à jour de l’UI à la suite de l’appel de publish. Progress • on. Post. Execute(Result) – Mise à jour de l’UI à la fin de la méthode do. In. Background http: //developer. android. com/reference/android/os/Async. Task. html Android_Thread et + 69
Demande de résultat: appel de get : Activity new : Async. Task : UIThread : Back. Ground. Thread execute(p 1, p 2, p 3) on. Pre. Execute() do. In. Back. Ground(p 1, p 2, p 3) publish. Progress(i 1, i 2, i 3) on. Progress. Update(i 1, i 2, i 3) return r 1 on. Post. Execute(r 1) get() • Discussions Android_Thread et + 70
Schéma de programme, syntaxe, Async. Task<Params, Progress, Result> public void on. Result(){ … Boolean r = wt. get(); } private class Work. Async. Task extends Async. Task<String, Long, Boolean>{ public Boolean do. In. Background(String… s) … } Android_Thread et + 71
Arrêt : appel de cancel(true) : Activity new : Async. Task : UIThread : Back. Ground. Thread execute(p 1, p 2, p 3) on. Pre. Execute() do. In. Back. Ground(p 1, p 2, p 3) cancel(true) return … on. Cancelled(p) • Discussions Android_Thread et + 72
Schéma de programme, syntaxe, Async. Task<Params, Progress, Result> public void on. Stop(){ … } boolean may. Interrupting. If. Running = true; boolean r = wt. cancel(may. Interrupting. If. Running); private class Work. Async. Task extends Async. Task<String, Long, Boolean>{ public Boolean do. In. Background(String… while(!is. Cancelled()){ … s) … public void on. Cancelled(String s){ // est appelée au return de do. In. Back. Ground } Android_Thread et + 73
Schémas de programme • Séquences et appels de méthodes • execute • publish. Progress • cancel(true) Android_Thread et + 74
Schéma de programme, syntaxe, Async. Task<Params, Progress, Result> execute(string 1, string 2, string 3) on. Pre. Execute est appelée do. In. Back. Ground(string 1, string 2, string 3 public void on. Start(){ … this. wt = new Work. Async. Task(); wt. execute(string 1, string 2, string 3); } private class Work. Async. Task extends Async. Task< String, Long, Boolean>{ void on. Pre. Execute() { // affichage d’un sablier… String Boolean do. In. Back. Ground(. . . t){ // t[0]/string 1, t[1]/string 2, … Android_Thread et + 75
Schéma de programme, syntaxe, Async. Task<Params, Progress, Result> public void on. Start(){ … this. wt = new Work. Async. Task(); wt. execute(string 1, string 2, string 3); … } private class Work. Async. Task extends Async. Task<String, Long, Boolean>{ Boolean do. In. Back. Ground(String. . . t){ boolean resultat. . while(condition){ … publish. Progress(valeur) do. In. Back. Ground publish. Progress déclenche on. Progress. Update } return resultat; } void on. Progress. Update(Long. . . v) { // informer l’utilisateur que le traitement est en cours v[0] == valeur } Android_Thread et + 76
Schéma de programme, syntaxe, Async. Task<Params, Progress, Result> public void on. Start(){ … this. wt = new Work. Async. Task(); wt. execute(string 1, string 2, string 3); … } private class Work. Async. Task extends Async. Task<String, Long, Boolean>{ Boolean do. In. Back. Ground(String. . . t){ return resultat; } do. In. Back. Ground se termine on. Post. Execute est appelée void on. Post. Execute(Boolean b) { } Android_Thread et + 77
Schéma de programme, syntaxe, Async. Task<Params, Progress, Result> public void on. Stop(){ … wt. cancel(true); // true: le thread (do. In. Back. Ground) peut être interrompu … } private class Work. Async. Task extends Async. Task<String, Long, Boolean>{ Boolean do. In. Back. Ground(String. . . t){ while(!is. Cancelled()){ // } return resultat; on. Cancelled } est appelée do. In. Back. Ground se termine void on. Cancelled(Boolean result) { } Android_Thread et + 78
execute et THREAD_POOL_EXECUTOR public void on. Start(){ … this. wt = new Work. Async. Task(); //wt. execute( string 1, string 2, string 3 ); wt. execute. On. Executor(Async. Task. THREAD_POOL_EXECUTOR , string 1, string 2, string 3 ); … } Par défaut: les Threads internes créés ne sont pas en « parallèle » private class Work. Async. Task extends Async. Task<String, Long, Boolean>{ void on. Pre. Execute() { // faire patienter l’utilisateur, affichage d’un sablier… Boolean do. In. Back. Ground(String. . . t){ // effectuer la tâche coûteuse en temps // t[0]/string 1, t[1]/string 2, … publish. Progress( l 1, l 2, l 3 ); // déclenche on. Progress. Update void on. Progress. Update(Long. . . v) { // informer l’utilisateur que le traitement est en cours void on. Post. Execute(Boolean b) { // le sablier disparaît, une éventuelle erreur est affichée } Android_Thread et + 79
Précautions … Async. Task<Params… • Attention aux références transmises : Params Méthodes exécutées dans un autre Thread • Result do. In. Background(Params… p) • Alors un accès concurrent est possible à p[0], p[1] – L’idéal serait de ne transmettre que des instances immutables • String, – Ou bien utiliser java. util. concurrent. * Atomic. Integer, … – Ou encore Collections. synchronized. Map … Android_Thread et + 80
Résumé, conclusion • Attention à la gestion de l’écran – Laissons cet UIThread gérer tous les évènements de l’utilisateur • Alors • Chaque opération coûteuse en temps d’exécution se fera dans un autre Thread • Si ce Thread doit interagir avec l’écran (Progress. Bar) alors – run. On. UIThread, Handler représentent une solution • Async. Task<Params, Progress, Result> » Est une autre solution avec une encapsulation (réussie) d’un thread et d’un handler Alors Async. Task<Params, Progress, Result> nous préfèrerons • En annexe : Accès au Web, utilisation d’Async. Task Android_Thread et + 81
Annexe • Lecture d’une page HTML • L’activité est détruite mais le Thread continue • Outil de supervision Android_Thread et + 82
Le patron Command/Async. Task • Cf. Command • Discussion Android_Thread et + 83
Async. Task et réseau, un exemple • Lire une page sur le web HTTP, requête GET – private class Lire. Page. HTML extends Async. Task<String, Void, String>{ Schéma on. Pre. Execute Afficher une fenêtre d’informations, Progress. Dialog do. In. Back. Ground Ouvrir une connexion, avec un échec éventuel on. Post. Execute Informer l’utilisateur Android_Thread et + 84
Lire la page www. cnam. fr • Si j’ai la permission … de naviguer sur le web – <uses-permission android: name="android. permission. INTERNET"></uses-permission> – Une IHM simple – L’accès au web est une opération coûteuse alors héritons de Async. Task Android_Thread et + 85
Une classe interne héritant de Async. Task protected String do. In. Background(String. . . args) { builder = new String. Builder(); try { Http. Client client = new Default. Http. Client(); Http. Get http. Get = new Http. Get(args[0]); Http. Response response = client. execute(http. Get); Status. Line status. Line = response. get. Status. Line(); int status. Code = status. Line. get. Status. Code(); if (status. Code == 200) { Http. Entity entity = response. get. Entity(); Input. Stream content = entity. get. Content(); Buffered. Reader reader = new Buffered. Reader( new Input. Stream. Reader(content)); String line; while ((line = reader. read. Line()) != null) { builder. append(line); } } else {error = "Failed to download file"; } } catch (Exception e) {error = e. get. Message(); } return builder. to. String(); } Android_Thread et + 86
Annexes • http: //som-itsolutions. blogspot. fr/2011/10/androidasynctask-demistified. html • L’activité est détruite, – Que devient le Thread ? • Mise au point, déploiement Android_Thread et + 87
L’activité est détruite • Un thread est créé • L’activité s’arrête, moult raisons • Le thread est encore là… Android_Thread et + 88
Un autre exemple, avec sauvegarde d’un thread • Cf. Cycle de vie d’une activité – Suspension pendant une communication … • Rappel – Une rotation de l’écran entraîne un arrêt puis un nouveau démarrage de l’activité, • ce qui nécessite une sauvegarde des données de l’activité et sa restitution. – Il existe bien d’autres possibilités d’arrêter l’activité en cours • ET si un thread était en pleine activité… • Lecture d’une page sur le web, calculs intenses, … Android_Thread et + 89
Une solution • Une solution – Classe interne et statique + Variable de classe + Thread • Un thread, indépendant du cycle de vie – Nous préférerons un service • très inspiré de http: //www. vogella. de/articles/Android. Performance/article. html Android_Thread et + 90
A la création public class Thread. Static. Activity extends Activity { private static Progress. Dialog dialog; private static Handler handler; private static Thread horloge; public void on. Create(Bundle saved. Instance. State) { super. on. Create(saved. Instance. State); set. Content. View(R. layout. main); handler = new Handler(); // restitution éventuelle horloge = (Thread) get. Last. Non. Configuration. Instance(); if (horloge != null && horloge. is. Alive()) { dialog = Progress. Dialog. show(this, "horloge", horloge. to. String()); } } Android_Thread et + 91
Démarrage et Sauvegarde au cas où public void on. Click. Start(View v){ horloge = new Horloge(); horloge. start(); dialog = Progress. Dialog. show(this, "horloge", horloge. to. String()); } @Override public Object on. Retain. Non. Configuration. Instance() { return horloge; } Android_Thread et + 92
Horloge interne et statique … discussions public static class Horloge extends Thread{ private final static int MN = 60*1000; private final static int PERIODE = 10*1000; private int compteur = 0; public String to. String(){ return Integer. to. String(compteur); } public void run(){ while(compteur<(3*MN)/PERIODE){ try{ Thread. sleep(PERIODE); }catch(Exception e){} compteur++; } handler. post(new Runnable(){ public void run(){ dialog. dismiss(); } }); }} Android_Thread et + // arrêt au bout de 3 mn // dismiss dans l’UIThread 93
Copies d’écran • L’IHM est réactualisée à chaque rotation de l’écran… – À chaque rotation sauvegarde du Thread … Android_Thread et + 94
Mise au point, déploiement • Outils graphiques prédéfinis Android_Thread et + 95
Déploiement, débogage • Chaque appel de méthode est recensé – Outil traceview Android_Thread et + 96
La classe Looper, en détail • Looper. loop() – Retrait des messages de la file d’attente – Chaque message possède son Handler • Dont la méthode handle. Message(Message msg) est appelée • Suis-je dans l’UIThread ? – Looper. get. Main. Looper(). get. Thread(). equals(Thread. current. Thread()) – Looper. get. Main. Looper(). equals(Looper. my. Looper()) • DDMS pour en savoir plus Android_Thread et + 97
DDMS • Méthode Looper. loop appelée depuis le thread main Android_Thread et + 98
extensions possibles • Comme serveur – Un serveur web sur votre mobile … – Cf. l’application Paw. Server • Déploiement & Développement – Pannes possibles, le wifi disparaît, le tunnel apparaît, ( le prévoir…) – Strict. Mode Android_Thread et + 99
Un serveur web sur votre mobile (wifi) • Attention, – Lancer l’émulateur une fois connecté wifi (et non 3 G) – depuis l’émulateur accéder au serveur installé sur le PC hôte • n’est pas localhost mais 10. 0. 2. 2 – http: //developer. android. com/guide/developing/tools/emulator. html – Proxy : Settings. System. put. String(get. Content. Resolver(), Settings. System. HTTP_PROXY, "myproxy: 8080"); Android_Thread et + 100
Android_Thread et + 101





































































































