Précédent   Forum du club des développeurs et IT Pro > Java > Général Java > Java & Mobiles > Android
Android Forum d'entraide sur Android, la plateforme mobile de Google pour téléphones portables et Smartphones. Avant de poster -> FAQ Android
Partagez cette discussion sur d'autres réseaux sociaux : Viadeo Twitter Google Facebook Digg Delicious MySpace Yahoo
Réponse
 
Outils de la discussion
Publicité
'
Vieux 15/12/2012, 15h29   #1
User Name
Nouveau Membre du Club
 
Avatar de User Name
 
Inscription : décembre 2007
Messages : 219
Détails du profil
Informations forums :
Inscription : décembre 2007
Messages : 219
Points : 32
Points : 32
Par défaut Impossible d'afficher une Alert Dialog dans un thread!

Bonjour à tous,

Je suis en train de développer une application dans laquelle j'ai une activité qui exécute un thread. Dans ce thread, j'appelle une fonction qui envoie une requête HTTP à un serveur, puis qui parse avec le Framework jSoup la page renvoyée.

Cette fonction (appelée requestAndMakeSheet() dans mon code ci-dessous) renvoie une exception si le parsing jSoup ne trouve pas certains éléments dans la page (ce qui veut dire que le login/mot de passe n'est pas bon, en fait).

Je voudrais que ce thread affiche un message d'erreur à l'utilisateur quand cette exception est levée, puis qu'il le renvoie vers l'activité principale, où il doit saisir ses identifiants.

Si par contre l'exception n'est pas levée, l'activité affiche la page HTML dans une webView.

Voici le code de mon thread:

Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
        thereIsSomethingToShow=true;
 
		//Envoyer la requête au site
        t1 = new Thread(new Runnable() //ReservationInfo.this.runOnUiThread(new Runnable() -> si je mets ça ici, l'app plante!
		{	
			public void run() 
			{
				Looper.prepare();
				StringBuffer stringBuffer = new StringBuffer("");
				BufferedReader bufferedReader = null;
				URI uri = null;
 
				try
				{
					requestAndMakeSheet(stringBuffer, bufferedReader, uri);
				}
				catch (Exception e)
				{
					thereIsSomethingToShow=false;
					ReservationInfo.this.runOnUiThread(new Runnable()
					{
						public void run() 
						{
							AlertDialog.Builder parsingErrorBox = new AlertDialog.Builder(ReservationInfo.this);
							parsingErrorBox.setTitle("Login error");
							parsingErrorBox.setMessage("You may have to check your credentials and then try again.");
							parsingErrorBox.setNeutralButton("OK",
							new DialogInterface.OnClickListener() 
							{
								public void onClick(DialogInterface dialog, int which) 
								{
									dialog.dismiss();
								}
						    });
							parsingErrorBox.show();
						}
					});
				}
				finally
				{
					if (bufferedReader!=null)
					{
						try
						{
							bufferedReader.close();
						}
						catch (IOException ioe)
						{
							Log.e("Web Request Error", ioe.getMessage());
						}
					}
				}
			}			
		});
		t1.start();
		try 
		{
			t1.join();
			if (thereIsSomethingToShow)
			{
				mWebview.loadUrl("file:///"+Environment.getExternalStorageDirectory()+"/ReservationSheet3.html");
				setContentView(mWebview);
			}
			else
			{
				//Return to main activity
				Intent myIntent = new Intent(this.getBaseContext(), MainActivity.class);
                startActivityForResult(myIntent, 0);
			}
		} 
		catch (InterruptedException e) 
		{
			e.printStackTrace();
		}
	}
Le seul problème que je rencontre avec ce code par rapport à ce que je veux, c'est que le message d'erreur ne s'affiche pas avant le retour à l'activité principale.

J'ai essayé plein de trucs vus sur StackOverflow, mais y a rien à faire! Ce sont eux entre autres qui m'ont dit d'entourer mon message d'un thread runOnUiThread, mais le mettre ou pas n'a donné aucune différence. Et si je le mets en lieu et place du thread t1 tout en haut, l'app plante dès qu'elle arrive sur l'activité qui contient ce thread!?

Pourriez-vous m'aider à comprendre ce qui se passe?
Merci d'avance.
__________________
C'est de nos erreurs qu'on apprend. Si ça pouvait être le cas du compilateur, ça nous arrangerait bien...
User Name est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 15/12/2012, 23h43   #2
nicroman
Modérateur
 
