TP 4 : Android et les sockets

On veut interroger le serveur de date réalisé plus tôt.

1. Créez un projet approprié dans eclipse, puis une interface comportant un TextView dans lequel on écrira le résultat – la date envoyée par le serveur (ou un message d’erreur), et un bouton permettant de lancer une requête sur le serveur.

Attention, le système Android nécessite dispose d’un mécanisme de permission, censé prévenir l’utilisateur lorsque des opérations sensibles, telles que l’accès au réseau ou à la mémoire de masse, ou l’accès aux capteurs, sont réalisées. Notre application doit utiliser un service réseau, il faut donc lui en donner la permission dans le fichier AndroidManifest.xml (à trouvez dans le projet). Insérez la ligne suivante après celle qui définit les sdk utilisés.

    <uses-permission android:name="android.permission.INTERNET"/>

2. Implémentez le traitement du bouton de requête (reprenez le code du client réalisé lors du TP 2).
Que se passe-t-il ?

3. Si cela n’a pas marché, c’est parce que le système Android impose que les appels réseau soient passés dans un thread séparé du thread principal. Modifiez votre code pour déplacer l’ouverture du socket, la lecture et sa fermeture dans un thread séparé. Que se passe-t-il maintenant ?

4. Vous devez trouver une exception de type « android.view.ViewRoot$CalledFromWrongThreadException » avec le message « Only the original thread that created a view hierarchy can touch its views.« . Cela signifie que le thread qui gère le socket ne peut pas écrire dans l’interface graphique, qui est gérée par un autre Thread. On va devoir utiliser un système d’envoi de message. Le principe est de créer un objet de type handle qui va faire l’intermédiaire entre les 2. Le thread qui gère les socket va envoyer un message par l’intermédiaire de ce handle, et celui ci va le renvoyer au thread qui gère l’interface graphique.

Pour cela on va déclare un objet handler, en surchargeant la méthode handleMessage de la classe Handler afin de lui faire réaliser le traitement voulu (ici afficheur.setText(text);).

mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        String text = (String)msg.obj;
        afficheur.setText(text);
    }
};

Ensuite lorsque dans le thread qui gère le socket on va utiliser :

Message msg = new Message();
msg.obj = answer;
mHandler.sendMessage(msg);

On crée ici un message, on y copie la chaîne à transmettre, et on envoie le message au handler.

Voici le code complet de la classe :

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.Socket;

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.app.Activity;
import android.graphics.Color;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

public class GetDateAppActivity extends Activity implements OnClickListener, Runnable {

	private static int DEFAULTPORT = 9090;
	private static String DEFAULTSERVER = "ouessant.univ-paris8.fr";
	
	private int portNumber; 
	private String serverName;
	
	private TextView afficheur;
	private Handler mHandler;
	private EditText serverField, portField;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.getdateapp_activity);
		
		portNumber=DEFAULTPORT;
		serverName=DEFAULTSERVER;
		
		Button t=(Button) this.findViewById(R.id.button1);
		t.setTextColor(Color.RED);
		t.setOnClickListener(this);
		
		afficheur=(TextView) this.findViewById(R.id.textView1);
		serverField=(EditText)this.findViewById(R.id.editText1);
		portField=(EditText)this.findViewById(R.id.editText2);
				
		mHandler = new Handler() {
			@Override
	        public void handleMessage(Message msg) {
				String text = (String)msg.obj;
				afficheur.setText(text);
			}
		};
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.activity_main, menu);
		return true;
	}

	@Override
	public void onClick(View v) {
		if (v.getId()==R.id.button1) {
			this.serverName=this.serverField.getText().toString();
			this.portNumber=Integer.parseInt(this.portField.getText().toString());
			new Thread(this).start();
						
		}
	}

	@Override
	public void run() {
		Socket s=null;
		try {
			s = new Socket(serverName, portNumber);
		} catch (IOException e) {
			
			//afficheur.setText("Cannot connect to "+server+" on port "+port);
			Message msg = new Message();
			msg.obj = "Cannot connect to "+serverName+" on port "+portNumber;
			mHandler.sendMessage(msg);
		}

		if (s!=null) {
			BufferedReader input;
			try {
				input = new BufferedReader(new InputStreamReader(s.getInputStream()));
				String answer = input.readLine();
	
				//afficheur.setText(answer);
				Message msg = new Message();
				msg.obj = answer;
				mHandler.sendMessage(msg);
				
				input.close();
				s.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}


}

 

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *