Exemple: Android bi-directionnelle prise réseau à l'aide de AsyncTask
Plus de la prise réseau des exemples que j'ai trouvé pour Android ont été un directionnelle seulement. J'ai besoin d'une solution pour un bi-directionnel du flux de données. Finalement, j'ai appris de l'AsyncTask. Cet exemple montre comment obtenir des données à partir d'une prise et d'envoyer des données vers elle. En raison du blocage de la nature de la douille qui est de la réception de données, que le blocage doit s'exécuter dans un thread autre que le thread d'INTERFACE utilisateur.
Pour exemple, ce code se connecte à un serveur web. En appuyant sur "Start AsyncTask" bouton pour ouvrir le socket. Une fois que le support est ouvert, le serveur web attend pour une demande. En appuyant sur "Envoyer un Message" bouton pour envoyer une requête au serveur. Aucune réponse du serveur sera affiché dans le TextView. Dans le cas de l'http, un serveur web déconnecter du client une fois que toutes les données ont été envoyées. Pour d'autres TCP flux de données, la connexion va rester en place jusqu'à ce qu'un côté se déconnecte.
Capture d'écran:
AndroidManifest.xml:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.exampleasynctask"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk android:minSdkVersion="8" />
<uses-permission android:name="android.permission.INTERNET" />
<application android:icon="@drawable/icon" android:label="@string/app_name">
<activity android:name=".MainActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
res\layout\main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<Button android:id="@+id/btnStart" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Start AsyncTask"></Button>
<Button android:id="@+id/btnSend" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Send Message"></Button>
<TextView android:id="@+id/textStatus" android:textSize="24sp" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Status Goes Here" />
</LinearLayout>
src\com.exampleasynctask\MainActivity.java:
package com.exampleasynctask;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends Activity {
Button btnStart, btnSend;
TextView textStatus;
NetworkTask networktask;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btnStart = (Button)findViewById(R.id.btnStart);
btnSend = (Button)findViewById(R.id.btnSend);
textStatus = (TextView)findViewById(R.id.textStatus);
btnStart.setOnClickListener(btnStartListener);
btnSend.setOnClickListener(btnSendListener);
networktask = new NetworkTask(); //Create initial instance so SendDataToNetwork doesn't throw an error.
}
private OnClickListener btnStartListener = new OnClickListener() {
public void onClick(View v){
btnStart.setVisibility(View.INVISIBLE);
networktask = new NetworkTask(); //New instance of NetworkTask
networktask.execute();
}
};
private OnClickListener btnSendListener = new OnClickListener() {
public void onClick(View v){
textStatus.setText("Sending Message to AsyncTask.");
networktask.SendDataToNetwork("GET /HTTP/1.1\r\n\r\n");
}
};
public class NetworkTask extends AsyncTask<Void, byte[], Boolean> {
Socket nsocket; //Network Socket
InputStream nis; //Network Input Stream
OutputStream nos; //Network Output Stream
@Override
protected void onPreExecute() {
Log.i("AsyncTask", "onPreExecute");
}
@Override
protected Boolean doInBackground(Void... params) { //This runs on a different thread
boolean result = false;
try {
Log.i("AsyncTask", "doInBackground: Creating socket");
SocketAddress sockaddr = new InetSocketAddress("192.168.1.1", 80);
nsocket = new Socket();
nsocket.connect(sockaddr, 5000); //10 second connection timeout
if (nsocket.isConnected()) {
nis = nsocket.getInputStream();
nos = nsocket.getOutputStream();
Log.i("AsyncTask", "doInBackground: Socket created, streams assigned");
Log.i("AsyncTask", "doInBackground: Waiting for inital data...");
byte[] buffer = new byte[4096];
int read = nis.read(buffer, 0, 4096); //This is blocking
while(read != -1){
byte[] tempdata = new byte[read];
System.arraycopy(buffer, 0, tempdata, 0, read);
publishProgress(tempdata);
Log.i("AsyncTask", "doInBackground: Got some data");
read = nis.read(buffer, 0, 4096); //This is blocking
}
}
} catch (IOException e) {
e.printStackTrace();
Log.i("AsyncTask", "doInBackground: IOException");
result = true;
} catch (Exception e) {
e.printStackTrace();
Log.i("AsyncTask", "doInBackground: Exception");
result = true;
} finally {
try {
nis.close();
nos.close();
nsocket.close();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
Log.i("AsyncTask", "doInBackground: Finished");
}
return result;
}
public void SendDataToNetwork(String cmd) { //You run this from the main thread.
try {
if (nsocket.isConnected()) {
Log.i("AsyncTask", "SendDataToNetwork: Writing received message to socket");
nos.write(cmd.getBytes());
} else {
Log.i("AsyncTask", "SendDataToNetwork: Cannot send message. Socket is closed");
}
} catch (Exception e) {
Log.i("AsyncTask", "SendDataToNetwork: Message send failed. Caught an exception");
}
}
@Override
protected void onProgressUpdate(byte[]... values) {
if (values.length > 0) {
Log.i("AsyncTask", "onProgressUpdate: " + values[0].length + " bytes received.");
textStatus.setText(new String(values[0]));
}
}
@Override
protected void onCancelled() {
Log.i("AsyncTask", "Cancelled.");
btnStart.setVisibility(View.VISIBLE);
}
@Override
protected void onPostExecute(Boolean result) {
if (result) {
Log.i("AsyncTask", "onPostExecute: Completed with an Error.");
textStatus.setText("There was a connection error.");
} else {
Log.i("AsyncTask", "onPostExecute: Completed.");
}
btnStart.setVisibility(View.VISIBLE);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
networktask.cancel(true); //In case the task is currently running
}
}
- U pourrait fournir la partie côté serveur, situé sur un appareil Android?
Vous devez vous connecter pour publier un commentaire.
La
SendDataToNetwork
l'exécution de la tâche dans le main thread de l'interface utilisateur, le sens qu'il va se planter à un Nid d'abeilles ou plus application en raison d'NetworkOnMainThreadException
exception Irrécupérable. Voici ce que monSendDataToNetwork
ressemble pour éviter ce problème:Votre
SendDataToNetwork
ne fonctionnent pas sur le même thread quedoInBackground()
. Il y a une possibilité queSendDataToNetwork
commence à envoyer des données avant de socket est prêt.Pour éviter tout cela utilisez simplement
SendDataToNetwork
pour enregistrer les données et de signaux au thread d'arrière-plan que les données sont prêtes à être envoyées.Puisqu'il y a possibilité que l'utilisateur peut appuyer sur le bouton plusieurs fois, tandis que les anciennes données sont encore en cours d'envoi, vous devriez avoir synchronisé la File d'attente à l'intérieur de NetworkTask. Alors:
SendDataToNetwork
ajoute des données dans la file d'attente et se réveille le thread d'arrière-plan (vianotify()
).finish
drapeau. Si elle est définie, elle ferme les connexions et les sorties. Si pas, il lit les données à partir de la File d'attente, il envoie à réseau et retourne se coucher.finish()
méthode qui définit unfinish
drapeau (variable atomique, comme booléen) et se réveille le thread d'arrière-plan. C'est une façon de s'arrêter normalement le thread d'arrière-plan.Jetez un oeil à combien de synchronisation de thread est fait: http://www.jchq.net/tutorial/07_03Tut.htm
De plus en plus interactifs exemple
Similaire à l'OP, mais vous pouvez contrôler l'hôte, le port et le message + il y a une fenêtre de notification d'erreur si la connexion a échoué.
Utilisation 1:
ifconfig
netcat -l 12345
sur un terminalCtrl + D
output:
sectionUtilisation 2:
google.com
80
"GET /HTTP/1.1\r\nHost: google.com\r\n\r\n"
Noter que certains serveurs HTTP de ne pas fermer après la réponse s'attendant à une nouvelle demande, et la demande va s'accrocher jusqu'à ce qu'ils timeout. Ces serveurs vous attends pour analyser les
Content-Width
en-tête et fermez-vous.Si la connexion échoue, un message d'alerte est affiché à l'utilisateur dans une boîte de dialogue.
Code
Ajouter à
AndroidManifest.xml
:Et la principale activité est:
Sur GitHub avec la version standard.
J'ai aussi posté un Android server exemple à: https://stackoverflow.com/a/35745834/895245
Testé sur Android 5.1.1, Sony Xperia 3 D6643.