jedix Posté(e) 29 décembre 2011 Share Posté(e) 29 décembre 2011 (modifié) Bonjour à tous ! Je suis heureux de poser ma première question sur ce forum :) Je ne suis pas totalement noob sur android, mais il me manque indubitablement quelques éléments de compréhension ! D'où ma question. J'ai un BroadcastReceiver pour les sms, quand celui ci attrape un sms il en fait le traitement dans un AsyncTask. Supposons que la tâche dans le AsyncTask soit assez longue (dans l'exemple de code ci dessous je le remplace par un Thread.sleep(6000) par exemple) : si j'envoie 2 sms successifs à mon émulateur, les sms vont être traités l'un après l'autre (donc 12s en tout) alors qu'ils sont traités dans des AsyncTask différents. Je pensais que les Threads s'éxécutaient en parallèle, non ? Y a t-il un moyen pour que les 2 travaillent en même temps sur leur sms respectif ? Voici des codes simplifiés : Le BroadcastReceiver basique : public class SMSReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // On traite ce message MessageProcessing messageProcess = new MessageProcessing(context); messageProcess.execute(intent); } } Le AsyncTask public class MessageProcessing extends AsyncTask<Intent, Void, Void> { private Message message = null; @Override protected Boolean doInBackground(Intent... intents) { // On récupère notre intent Intent intent = intents[0]; Bundle bundle = intent.getExtras(); if (bundle != null) { Object[] pdus = (Object[]) bundle.get("pdus"); // On récupère le dernier sms et on extait numéro et corps du // message final SmsMessage lastMessage = SmsMessage .createFromPdu((byte[]) pdus[0]); final String messageBody = lastMessage.getMessageBody(); final String phoneNumber = lastMessage .getDisplayOriginatingAddress(); //On cré un objet Message (classe perso) Date now = new Date(); message = new Message(phoneNumber, messageBody, now); // On simule un traitement long try { Thread.sleep(6000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } Je vous serais reconnaissant de m'éclairer de vos lumières :) Merci ! Modifié 29 décembre 2011 par jedix Citer Lien vers le commentaire Partager sur d’autres sites More sharing options...
nbbu Posté(e) 29 décembre 2011 Share Posté(e) 29 décembre 2011 C'est un bug qui a été corrigé par notre compatriote Mr Romain GUY http://groups.google...9bad2620e38496a J'ai fait un test en API Level 7 et ça fonctionne bien. package com.nbbu.examples.asynctask; import android.app.Activity; import android.os.Bundle; import android.os.AsyncTask; public class AsyncTaskTest extends Activity { class MyTask extends AsyncTask<String, Void, Integer> { @Override protected Integer doInBackground(String ... x) { System.out.println("Start processing " + x[0]); try { Thread.sleep(6000); } catch (InterruptedException ie) { ie.printStackTrace(); } System.out.println(x[0] + " processed"); return 0; } }; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); new MyTask().execute("SMS 1"); new MyTask().execute("SMS 2"); } } résultat : :03.547: INFO/System.out(326): Start processing SMS 2 :03.744: INFO/System.out(326): Start processing SMS 1 :09.603: INFO/System.out(326): SMS 2 processed :09.750: INFO/System.out(326): SMS 1 processed 1 Citer Lien vers le commentaire Partager sur d’autres sites More sharing options...
jedix Posté(e) 29 décembre 2011 Auteur Share Posté(e) 29 décembre 2011 Salut nbbu et merci de ta réponse ! J'ai testé ton code et effectivement là ça marche bien. J'ai donc du investiger pendant un petit moment... et je crois avoir trouvé là où ça coince ! C'est que dans mon BroadcasteListener je fais ensuite appel à la méthode .get() de AsyncTask pour récupérer un Boolean (si le traitement s'est bien passé), or évidemment cette méthode attend la fin du thread. Or vu que ça se trouve dans mon BroadcastListener et que ce dernier est dans le ThreadUI, ça bloque tout ! Et ça revient quasiment au même que si je ne faisais pas mon traitement dans un Thread... (d'où le fait que mes 2 AsyncTask sont éxécutés l'un après l'autre...). Du coup je ne fois pas trop comment faire ! Car je dois absolument récupérer le résultat booléen pour ensuite appeler ou non abortBroadcast(). Voici le code complet de mon BrodcastReceiver : public class SMSReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { Hashtable<String, Boolean> result = null; // On traite ce message MessageProcessing messageProcess = new MessageProcessing(context); messageProcess.execute(intent); try {// On récupère un booléan pour savoir si le message a été validé/traité result = (Hashtable<String, Boolean>) messageProcess.get(); // On arrête le broadcast si le message a été validé (pour ne pas // recevoir le sms dans sa boite de réception) if (result.get("validated")) { abortBroadcast(); } } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ExecutionException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } De ce fait je suis dans l'impasse, est-ce que vous auriez des idées ? Citer Lien vers le commentaire Partager sur d’autres sites More sharing options...
nbbu Posté(e) 30 décembre 2011 Share Posté(e) 30 décembre 2011 Malheureusement la doc est très claire à ce sujet : A BroadcastReceiver object is only valid for the duration of the call to onReceive(Context, Intent)anything that requires asynchronous operation is not available, because you will need to return from the function to handle the asynchronous operation, but at that point the BroadcastReceiver is no longer active and thus the system is free to kill its process before the asynchronous operation completes. Donc je ne vois pas trop d'autres solutions à part peut être faire un abort systématique, faire le traitement en dehors du BroadcastReceiver, puis en fonction du résultat (via la surcharge de onPostExecute) re-broadcaster ou non via sendBroadcast. C'est juste une idée comme ça, je ne sais pas si c'est réalisable ... :emo_im_undecided: Dans tous les cas, tiens nous au courant de tes expérimentations passionnantes :P Citer Lien vers le commentaire Partager sur d’autres sites More sharing options...
jedix Posté(e) 30 décembre 2011 Auteur Share Posté(e) 30 décembre 2011 (modifié) Donc si je comprends bien, le BroadcastListener lance l'AsyncTask puis meurt (alors que l'AsyncTask n'a pas terminé). Du fait que le BroadcastListener ne soit plus, Android considère qu'il peut tuer de façon impromptue mon AsyncTask ? Il y a donc une probabilité X (qu'on ne maitrise pas du tout) que le traitement soit avorté ? Si c'est bien ça, n'est il pas possible de rattacher l'AsyncTask à l'activité en cours par exemple ? Ou même le rattacher à quelque chose d'encore plus durable qu'une simple activité (un service par exemple ?? je pose la question comme ça, je n'ai pas encore saisi tous les tenants et aboutissants des services). Ca semble étrange que la vie d'un thread soit si facilement menacée, je veux dire, même pour une activité, il suffit d'une rotation et elle est détruite pour être reconstruite ! Avez-vous quelques réflexions sur le sujet ? ^^ Modifié 30 décembre 2011 par jedix Citer Lien vers le commentaire Partager sur d’autres sites More sharing options...
nbbu Posté(e) 30 décembre 2011 Share Posté(e) 30 décembre 2011 Ce thread (je parle de ta question) devient vraiment très intéressant :lol: Donc si je comprends bien, en tout cas je comprends la même chose :PDonc j'ai parsé parcouru la doc ici : http://developer.and...nd-threads.html et j'ai repéré cette phrase : Caution: Another problem you might encounter when using a worker thread is unexpected restarts in your activity due to a runtime configuration change (such as when the user changes the screen orientation), which may destroy your worker thread. To see how you can persist your task during one of these restarts and how to properly cancel the task when the activity is destroyed, see the source code for the Shelves sample application. C'est exactement le cas qui nous intéresse. Persister le traitement fait dans l'AsychTask. Bon je sais pas si je vais avoir le temps de lire ça avant le réveillon ... je voudrais pas faire mon no-life... On en rediscute l'année prochaine si tu veux bien :lol: Citer Lien vers le commentaire Partager sur d’autres sites More sharing options...
jedix Posté(e) 30 décembre 2011 Auteur Share Posté(e) 30 décembre 2011 (modifié) Content de trouver quelqu'un avec qui explorer le problème ^^ Dans le lien que tu as donné le passage qui m'a interpelé c'est Because a process running a service is ranked higher than a process with background activities, an activity that initiates a long-running operation might do well to start a service for that operation, rather than simply create a worker thread—particularly if the operation will likely outlast the activity. For example, an activity that's uploading a picture to a web site should start a service to perform the upload so that the upload can continue in the background even if the user leaves the activity. Using a service guarantees that the operation will have at least "service process" priority, regardless of what happens to the activity. This is the same reason that broadcast receivers should employ services rather than simply put time-consuming operations in a thread. Cette dernière phrase me conduit à essayer avec un service ! Ca me permettra de me plonger pour la première fois là dedans au passage. Grosso ce que je vais essayer de faire c'est faire tourner un service en fond qui sera appelé par le BroadcastReceiver, et c'est dans le service que tournera l'AsyncTask. On verra bien si ça fonctionne. Edit : et ce post me conforte dans cette voie : http://androiddev.orkitra.com/?p=283 Modifié 30 décembre 2011 par jedix 1 Citer Lien vers le commentaire Partager sur d’autres sites More sharing options...
nbbu Posté(e) 3 janvier 2012 Share Posté(e) 3 janvier 2012 Grosso ce que je vais essayer de faire c'est faire tourner un service en fond qui sera appelé par le BroadcastReceiver, et c'est dans le service que tournera l'AsyncTask. D'après tes articles cités, cela semble en effet une très bonne solution. Tiens moi nous au courant :P Citer Lien vers le commentaire Partager sur d’autres sites More sharing options...
Bismuth76 Posté(e) 3 janvier 2012 Share Posté(e) 3 janvier 2012 Tiens moi nous au courant :P Moi je ne dis rien mais ça m'intéresse beaucoup aussi :P Citer Lien vers le commentaire Partager sur d’autres sites More sharing options...
jedix Posté(e) 3 janvier 2012 Auteur Share Posté(e) 3 janvier 2012 J'ai essayé de migrer vers le sytème de service, mais ça a engendré pas mal de complications, notamment pour passer les données à traiter au service : il faut utiliser putExtra et getExtra de la classe Intent, or si on veut passer un objet il faut qu'il implémente l'interface Parcelable (dans le même esprit que Serializable). Bon, mais le problème n'est pas là, c'est après j'ai un pb de cast à l'éxécution, et comme les vacs sont finies et que j'ai repris les cours je n'ai pas trop eu le temps de me repencher sur le problème... il faudrait que je fasse des essais sur un projet test... Citer Lien vers le commentaire Partager sur d’autres sites More sharing options...
nbbu Posté(e) 4 janvier 2012 Share Posté(e) 4 janvier 2012 Moi je ne dis rien mais ça m'intéresse beaucoup aussi Cool on se sent moins seul :) De mon coté j'ai jeté un coup d’œil sur le code de Romain Guy (Shelves) cité plus haut dans lequel il parle de persistance de tâches. La solution comporte la même difficulté que dans ton cas de service, c'est à dire la sérialisation des données. En fait sur le onSaveInstanceState il sérialise les taches en cours dans un Bundle. Dans son cas c'est assez simple puisque la tache consite au téléchargement d'un livre lié à son numéro isbn. Il stocke donc ce numéro pour pouvoir le télécharger de nouveau lors du onRestoreInstanceState. Ton cas est beaucoup plus tricky étant donné que c'est un SMS. Je pense en plus que tu risque d'être confronté au fait qu'il s'agisse d'un evt que du veux capturer ou laisser passer et que dans ce cas la vitesse de traitement est assez critique. Citer Lien vers le commentaire Partager sur d’autres sites More sharing options...
ChrOnOs Posté(e) 13 janvier 2012 Share Posté(e) 13 janvier 2012 (modifié) Pourquoi ne pas simplement utiliser un Thread classique pour effectué ceci ? final Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { super.handleMessage(msg); //Dans le thread de l'UI switch (msg.what) { case 1: break; } } }; Thread thread = new Thread(){ public void run() { //Dans le thread //envoi de données au thread de l'UI handler.sendEmptyMessage(1); } }; thread.setPriority(Thread.MIN_PRIORITY); thread.start(); Modifié 13 janvier 2012 par ChrOnOs Citer Lien vers le commentaire Partager sur d’autres sites More sharing options...
jedix Posté(e) 26 janvier 2012 Auteur Share Posté(e) 26 janvier 2012 Un autre lien que j'ai pu trouver sur le sujet : http://www.javacodegeeks.com/2012/01/learning-android-freezing-ui-with.html Citer Lien vers le commentaire Partager sur d’autres sites More sharing options...
Recommended Posts
Rejoignez la conversation
Vous pouvez poster maintenant et vous enregistrez plus tard. Si vous avez un compte, connectez-vous maintenant pour poster.