Homme Nicolas Romantzoff
Ingénieur systèmes et réseaux
Inscription : février 2007
Messages : 2 859
Détails du profil
Informations personnelles :
Nom : Homme Nicolas Romantzoff
Localisation : France, Rhône (Rhône Alpes)

Informations professionnelles :
Activité : Ingénieur systèmes et réseaux
Secteur : High Tech - Multimédia et Internet

Informations forums :
Inscription : février 2007
Messages : 2 859
Points : 4 903
Points : 4 903
Envoyer un message via Skype™ à nicroman
Y a des trucs bizarres quand même:
Code :
1
2
3
4
5
 
t1.start();
		try 
		{
			t1.join();
?????? quel intérêt de faire un thread alors si c'est pour attendre qu'il se termine aussitôt ? D'autant que si t1 rame un peu... l'application sera interrompue avec un ANR. Et puis de toute manière c'est interdit ^^

Sinon, dans le code, tout est mélangé... le thread, le dialog d'erreur ... c'est à se perdre à mon avis:

Le thread fait une action (pourquoi passer un new Runnable() ? autant surcharger run() du thread non ?)
Éventuellement on code comme cela:
Code :
1
2
3
4
5
6
7
8
9
10
11
 
Thread t1 = new Thread() {
   public void run() {
      try {
          .... tout le truc en background...
          final result = xxxxx;
          runOnUIThread(new Runnable() { public void run() { NomDeLACtivite.this.onThreadResult(result); } });
      } catch (Exception ex) {
          runOnUIThread(new Runnable() { public void run() { NomDeLActivite.this.onThreadError(ex); } });
      }
}
Voila, ensuite, deux fonction à implémenter coté activité (UI thread) onThreadResult() et onThreadError().
A noter que l'utilisation d'AsyncTask aurait grandement simplifier les choses (voire d'un 'loader' si le but était de charger quelque chose):
Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 
AsyncTask<?,?,?> t = new ASyncTask<Void,RESULT,Void>()
{
    Exception error;
    public RESULT doInBackground(Void ... params) {
        try {
          .... tout le truc en background...
          return result;
        } catch (Exception ex) {
          this.error = ex;
          return null;
        }
    }
    public void onPostExecute(RESULT res) {
       if (res != null)
           NomDeLActivite.this.onThreadResult(res);
       else
           NomDeLActivite.this.onThreadError(this.error);
    }
}
__________________
N'oubliez pas de cliquer sur mais aussi sur si un commentaire vous a été utile !
Et surtout
nicroman est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 16/12/2012, 14h42   #3
User Name
Nouveau Membre du Club
 
Avatar de User Name
 
Inscription : décembre 2007
Messages : 219
Détails du profil
Informations forums :
Inscription : décembre 2007
Messages : 219
Points : 32
Points : 32
La vache, je ne connaissais rien de tout ce que tu m'as évoqué, je connais à peine l'utilité d'un thread, et je l'utilise parce que je suis obligé pour mes requêtes HTTP dedans.

En fait ce que fait mon thread, c'est charger une page web et la parser avec jSoup pour l'afficher d'une manière particulière dans une webView. Mais dans le fond, je ne sais pas si c'est la meilleure façon de faire, sauf que c'est la SEULE qui ait marché jusqu'à présent. :/

Je vais voir ce qu'est un Loader si j'ai encore le temps (projet à finir pour bientôt, donc j'ai peur de faire de grosses modifs qui partent en sucette).

ASyncTask -> très mal expliqué sur le web. Tu aurais un bon exemple?

Sinon, j'ai réussi à afficher mon alert dialog comme ça:

Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
    private void nuclearRequest(final Bundle bundle)
    {
    	//Send request to website
        t1 = new Thread(new Runnable()
		{	
			public void run() 
			{
				Looper.prepare();
				
				StringBuffer stringBuffer = new StringBuffer("");
				BufferedReader bufferedReader = null;
				URI uri = null;
				
				Log.i("goCheckOnline", bundle.getString("goCheckOnline"));
				if (bundle.getString("goCheckOnline").equals("Yes"))
				{
					//Check Internet connectivity
					ConnectivityManager cm = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
					NetworkInfo networkInfo = cm.getActiveNetworkInfo();
					
					try
					{
						int networkType = networkInfo.getType();
						android.net.NetworkInfo.State networkState = networkInfo.getState();
						if (networkState.compareTo(android.net.NetworkInfo.State.CONNECTED)==0)
						{	
							requestAndMakeSheet(stringBuffer, bufferedReader, uri);
							
							if (!rdb.containsReference(Ref))
							{
								rdb.insertReservation(new Reservation(Ref, Name, 1));	
							}
						}
						else
						{
							Toast.makeText(getBaseContext(), "Please check your Internet connection and then try again.", Toast.LENGTH_LONG).show();
							
							//Return to main activity
							Intent myIntent = new Intent(getBaseContext(), MainActivity.class);
			    			myIntent.putExtra("Ref", Ref);
			    			myIntent.putExtra("Name", Name);
			                startActivityForResult(myIntent, 0);
						}
					}
					catch (NullPointerException n)
					{
						//n.printStackTrace();
						//Toast.makeText(getBaseContext(), "A NullPointerException has occured.", Toast.LENGTH_LONG).show();
						displayBox("No connnection", "You may be using Airplane mode. It must be switched off in order to check your trip.");
						thereIsSomethingToShow = false;
					}
					catch (URISyntaxException urle)
					{
						uponExceptionCatch();
					}
					catch (IOException ioe)
					{
						uponExceptionCatch();
					}
					catch (ParsingException pe)
					{
						uponExceptionCatch();
					}
					finally
					{
						//Hide progress dialog
						//progressDialog.dismiss();
						
						if (bufferedReader!=null)
						{
							try
							{
								bufferedReader.close();
							}
							catch (IOException ioe)
							{
								Log.e("Error closing buffer", ioe.getMessage());
							}
						}
					}
				} //End of "if goCheckOnline"
				progressDialog.dismiss();
			} //End of "run" method
		});
		t1.start();
		try 
		{
			t1.join();
			if (thereIsSomethingToShow)
			{
				File dir = getDir("MyApp", MODE_WORLD_READABLE);
	            File newfile = new File(dir.getAbsolutePath() + File.separator + Ref+"-"+Name+".html");
				mWebview.loadUrl("file://"+dir.getAbsolutePath()+File.separator+Ref+"-"+Name+".html"); //("file:///"+Environment.getExternalStorageDirectory()/*+"//MyApp//ReservationSheets//"*/+"//"+Ref+"-"+Name+".html");
				setContentView(mWebview);
				if (bundle.getString("goCheckOnline").equals("No"))
				{
					Toast.makeText(getBaseContext(), "This is an offline file. To refresh trip data, use \"Refresh\" in the menu.", Toast.LENGTH_LONG).show();
				}
			}
Avec displayBox contenant:
Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
 
    private void displayBox(final String title, final String message)
    {
		ReservationInfo.this.runOnUiThread(new Runnable()
		{
			public void run() 
			{
				//Display error alert dialog
				AlertDialog.Builder parsingErrorBox = new AlertDialog.Builder(ReservationInfo.this);
				parsingErrorBox.setTitle(title);
				parsingErrorBox.setMessage(message);
				parsingErrorBox.setNeutralButton("OK",
				new DialogInterface.OnClickListener() 
				{
					public void onClick(DialogInterface dialog, int which) 
					{
						dialog.dismiss();
 
						//Return to main activity
						Intent myIntent = new Intent(getBaseContext(), MainActivity.class);
		    			myIntent.putExtra("ReservationReference", ReservationReference);
		    			myIntent.putExtra("LastName", LastName);
		                startActivityForResult(myIntent, 0);
					}
			    });
				parsingErrorBox.show();
			}
		});
    }
Maintenant ce que j'aimerais faire, c'est afficher une fenêtre de chargement pour toute la durée de mon thread.

J'ai déclaré ceci avant la grosse méthode nuclearRequest (elle s'appelle comme ça du fait de son énorme taille, justement ):

Code :
1
2
3
4
5
6
 
private ProgressDialog progressDialog;
 
progressDialog = new ProgressDialog(this);
        progressDialog.setMessage("Checking trip information...");
        progressDialog.show();
Et à la fin de mon thread, j'ai mis progressDialog.dismiss();

En fait, mon application est prévue pour, lorsque je suis dans l'activité principale, j'ai un bouton qui envoie en extras le login et le mot de passe que l'user a saisis dedans, à l'activité qui contient le code ci-dessus et le thread.

Cette seconde activité va requêter un serveur en HTML, parser la page avec jSoup, afficher un message d'erreur si le parsage a des problèmes (ce qui signifie forcément que le login/mot de passe est mauvais), et afficher la page si tout se passe bien.

Le problème, c'est que entre le moment où je clique sur le bouton qui m'emmène de la première activité à la seconde, j'ai pendant un long moment une page noire, puis pendant un très bref instant la fenêtre de chargement, et enfin le chargement de la page ou le message d'erreur.

Pourquoi?
__________________
C'est de nos erreurs qu'on apprend. Si ça pouvait être le cas du compilateur, ça nous arrangerait bien...
User Name est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 17/12/2012, 11h17   #4
shebu
Candidat au titre de Membre du Club
 
Homme Sylvain Hébuterne
Ingénieur développement logiciels
Inscription : juillet 2009
Messages : 16
Détails du profil
Informations personnelles :
Nom : Homme Sylvain Hébuterne
Localisation : France, Loire Atlantique (Pays de la Loire)

Informations professionnelles :
Activité : Ingénieur développement logiciels

Informations forums :
Inscription : juillet 2009
Messages : 16
Points : 14
Points : 14
Par défaut Il faut utiliser un AsyncTask

La classe AsyncTask est typiquement faite pour cela, et très simple à utiliser.

Un tuto, parmi d'autres, ici : http://www.tutos-android.com/asyncta...one-background
shebu est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 17/12/2012, 11h27   #5
User Name
Nouveau Membre du Club
 
Avatar de User Name
 
Inscription : décembre 2007
Messages : 219
Détails du profil
Informations forums :
Inscription : décembre 2007
Messages : 219
Points : 32
Points : 32
Ah d'accoooooooord!
ASyncTask c'est un objet, qu'on instancie dans notre activité principale!?
Je croyais que c'était un type d'activité en soi.

Edit: Merci infiniment pour ce magnifique tuto qui démystifie bien cette notion tarabiscottée.
__________________
C'est de nos erreurs qu'on apprend. Si ça pouvait être le cas du compilateur, ça nous arrangerait bien...
User Name est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 17/12/2012, 14h25   #6
sinok
Modérateur
 
Avatar de sinok
 
Inscription : août 2004
Messages : 8 637
Détails du profil
Informations personnelles :
Âge : 33
Localisation : France, Paris (Île de France)

Informations forums :
Inscription : août 2004
Messages : 8 637
Points : 12 438
Points : 12 438
Citation:
Envoyé par nicroman Voir le message
Le thread fait une action (pourquoi passer un new Runnable() ? autant surcharger run() du thread non ?)
Pour moi c'est la façon propre de faire.
Voir les débats sur composition/délégation vs héritage. (http://en.wikipedia.org/wiki/Composi...er_inheritance)

Dans l'absolue, l'idée, c'est que tu n'obtiendras aucun gain par l'extension de Thread, et tu te trouveras avec un objet possédant tout un paquet de méthodes dont tu n'as absolument pas besoin.
Il est préférable de limiter l'utilisation de l'héritage à des composants/classes dont on veut redéfinir le mode de fonctionnement.
__________________
Hey, this is mine. That's mine. All this is mine. I'm claiming all this as mine. Except that bit. I don't want that bit. But all the rest of this is mine. Hey, this has been a really good day. I've eaten five times, I've slept six times, and I've made a lot of things mine. Tomorrow, I'm gonna see if I can't have sex with something.
sinok est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 17/12/2012, 18h43   #7
nicroman
Modérateur
 
Homme Nicolas Romantzoff
Ingénieur systèmes et réseaux
Inscription : février 2007
Messages : 2 859
Détails du profil
Informations personnelles :
Nom : Homme Nicolas Romantzoff
Localisation : France, Rhône (Rhône Alpes)

Informations professionnelles :
Activité : Ingénieur systèmes et réseaux
Secteur : High Tech - Multimédia et Internet

Informations forums :
Inscription : février 2007
Messages : 2 859
Points : 4 903
Points : 4 903
Envoyer un message via Skype™ à nicroman
Citation:
Envoyé par sinok Voir le message
Dans l'absolue, l'idée, c'est que tu n'obtiendras aucun gain par l'extension de Thread, et tu te trouveras avec un objet possédant tout un paquet de méthodes dont tu n'as absolument pas besoin.
Il est préférable de limiter l'utilisation de l'héritage à des composants/classes dont on veut redéfinir le mode de fonctionnement.
Tout à fait d'accord, mais là on a deux objets anonymes
Et puis de toute manière faut utiliser AsyncTask
__________________
N'oubliez pas de cliquer sur mais aussi sur si un commentaire vous a été utile !
Et surtout
nicroman est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 18/12/2012, 22h06   #8
User Name
Nouveau Membre du Club
 
Avatar de User Name
 
Inscription : décembre 2007
Messages : 219
Détails du profil
Informations forums :
Inscription : décembre 2007
Messages : 219
Points : 32
Points : 32
Bon, j'ai fini par y arriver, youpiiiiiiii!

Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
 
private requestAndMakeSheetTask ramst;
...
 
//Dans onCreate()...
 
ramst = new requestAndMakeSheetTask();
ramst.execute();
 
//La Task
 
    private class requestAndMakeSheetTask extends AsyncTask<Void, Integer, Void>
    {
 
    	@Override
    	protected void onPreExecute() 
    	{
    		super.onPreExecute();
    		//Toast.makeText(getApplicationContext(), "Début du traitement asynchrone", Toast.LENGTH_LONG).show();
    	}
 
    	@Override
    	protected void onProgressUpdate(Integer... values)
    	{
    		super.onProgressUpdate(values);
    		// Mise à jour de la ProgressBar
    		//progressBar.setProgress(values[0]);
    	}
 
    	@Override
    	protected Void doInBackground(Void... arg0) 
    	{
    		int progress;
    		for (progress=0;progress<=100;progress++)
    		{
    			for (int i=0; i<1000000; i++){}
    			//la méthode publishProgress met à jour l'interface en invoquant la méthode onProgressUpdate
    			publishProgress(progress);
    			progress++;				
    		}
    		nuclearRequest(bundle);
    		return null;
    	}
 
    	@Override
    	protected void onPostExecute(Void result) 
    	{
    		progressDialog.dismiss();
    		if (bundle.getString("goCheckOnline").equals("No") && Refresh.equals("No"))
			{
    			Log.i("bundle.getString(\"goCheckOnline\")", bundle.getString("goCheckOnline"));
    			Log.i("Refresh", Refresh);
				Toast.makeText(getBaseContext(), "This is an offline file. To refresh trip data, use \"Refresh\" in the menu.", Toast.LENGTH_LONG).show();
			}
    		//Toast.makeText(getApplicationContext(), "Le traitement asynchrone est terminé", Toast.LENGTH_LONG).show();
    	}
    }
Me reste un petit problème: j'ai un menu où j'ai ça:

Code :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
    public boolean onOptionsItemSelected(MenuItem item) 
    {
        // Handle item selection
        switch (item.getItemId()) 
        {
            case R.id.refresh:
            	Refresh="Yes";
            	ramst = new requestAndMakeSheetTask();
            	ramst.execute();
                //nuclearRequest(bundle);
                Refresh="No";
                return true;
            case R.id.send:
            	sendByMail();
                return true;
            default:
                return super.onOptionsItemSelected(item);
        }
    }
Normalement, quand j'appuie sur Refresh dans le menu, la variable Refresh se met à Yes et la task ne doit pas afficher le toast final. Mais là j'ai Refresh qui reste interprété à "No" par la task!

Pour être honnête, je m'attendais à ce genre de problème, mais je ne me rappelle plus du pourquoi. Comment y remédier?

Merci d'avance.
__________________
C'est de nos erreurs qu'on apprend. Si ça pouvait être le cas du compilateur, ça nous arrangerait bien...
User Name est déconnecté   Envoyer un message privé Réponse avec citation 00
Vieux 07/01/2013, 23h24   #9
Feanorin
Responsable Android

 
Avatar de Feanorin
 
Inscription : avril 2004
Messages : 3 271
Détails du profil
Informations forums :
Inscription : avril 2004
Messages : 3 271
Points : 5 288
Points : 5 288
Envoyer un message via Skype™ à Feanorin
Bonjour,

Cela n'est pas normal

Tu remet ta variable à No après avoir exécuté ton AsyncTask, là ou il me semble ou tu interprètes ta variable pour savoir si tu dois afficher ton dialogue se fait dans le onPostExecute ce qui veux dire que tu auras forcément ta variable à No.

Tu met à jour trop vite ta variable, ou tu ne met pas à jour ta variable au bon endroit .
Feanorin est déconnecté   Envoyer un message privé Réponse avec citation 00
Réponse
Outils de la discussion

Navigation rapide


Fuseau horaire GMT +2. Il est actuellement 02h22.


 
 
 
 
Partenaires

Hébergement Web