Partage
  • Partager sur Facebook
  • Partager sur Twitter

[Android] Thread qui execute une fonction avec paramètres

    16 août 2012 à 11:41:10

    Bonjour,
    Ayant récemment réussi à communiquer avec un Web Service pour mon appli android, j'ai essayé différentes versions pour voir si tout fonctionnait. Le problème est que pour la version 4.0.3, une erreur a fait son apparition : NetworkOnMainThreadException.
    De ce que j'ai trouvé par mes recherches, il semblerait que depuis la version 11 d'Android, il soit nécessaire de mettre les tâches lourdes dans des Thread à part (ce que je trouve logique, mais que j'avais voulu éviter car n'ayant aucune idée de comment le gérer avec mon code)

    J'ai donc une fonction qui prend en compte un paramètre :

    public static final String strURL = "http://xx.fr/xx/xx.json";
    
    private String getServerData(String returnString){
    		final EditText _mail = (EditText)findViewById(R.id.reg_mail);
    		final EditText _pass = (EditText)findViewById(R.id.reg_pass);
    		final EditText _conf = (EditText)findViewById(R.id.reg_conf_pass);
    		final String mail = _mail.getText().toString();
    		final String pass = _pass.getText().toString();
    		final String conf = _conf.getText().toString();
    		
    		InputStream is = null;
    		String result = "";
    		if(pass.equals(conf) && pass.length() >= 6){
    			ArrayList<NameValuePair> nvp = new ArrayList<NameValuePair>();
    			nvp.add(new BasicNameValuePair("email", mail));
    			nvp.add(new BasicNameValuePair("password", pass));
    			
    			try{
    				HttpClient client = new DefaultHttpClient();
    				HttpPost post = new HttpPost(strURL);
    				//Log.println(BIND_AUTO_CREATE, "post", post.getMethod());
    				post.setEntity(new UrlEncodedFormEntity(nvp));
    				HttpResponse resp = client.execute(post);
    				HttpEntity entity = resp.getEntity();
    				is = entity.getContent(); 
    			}
    			catch(Exception e){
    				Log.e("log_tag", e.toString());
    			}
    			
    			try{
    				BufferedReader reader = new BufferedReader(new InputStreamReader(is));
    				StringBuilder total = new StringBuilder();
    				String line = null;
    				
    				while((line = reader.readLine()) != null ){
    					total.append(line + "\n");
    				}
    				is.close();
    				result = total.toString();
    			}
    			catch(Exception e){
    				Log.e("log_tag", e.toString());
    			}
    			
    			try{
    				JSONArray jArray = new JSONArray(result);
    				for(int i = 0; i < jArray.length(); i++){
    					JSONObject json_data = jArray.getJSONObject(i);
    					
    					Log.i("log_tag", "mail : " + json_data.getString("mail") + "\n pass : " + 
    							json_data.getString("password"));
    					returnString += "\n \t" + jArray.getJSONObject(i);
    				}
    			}
    			catch(JSONException e){
    				Log.e("log_tag", "Error parsing data " + e.toString());
    			}
    			returnString = result;
    			return returnString;
    		}
    		else
    			return "Les mots de passe ne sont pas identiques";
    	}
    


    Cette fonction me permet dans le cas présent d'inscrire un utilisateur dans une base de données. Le problème est que lorsque j'y mets un thread, le paramètre returString ne peut pas être placé dans le thread, sans quoi il faudrait le mettre en final, ce qui implique que je ne pourrais pas le modifier, et il en est de même pour le String result, que je modifie au long du code mais auquel je peux pas accéder ou modifier dans le Thread à moins de le déclarer dedans, mais dans ce cas je ne peux pas l'utiliser pour returnString à la fin.

    J'ai fait pas mal de recherches pour essayer de palier à ça, mais je n'ai rien réussi à trouver qui me soit utile.

    Merci d'avance.
    • Partager sur Facebook
    • Partager sur Twitter
      16 août 2012 à 12:06:38

      J'avais le même soucis que toi et donc on m'a pas mal aidé ici ^^
      Je te passe le lien de mon sujet qui est assé gros mais qui m'a fait comprendre le fonctionnement ^^
      Solution
      • Partager sur Facebook
      • Partager sur Twitter
        16 août 2012 à 15:25:30

        Merci pour ta réponse.

        J'ai essayé en modifiant un peu ton code pour l'adapter au mien, mais ça ne marche pas mieux.
        En gros j'ai refait un code à part pour tester plus lisiblement, pour un formulaire de connexion, en reprenant les bases de mon code original.

        J'ai donc créé deux classes que voici :

        package com.example.testthread;
        
        import java.io.BufferedReader;
        import java.io.IOException;
        import java.io.InputStream;
        import java.io.InputStreamReader;
        import java.util.ArrayList;
        import java.util.List;
        
        import org.apache.http.HttpEntity;
        import org.apache.http.HttpResponse;
        import org.apache.http.NameValuePair;
        import org.apache.http.client.ClientProtocolException;
        import org.apache.http.client.HttpClient;
        import org.apache.http.client.entity.UrlEncodedFormEntity;
        import org.apache.http.client.methods.HttpPost;
        import org.apache.http.impl.client.DefaultHttpClient;
        import org.apache.http.message.BasicNameValuePair;
        import org.json.JSONArray;
        import org.json.JSONException;
        import org.json.JSONObject;
        
        import android.util.Log;
        
        public class HttpConnexion {
        	private static String TAG = "log_tag";
        	
        	public InputStream downloadPost(String url, String mail, String pass) {
        		InputStream is = null;
        		try {
        			HttpClient httpclient = new DefaultHttpClient();
        			HttpPost httpPost = new HttpPost(url);
        			// Ajoute des paramètres à la requête
        			List<NameValuePair> nvp = new ArrayList<NameValuePair>();
        			nvp.add(new BasicNameValuePair("email", mail));
        			nvp.add(new BasicNameValuePair("password", pass));
        			httpPost.setEntity(new UrlEncodedFormEntity(nvp));
        			HttpResponse response = httpclient.execute(httpPost);
        			HttpEntity entity = response.getEntity();
        			is = entity.getContent();		
        		}
        		catch (ClientProtocolException e) {
        			if (BuildConfig.DEBUG)
        				Log.e(TAG, "Client Protocol Exception : " + e.toString());
        		} 
        		catch (IOException e) {
        			if (BuildConfig.DEBUG)
        				Log.e(TAG, "IO Exception : " + e.toString());
        		}
        		return is;
        	}
        
        	/**
        	 * Convertis un objet InputStream en chaine de caractères
        	 * 
        	 * @param is
        	 *            Input Stream
        	 * @return fichier json en string
        	 */
        	public String convertInputStreamToString(InputStream is) {
        		StringBuilder sb = new StringBuilder();
        		try {
        			BufferedReader reader = new BufferedReader(new InputStreamReader(
        					is, "iso-8859-1"), 8);
        			String line = null;
        			while ((line = reader.readLine()) != null) {
        				sb.append(line + "\n");
        			}
        			is.close();
        		} catch (Exception e) {
        			Log.e(TAG, "Error converting result " + e.toString());
        		}
        		return sb.toString();
        	}
        
        	/**
        	 * Parse le fichier JSON dans une liste
        	 * 
        	 * @param result
        	 *            Fichier json
        	 * @return liste du résultat du parsing
        	 */
        	public List<String> parseJsonFile(String result) {
        		List<String> data = new ArrayList<String>();
        		try {
        			JSONArray jArray = new JSONArray(result);
        			for (int i = 0; i < jArray.length(); i++) {
        				JSONObject json_data = jArray.getJSONObject(i);
        				data.add(json_data.getString("email"));
        				// Affichage ID_ville et Nom_ville dans le LogCat
        				Log.i("log_tag", json_data.getString("password"));
        				// Résultats de la requête
        			}
        		}
        		catch (JSONException e) {
        			Log.e(TAG, "Error parsing data " + e.toString());
        		}
        		return data;
        	}
        }
        


        Ca c'est pour le traitement des données, et ça pour les traiter en asynchrones :

        package com.example.testthread;
        
        import java.io.InputStream;
        import java.util.ArrayList;
        import java.util.List;
        
        import android.app.Activity;
        import android.os.AsyncTask;
        import android.os.Bundle;
        import android.view.View;
        import android.view.View.OnClickListener;
        import android.widget.ArrayAdapter;
        import android.widget.Button;
        import android.widget.EditText;
        import android.widget.ProgressBar;
        import android.widget.Toast;
        
        public class WSCall extends Activity {
        	
        	private ProgressBar mProgressBar;
        	private Button mButton;
        	private HttpConnexion mHttpConnexion = new HttpConnexion();
        	private InputStream mInputStream;
        	private List<String>mData = new ArrayList<String>();
        	private EditText mail = (EditText)findViewById(R.id.mail);
        	private EditText pass = (EditText)findViewById(R.id.pass);
        	private String mMail = mail.getText().toString();
        	private String mPass = pass.getText().toString();
        	
            @Override
            public void onCreate(Bundle savedInstanceState) {
               super.onCreate(savedInstanceState);
               setContentView(R.layout.wscall);
               
               mButton = (Button)findViewById(R.id.confirm);
               mButton.setOnClickListener(new OnClickListener() {
            	   @Override
            	   public void onClick(View v) {
            		   new AsyncDownload().execute();
            	   }
               });
            }
            
            public class AsyncDownload extends AsyncTask<Void, Void, Void>{
        
        		@Override
        		protected Void doInBackground(Void... params) {
        			mInputStream = mHttpConnexion.downloadPost("http://xxx.fr/xx/xx.json",
        					mMail, mPass);
        			return null;
        		}
        		
        		@Override
        		protected void onPostExecute(Void result) {
        			super.onPostExecute(result);
        			// Ici je ne suis plus dans le thread secondaire
        			if (mInputStream != null) {
        				// Aucun problème survenu
        				String res = mHttpConnexion.convertInputStreamToString(mInputStream);
        				mData = mHttpConnexion.parseJsonFile(res);
        				for(int i = 0; i < mData.size(); i++){
        					Toast.makeText(getApplicationContext(), mData.get(i), Toast.LENGTH_SHORT).show();
        				}
        			}
        		}
            }
        }
        


        Mais malgré ça, l'application plante dès le lancement avec ce message dans le LogCat :

        08-16 15:13:34.559: E/AndroidRuntime(31461): FATAL EXCEPTION: main
        08-16 15:13:34.559: E/AndroidRuntime(31461): java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{com.example.testthread/com.example.testthread.WSCall}: java.lang.NullPointerException
        08-16 15:13:34.559: E/AndroidRuntime(31461): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1573)
        08-16 15:13:34.559: E/AndroidRuntime(31461): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1667)
        08-16 15:13:34.559: E/AndroidRuntime(31461): at android.app.ActivityThread.access$1500(ActivityThread.java:117)
        08-16 15:13:34.559: E/AndroidRuntime(31461): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:935)
        08-16 15:13:34.559: E/AndroidRuntime(31461): at android.os.Handler.dispatchMessage(Handler.java:99)
        08-16 15:13:34.559: E/AndroidRuntime(31461): at android.os.Looper.loop(Looper.java:123)
        08-16 15:13:34.559: E/AndroidRuntime(31461): at android.app.ActivityThread.main(ActivityThread.java:3687)
        08-16 15:13:34.559: E/AndroidRuntime(31461): at java.lang.reflect.Method.invokeNative(Native Method)
        08-16 15:13:34.559: E/AndroidRuntime(31461): at java.lang.reflect.Method.invoke(Method.java:507)
        08-16 15:13:34.559: E/AndroidRuntime(31461): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867)
        08-16 15:13:34.559: E/AndroidRuntime(31461): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:625)
        08-16 15:13:34.559: E/AndroidRuntime(31461): at dalvik.system.NativeStart.main(Native Method)
        08-16 15:13:34.559: E/AndroidRuntime(31461): Caused by: java.lang.NullPointerException
        08-16 15:13:34.559: E/AndroidRuntime(31461): at android.app.Activity.findViewById(Activity.java:1647)
        08-16 15:13:34.559: E/AndroidRuntime(31461): at com.example.testthread.WSCall.<init>(WSCall.java:25)
        08-16 15:13:34.559: E/AndroidRuntime(31461): at java.lang.Class.newInstanceImpl(Native Method)
        08-16 15:13:34.559: E/AndroidRuntime(31461): at java.lang.Class.newInstance(Class.java:1409)
        08-16 15:13:34.559: E/AndroidRuntime(31461): at android.app.Instrumentation.newActivity(Instrumentation.java:1021)
        08-16 15:13:34.559: E/AndroidRuntime(31461): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1565)
        08-16 15:13:34.559: E/AndroidRuntime(31461): ... 11 more

        J'ai essayé quelques modifications dans les paramètres etc.. mais ça ne change rien, toujours le close forcé de l'appli dès son lancement.
        • Partager sur Facebook
        • Partager sur Twitter
          16 août 2012 à 17:40:27

          Essaye déjà de mettre
          public class AsyncDownload extends AsyncTask<String, Void, Void>
          

          Vu que les paramètre de ta fonction de connexion sont des string.

          Après faut voir, j'ai jamais compris pq mais même avec un virtualHost je n'arrive pas à atteindre mon webservice via le nom de domaine mais via son ip (tu as bien donné les droits d'accès dans le manifest? je pense je ne vois pas l'erreur en tout cas).

          Ensuite quand tu exécutes ta méthode asynchrone, tu dois passer dans execute() tes praramètres:
          new AsyncDownload().execute(url, mMail, mPass);
          

          Et ensuite tu les récupères dans ta fonction asynchrone:
          public class AsyncDownload extends AsyncTask<String, Void, Void>{
          
          		@Override
          		protected Void doInBackground(Void... params) {
                                  String url = params[0];
          			String mail= params[1];
          			String pass = params[2];
          			mInputStream = mHttpConnexion.downloadPost(url,
          					mail, pass);
          			return null;
          		}
          		
          		@Override
          		protected void onPostExecute(Void result) {
          			super.onPostExecute(result);
          			// Ici je ne suis plus dans le thread secondaire
          			if (mInputStream != null) {
          				// Aucun problème survenu
          				String res = mHttpConnexion.convertInputStreamToString(mInputStream);
          				mData = mHttpConnexion.parseJsonFile(res);
          				for(int i = 0; i < mData.size(); i++){
          					Toast.makeText(getApplicationContext(), mData.get(i), Toast.LENGTH_SHORT).show();
          				}
          			}
          		}
              }
          
          • Partager sur Facebook
          • Partager sur Twitter
            17 août 2012 à 14:41:43

            Merci.
            J'ai réessayé en modifiant mon code comme tu le dis, mais l'appli se coupe toujours dès le lancement :

            08-17 14:38:35.779: E/AndroidRuntime(389): FATAL EXCEPTION: main
            08-17 14:38:35.779: E/AndroidRuntime(389): java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{com.example.testthread/com.example.testthread.WSCall}: java.lang.NullPointerException
            08-17 14:38:35.779: E/AndroidRuntime(389): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1573)
            08-17 14:38:35.779: E/AndroidRuntime(389): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1667)
            08-17 14:38:35.779: E/AndroidRuntime(389): at android.app.ActivityThread.access$1500(ActivityThread.java:117)
            08-17 14:38:35.779: E/AndroidRuntime(389): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:935)
            08-17 14:38:35.779: E/AndroidRuntime(389): at android.os.Handler.dispatchMessage(Handler.java:99)
            08-17 14:38:35.779: E/AndroidRuntime(389): at android.os.Looper.loop(Looper.java:123)
            08-17 14:38:35.779: E/AndroidRuntime(389): at android.app.ActivityThread.main(ActivityThread.java:3687)
            08-17 14:38:35.779: E/AndroidRuntime(389): at java.lang.reflect.Method.invokeNative(Native Method)
            08-17 14:38:35.779: E/AndroidRuntime(389): at java.lang.reflect.Method.invoke(Method.java:507)
            08-17 14:38:35.779: E/AndroidRuntime(389): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:867)
            08-17 14:38:35.779: E/AndroidRuntime(389): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:625)
            08-17 14:38:35.779: E/AndroidRuntime(389): at dalvik.system.NativeStart.main(Native Method)
            08-17 14:38:35.779: E/AndroidRuntime(389): Caused by: java.lang.NullPointerException
            08-17 14:38:35.779: E/AndroidRuntime(389): at android.app.Activity.findViewById(Activity.java:1647)
            08-17 14:38:35.779: E/AndroidRuntime(389): at com.example.testthread.WSCall.<init>(WSCall.java:25)
            08-17 14:38:35.779: E/AndroidRuntime(389): at java.lang.Class.newInstanceImpl(Native Method)
            08-17 14:38:35.779: E/AndroidRuntime(389): at java.lang.Class.newInstance(Class.java:1409)
            08-17 14:38:35.779: E/AndroidRuntime(389): at android.app.Instrumentation.newActivity(Instrumentation.java:1021)
            08-17 14:38:35.779: E/AndroidRuntime(389): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1565)
            08-17 14:38:35.779: E/AndroidRuntime(389): ... 11 more

            J'ai bien filé les autorisations dans mon Manifest, pas de soucis de ce côté, et j'arrive très bien à accéder au WS en utilisant l'URL sur les versions où je n'ai pas besoin de créer de Threads, donc je ne pense pas non plus que le problème vienne de là.

            Je vais continuer à chercher de mon côté, mais les tutos là dessus ne sont pas légion.
            • Partager sur Facebook
            • Partager sur Twitter
              18 août 2012 à 9:48:43

              Bonjour,

              Si on regarde ton LogCat, le crash est causé par ça:
              08-17 14:38:35.779: E/AndroidRuntime(389): Caused by: java.lang.NullPointerException
              08-17 14:38:35.779: E/AndroidRuntime(389): at android.app.Activity.findViewById(Activity.java:1647)
              08-17 14:38:35.779: E/AndroidRuntime(389): at com.example.testthread.WSCall.<init>(WSCall.java:25)


              Ce qui correspond à la ligne suivante:
              private EditText mail = (EditText)findViewById(R.id.mail);
              

              C'est normal car tu fais cet appel en global dans la classe (j'entends par là en dehors d'une fonction). Or, le XML de ton layout est inflaté lors de l'appel à onCreate() et pas avant!
              Donc, tu peux déclarer ton EditText en global mais il faut que tu l'initialises dans le onCreate() (et après le setContentView() bien sûr...)
              • Partager sur Facebook
              • Partager sur Twitter

              [Android] Thread qui execute une fonction avec paramètres

              × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
              × Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
              • Editeur
              • Markdown