Most of the network socket examples I found for Android were one directional only. I needed a solution for a bi-directional data stream. I eventually learned of the AsyncTask. This example shows how to get data from a socket and send data back to it. Due to the blocking nature of a socket that is receiving data, that blocking needs to run in a thread other than the UI thread.
For the sake of example, this code connects to a webserver. Pressing the "Start AsyncTask" button will open the socket. Once the socket is open, the web server waits for a request. Pressing the "Send Message" button will send a request to the server. Any response from the server will be displayed in the TextView. In the case of http, a web server will disconnect from the client once all the data has been sent. For other TCP data streams, the connection will stay up until one side disconnects.
Screenshot:
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 } }
The SendDataToNetwork
task runs in the main ui thread, meaning it will crash a Honeycomb or higher app due to NetworkOnMainThreadException
Fatal exception. Here's what my SendDataToNetwork
looks like to avoid this issue:
public boolean sendDataToNetwork(final byte[] cmd) { if (_nsocket.isConnected()) { Log.i(TAG, "SendDataToNetwork: Writing received message to socket"); new Thread(new Runnable() { public void run() { try { _nos.write(cmd); } catch (Exception e) { e.printStackTrace(); Log.i(TAG, "SendDataToNetwork: Message send failed. Caught an exception"); } } }).start(); return true; } Log.i(TAG, "SendDataToNetwork: Cannot send message. Socket is closed"); return false; }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With