Example: Android bi-directional network socket using AsyncTask - android

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;
}

Your SendDataToNetwork does not run on the same thread as doInBackground(). There is a possibility that SendDataToNetwork would start sending data before socket is ready.
To avoid all this just use SendDataToNetwork to save data and signal to background thread that data is ready to be sent.
Since there is possibility that user can press button multiple times, while the old data is still being sent, you should have synchronized Queue inside NetworkTask. Then:
Background thread sets up the socket connection and then goes to sleep (via wait()).
On button press, SendDataToNetwork adds data to queue and wakes up the background thread (via notify()).
When background thread wakes up, it first checks the finish flag. If set, it closes connections and exits. If not it reads data from Queue, sends it to network and goes back to sleep.
You should have finish() method which sets a finish flag (atomic variable, like boolean) and wakes the background thread. This is a way to gracefully exit the background thread.
Take a look at how thread synchronization is done: http://www.jchq.net/tutorial/07_03Tut.htm

More interactive example
Similar to the OP's, but you can control host, port and message + there is a popup error notification if the connection failed.
Usage 1:
get Android and a Linux desktop on a LAN
find the IP of the desktop with ifconfig
run netcat -l 12345 on a terminal
on Android, fill in the IP of the desktop
click contact server
on the terminal, type the reply, and hit Ctrl + D
it appears on the output: section
Usage 2:
hostname google.com
port 80
Message: "GET / HTTP/1.1\r\nHost: google.com\r\n\r\n"
Note that some HTTP servers won't close after the reply expecting further requests, and the application will hang until they timeout. Such servers expect you to parse the Content-Width header and close yourself.
If the connection fails, an alert message is shown to the user on a dialog.
Code
Add to AndroidManifest.xml:
<uses-permission android:name="android.permission.INTERNET" />
And the main activity is:
import android.app.Activity;
import android.app.AlertDialog;
import android.app.IntentService;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
public class Main extends Activity {
final static String TAG = "AndroidCheatSocket";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final LinearLayout linearLayout = new LinearLayout(this);
linearLayout.setOrientation(LinearLayout.VERTICAL);
TextView textView;
final String defaultHostname = "192.168.0.";
textView = new TextView(this);
textView.setText("hostname / IP:");
linearLayout.addView(textView);
final EditText hostnameEditText = new EditText(this);
hostnameEditText.setText(defaultHostname);
hostnameEditText.setSingleLine(true);
linearLayout.addView(hostnameEditText);
textView = new TextView(this);
textView.setText("port:");
linearLayout.addView(textView);
final EditText portEditText = new EditText(this);
portEditText.setText("12345");
portEditText.setSingleLine(true);
linearLayout.addView(portEditText);
textView = new TextView(this);
textView.setText("data to send:");
linearLayout.addView(textView);
final EditText dataEditText = new EditText(this);
dataEditText.setText(String.format("GET / HTTP/1.1\r\nHost: %s\r\n\r\n", defaultHostname));
linearLayout.addView(dataEditText);
final TextView replyTextView = new TextView(this);
final ScrollView replyTextScrollView = new ScrollView(this);
replyTextScrollView.addView(replyTextView);
final Button button = new Button(this);
button.setText("contact server");
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
button.setEnabled(false);
new MyAsyncTask(Main.this, replyTextView, button).execute(
hostnameEditText.getText().toString(),
portEditText.getText().toString(),
dataEditText.getText().toString());
}
});
linearLayout.addView(button);
textView = new TextView(this);
textView.setText("output:");
linearLayout.addView(textView);
linearLayout.addView(replyTextScrollView);
this.setContentView(linearLayout);
}
private class MyAsyncTask extends AsyncTask<String, Void, String> {
Activity activity;
Button button;
TextView textView;
IOException ioException;
MyAsyncTask(Activity activity, TextView textView, Button button) {
super();
this.activity = activity;
this.textView = textView;
this.button = button;
this.ioException = null;
}
#Override
protected String doInBackground(String... params) {
StringBuilder sb = new StringBuilder();
try {
Socket socket = new Socket(
params[0],
Integer.parseInt(params[1]));
OutputStream out = socket.getOutputStream();
out.write(params[2].getBytes());
InputStream in = socket.getInputStream();
byte buf[] = new byte[1024];
int nbytes;
while ((nbytes = in.read(buf)) != -1) {
sb.append(new String(buf, 0, nbytes));
}
socket.close();
} catch(IOException e) {
this.ioException = e;
return "error";
}
return sb.toString();
}
#Override
protected void onPostExecute(String result) {
if (this.ioException != null) {
new AlertDialog.Builder(this.activity)
.setTitle("An error occurrsed")
.setMessage(this.ioException.toString())
.setIcon(android.R.drawable.ic_dialog_alert)
.show();
} else {
this.textView.setText(result);
}
this.button.setEnabled(true);
}
}
}
On GitHub with build boilerplate.
I've also posted an Android server example at: https://stackoverflow.com/a/35745834/895245
Tested on Android 5.1.1, Sony Xperia 3 D6643.

Related

Socket For Android Emulator Works but Not For Real Device

I have java code that sends strings via ip to a python script. The code works perfectly with the emulator but when I successfully install the app via usb to my phone it does not work. Here is the code:
import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
public class MainActivity extends AppCompatActivity {
public String message;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
final Button btn_python = findViewById(R.id.python);
final Button btn_movie = findViewById(R.id.movie);
final Button btn_hw = findViewById(R.id.homework);
btn_python.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view1) {
send py = new send();
message = "python";
Log.i("Button", "Button works");
System.out.println("whatever");
py.execute();
}
});
btn_movie.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view2) {
send mov = new send();
message = "movie";
mov.execute();
}
});
btn_hw.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view2) {
send hw = new send();
message = "homework";
hw.execute();
}
});
}
class send extends AsyncTask<Void,Void,Void>{
Socket s;
PrintWriter pw;
#Override
protected Void doInBackground(Void...params) {
System.out.println("whatevernumbertwo");
try {
System.out.println("whatevernumberthree");
s = new Socket("ip address", 7800);
Log.i("Socket", "connects to socket");
pw = new PrintWriter(s.getOutputStream());
Log.i("output stream", "Output stream works");
pw.write(message);
Log.i("write", "Write works");
pw.flush();
Log.i("flush", "Flush works");
pw.close();
s.close();
} catch (UnknownHostException e) {
System.out.println("Fail");
e.printStackTrace();
} catch (IOException e) {
System.out.println("Fail");
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
}
As I mentioned this works on the emulator but not on the actual device. The proper permissions have also been given. What am I doing wrong? Thanks in advance.
After much digging around, it turned out to be the server's firewall all along. That explains why (apparently) no exception was thrown, and why the code didn't seem to execute; it was executing, it was just getting stuck inside Socket() (during the connect).
Surely Socket() is, in fact, throwing an IOException; it probably just takes a while.
The code works on the emulator because, as it is operating on the same machine, it is behind the firewall.

send udp message from PC(windows)(client) to android phone(server) not working

I want to send a UDP message from PC(server) to my android phone 4.2(client) using WIFI connection. My phone and PC are connected via wireless router. But no message is received from phone to mobile.
I have understood from debugging that the program is waiting at socket.receive(packet);. So, there is no update on the UI.
I want to show message to the UI when server received a message from client and this process will be continue.
I would be grateful, if you could help me. Thank you. I have added necessary permission.
server:
package com.example.abuttontest;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import android.app.Activity;
import android.os.Bundle;
import android.provider.Settings.System;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends Activity
{
private TextView tv;
int i =0;
// String s;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button1 = (Button) findViewById(R.id.button1);
tv = (TextView) findViewById(R.id.textView1);
//TextView textMessage = (TextView) findViewById(R.id.textView2);
button1.setOnClickListener(new OnClickListener()
{
#Override
public void onClick(View v)
{
/*cheack ping message*/
//boolean morgan= isOnline();
//String s = String.valueOf(morgan);
tv.setText("kkkkkkkkkk"); // print ping message
Log.d("MyTag#","This is sample log message");
}
});
/* Thread for receiving Data from CLient */
runThread();
}
private void runThread()
{
new Thread()
{
public void run()
{
Log.d("p2p", "1");
while (i++ < 1000)
{
Log.d("p2p", "2");
try {
/////////////////////
//System.out.println("aaa");
byte[] inbuf = new byte[1000]; // default size
DatagramPacket packet = new DatagramPacket(inbuf, inbuf.length);
Log.d("p2p", "3");
DatagramSocket socket = new DatagramSocket(6000);
socket.receive(packet);
Log.d("p2p", "4");
int numBytesReceived = packet.getLength();
//System.out.println(numBytesReceived);
String s = new String(inbuf);
//System.out.println(s);
//System.out.println(inbuf[2]);
socket.close();
Log.d("p2p", "5");
runOnUiThread(new Runnable()
{
#Override
public void run()
{
tv.setText("#" + i);
}
});
Thread.sleep(300);
}
catch (InterruptedException e)
{
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}.start();
}
#Override
public boolean onCreateOptionsMenu(Menu menu)
{
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item)
{
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.one)
{
Toast.makeText(getApplicationContext(), "Toast Message ONE", Toast.LENGTH_LONG).show();
}
if (id == R.id.two)
{
Toast.makeText(getApplicationContext(), "Toast Message TWO", Toast.LENGTH_LONG).show();
}
if (id == R.id.action_settings)
{
return true;
}
return super.onOptionsItemSelected(item);
}
}
client:
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
/*packets in the IP layer are called the datagrams */
/*After creating a packet, the process of sending or receiving it involves calling the send or receive method of DatagramSocket. More specifically, you create a packet, then you create a socket. After you create the socket, you call the send method of DatagramSocket to send the datagram packet or use the receive method of DatagramSocket to receive a packet. You can also use the same DatagramSocket to send and receive multiple packets, each going to different destinations and coming from different sources.*/
public class client
{
public static void main(String args[])
{
System.out.println("from client");
try{
//InetAddress ipaddress = InetAddress.getByName("localhost");
// InetAddress ipaddress = InetAddress.getByName("192.168.0.103");
// while(true)
// {
InetAddress ipaddress = InetAddress.getByName("192.168.0.102");
int port = 6000;
//byte[] buffer = new byte[1024]; // empty byte array
String msg ="hello goooooooogle"; // send this message to the server
byte [] b_array = msg.getBytes();
//on SERVER side DatagramSocket able to receive packets on 8080 port
DatagramPacket packet = new DatagramPacket(b_array, b_array.length, ipaddress, port);// DatagramPacket(byte[], byte_length, InetAddress, port_number)
DatagramSocket socket = new DatagramSocket();
socket.send(packet);
socket.close();
// }
}
catch(Exception e)
{
System.out.println(e);
}
}
}
In your MainActivity you have not mentioned the port number which your server has to listen on.

Fatal exception in main thread [duplicate]

This question already has answers here:
How can I fix 'android.os.NetworkOnMainThreadException'?
(66 answers)
Closed 9 years ago.
I am new to android. My project is related to networking. I am getting this error
FATAL EXCEPTION: main
android.os.NetworkOnMainThreadException
at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java1133)
...
...
My code is :
package com.example.simpleclientactivity;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
public class SimpleClientActivity extends Activity {
private Socket client;
private PrintWriter printwriter;
private EditText textField1;
private Button button;
private String messsage;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
textField1 = (EditText) findViewById(R.id.editText1); //reference to the text field
button = (Button) findViewById(R.id.button1); //reference to the send button
//Button press event listener
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Context context = getApplicationContext();
CharSequence text = "Hello toast!";
int duration = Toast.LENGTH_SHORT;
Toast toast = Toast.makeText(context, text, duration);
toast.show();
messsage = textField1.getText().toString(); //get the text message on the text field
textField1.setText(""); //Reset the text field to blank
try {
client = new Socket("10.0.2.2", 4444); //connect to server
printwriter = new PrintWriter(client.getOutputStream(),true);
printwriter.write(messsage); //write the message to output stream
printwriter.flush();
printwriter.close();
client.close(); //closing the connection
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
}
Here is main.xml code
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<EditText
android:id="#+id/editText1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"`enter code here`
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="83dp"
android:ems="10"
android:text="Client" >
<requestFocus />
</EditText>
<Button
android:id="#+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/editText1"
android:layout_centerHorizontal="true"
android:layout_marginTop="53dp"
android:text="Send" />
</RelativeLayout>
It's not even displaying the toast message after clicking the button. However it's showing error in android.os.NetworkOnMainThreadException.
Try this..
This exception is thrown when an application attempts to perform a networking operation on its main thread
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Context context = getApplicationContext();
CharSequence text = "Hello toast!";
int duration = Toast.LENGTH_SHORT;
Toast toast = Toast.makeText(context, text, duration);
toast.show();
messsage = textField1.getText().toString(); //get the text message on the text field
textField1.setText(""); //Reset the text field to blank
new MyClass().execute(messsage);
}
});
MyClass.class AsyncTask
class MyClass extends AsyncTask<String, Void, String> {
private Exception exception;
protected String doInBackground(String... messsage) {
try {
client = new Socket("10.0.2.2", 4444); //connect to server
printwriter = new PrintWriter(client.getOutputStream(),true);
printwriter.write(messsage); //write the message to output stream
printwriter.flush();
printwriter.close();
client.close(); //closing the connection
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
protected void onPostExecute(String result) {
// TODO: check this.exception
// TODO: do something with the feed
}
}
You cannot perform network IO on the UI thread on Honeycomb. Technically it is possible on earlier versions of Android, but is a really bad idea as it will cause your app to stop responding, and can result in the OS killing your app for being badly behaved. You'll need to run a background process or use AsyncTask to perform your network transaction on a background thread.
There is an article about Painless Threading on the Android developer site which is a good introduction to this, and will provide you with much better depth of answer than can be realistically provided here.
You have to put your code that access the internet on a thread
new Thread(new Runnable(){
#Override
public void run(){
//your code that access internet here
}
}).start();
try this code
package com.example.simpleclientactivity;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
public class SimpleClientActivity extends Activity {
private Socket client;
private PrintWriter printwriter;
private EditText textField1;
private Button button;
private String messsage;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
textField1 = (EditText) findViewById(R.id.editText1); //reference to the text field
button = (Button) findViewById(R.id.button1); //reference to the send button
//Button press event listener
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Context context = getApplicationContext();
CharSequence text = "Hello toast!";
int duration = Toast.LENGTH_SHORT;
Toast toast = Toast.makeText(context, text, duration);
toast.show();
messsage = textField1.getText().toString(); //get the text message on the text field
textField1.setText(""); //Reset the text field to blank
new GetCategory().execute(message);
}
});
}
//add inner class
class GetCategory extends AsyncTask<Void, Void, ArrayList<AbstractDetail>>{
protected ArrayList<AbstractDetail> doInBackground(String... messsage) {
try {
client = new Socket("10.0.2.2", 4444); //connect to server
printwriter = new PrintWriter(client.getOutputStream(),true);
printwriter.write(messsage); //write the message to output stream
printwriter.flush();
printwriter.close();
client.close(); //closing the connection
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
protected void onPostExecute(ArrayList<AbstractDetail> result)
{
}
}
}
}
Problem
You can not access network over UI thread. You are accessing Network through UI thread that's why you are getting NetworkOnMainThreadException.
Solution
Create new thread and access Network over that Thread.
OR
Use AsyncTask to do network related work on doInBackgroung() and for updating UI onPostExecute().

How to receive serial data using android bluetooth

I am new to android. I am designing an android application that receives serial data from a hardware device through bluetooth. I am working on Htc desire S. I used the sample Bluetooth chat code to receive data. But the data received is incorrect. It misses some values. Can anyone please provide me any other sample code to receive large amount of data through bluetooth and save it in a file.
try this code :
Activity:
package Android.Arduino.Bluetooth;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.TextView;
import android.widget.EditText;
import android.widget.Button;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Set;
import java.util.UUID;
public class MainActivity extends Activity
{
TextView myLabel;
EditText myTextbox;
BluetoothAdapter mBluetoothAdapter;
BluetoothSocket mmSocket;
BluetoothDevice mmDevice;
OutputStream mmOutputStream;
InputStream mmInputStream;
Thread workerThread;
byte[] readBuffer;
int readBufferPosition;
int counter;
volatile boolean stopWorker;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button openButton = (Button)findViewById(R.id.open);
Button sendButton = (Button)findViewById(R.id.send);
Button closeButton = (Button)findViewById(R.id.close);
myLabel = (TextView)findViewById(R.id.label);
myTextbox = (EditText)findViewById(R.id.entry);
//Open Button
openButton.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
try
{
findBT();
openBT();
}
catch (IOException ex) { }
}
});
//Send Button
sendButton.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
try
{
sendData();
}
catch (IOException ex) { }
}
});
//Close button
closeButton.setOnClickListener(new View.OnClickListener()
{
public void onClick(View v)
{
try
{
closeBT();
}
catch (IOException ex) { }
}
});
}
void findBT()
{
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if(mBluetoothAdapter == null)
{
myLabel.setText("No bluetooth adapter available");
}
if(!mBluetoothAdapter.isEnabled())
{
Intent enableBluetooth = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBluetooth, 0);
}
Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
if(pairedDevices.size() > 0)
{
for(BluetoothDevice device : pairedDevices)
{
if(device.getName().equals("MattsBlueTooth"))
{
mmDevice = device;
break;
}
}
}
myLabel.setText("Bluetooth Device Found");
}
void openBT() throws IOException
{
UUID uuid = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); //Standard SerialPortService ID
mmSocket = mmDevice.createRfcommSocketToServiceRecord(uuid);
mmSocket.connect();
mmOutputStream = mmSocket.getOutputStream();
mmInputStream = mmSocket.getInputStream();
beginListenForData();
myLabel.setText("Bluetooth Opened");
}
void beginListenForData()
{
final Handler handler = new Handler();
final byte delimiter = 10; //This is the ASCII code for a newline character
stopWorker = false;
readBufferPosition = 0;
readBuffer = new byte[1024];
workerThread = new Thread(new Runnable()
{
public void run()
{
while(!Thread.currentThread().isInterrupted() && !stopWorker)
{
try
{
int bytesAvailable = mmInputStream.available();
if(bytesAvailable > 0)
{
byte[] packetBytes = new byte[bytesAvailable];
mmInputStream.read(packetBytes);
for(int i=0;i<bytesAvailable;i++)
{
byte b = packetBytes[i];
if(b == delimiter)
{
byte[] encodedBytes = new byte[readBufferPosition];
System.arraycopy(readBuffer, 0, encodedBytes, 0, encodedBytes.length);
final String data = new String(encodedBytes, "US-ASCII");
readBufferPosition = 0;
handler.post(new Runnable()
{
public void run()
{
myLabel.setText(data);
}
});
}
else
{
readBuffer[readBufferPosition++] = b;
}
}
}
}
catch (IOException ex)
{
stopWorker = true;
}
}
}
});
workerThread.start();
}
void sendData() throws IOException
{
String msg = myTextbox.getText().toString();
msg += "\n";
mmOutputStream.write(msg.getBytes());
myLabel.setText("Data Sent");
}
void closeBT() throws IOException
{
stopWorker = true;
mmOutputStream.close();
mmInputStream.close();
mmSocket.close();
myLabel.setText("Bluetooth Closed");
}
}
AND Here the layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
tools:ignore="TextFields,HardcodedText" >
<TextView
android:id="#+id/label"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Type here:" />
<EditText
android:id="#+id/entry"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="#id/label"
android:background="#android:drawable/editbox_background" />
<Button
android:id="#+id/open"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_below="#id/entry"
android:layout_marginLeft="10dip"
android:text="Open" />
<Button
android:id="#+id/send"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="#id/open"
android:layout_toLeftOf="#id/open"
android:text="Send" />
<Button
android:id="#+id/close"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="#id/send"
android:layout_toLeftOf="#id/send"
android:text="Close" />
</RelativeLayout>
Here for Manifest:
add to Application
// permission must be enabled complete
<manifest ....>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<application>
</application>
</manifest>
I tried this out for transmitting continuous data (float values converted to string) from my PC (MATLAB) to my phone. But, still my App misreads the delimiter '\n' and still data gets garbled. So, I took the character 'N' as the delimiter rather than '\n' (it could be any character that doesn't occur as part of your data) and I've achieved better transmission speed - I gave just 0.1 seconds delay between transmitting successive samples - with more than 99% data integrity at the receiver i.e. out of 2000 samples (float values) that I transmitted, only 10 were not decoded properly in my application.
My answer in short is: Choose a delimiter other than '\r' or '\n' as these create more problems for real-time data transmission when compared to other characters like the one I've used. If we work more, may be we can increase the transmission rate even more. I hope my answer helps someone!
The issue with the null connection is related to the findBT() function. you must change the device name from "MattsBlueTooth" to your device name as well as confirm the UUID for your service/device. Use something like BLEScanner app to confrim both on Android.
Take a look at incredible Bluetooth Serial class that has onResume() ability that helped me so much.
I hope this helps ;)
You can use android-bluetooth-serial library to send/receive messages.
Add this package as dependency in app build.gradle.
dependencies {
implementation 'com.github.harry1453:android-bluetooth-serial:v1.1'
// RxJava is also required.
implementation 'io.reactivex.rxjava2:rxjava:2.1.12'
implementation 'io.reactivex.rxjava2:rxandroid:2.0.2'
}
In your app, add a class to interact with device.
private void connectDevice(String mac) {
bluetoothManager.openSerialDevice(mac)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::onConnected, this::onError);
}
private void onConnected(BluetoothSerialDevice connectedDevice) {
// You are now connected to this device!
// Here you may want to retain an instance to your device:
deviceInterface = connectedDevice.toSimpleDeviceInterface();
// Listen to bluetooth events
deviceInterface.setListeners(this::onMessageReceived, this::onMessageSent, this::onError);
// Let's send a message:
deviceInterface.sendMessage("Hello world!");
}
private void onMessageSent(String message) {
// We sent a message! Handle it here.
Toast.makeText(context, "Sent a message! Message was: " + message, Toast.LENGTH_LONG).show(); // Replace context with your context instance.
}
private void onMessageReceived(String message) {
// We received a message! Handle it here.
Toast.makeText(context, "Received a message! Message was: " + message, Toast.LENGTH_LONG).show(); // Replace context with your context instance.
}
You can see the readme page for more details on using this package.

How to get bluetooth RFCOMM to work consistently?

I am trying to build an Android application that will interface with an external GPS receiver via the Bluetooth Serial Port Profile (SPP). I am using a Nexus One running 2.3.3. I have managed to get my application to receive data from GPS, but I have two issues: 1) When I connect to the device, it only works some of the time. Sometimes the connection just times out, other times it says the device is busy or in use. 2) I haven't been able to figure out how to send data back to the device, which is probably an issue of how I'm using the streams since the incoming stream is a blocking call.
I moved just the relevant code to a new Android application for testing, which is the following:
/res/layout/main.xml (two buttons and a textview)
<?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="Connect"></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.example.bluetoothspp/MainActivity.java
package com.example.bluetoothspp;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.net.SocketTimeoutException;
import java.util.UUID;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
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 {
private static final String TAG = "MainActivity";
private static final String BTAG = "BTThread";
static final int MSG_BT_GOT_DATA = 1;
static final int MSG_BT_STATUS_MSG = 2;
static final int MSG_BT_FINISHED = 99;
Button btnStart, btnSend;
TextView textStatus;
private BluetoothAdapter mBluetoothAdapter = null;
private BluetoothDevice btdevice = null;
Thread bThread;
BluetoothSocket bsocket;
InputStream bis = null; //Bluetooth input stream
OutputStream bos = null; //Bluetooth output stream
private String MACAddress = "00:01:95:06:1F:32";
#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);
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
}
private OnClickListener btnStartListener = new OnClickListener() {
public void onClick(View v){
if(btnStart.getText().equals("Connect")){
Log.i(TAG, "Connect button pressed");
if (mBluetoothAdapter == null) { //No adapter. Fail
Log.e(TAG, "getDefaultAdapter returned null");
textStatus.setText("getDefaultAdapter returned null");
} else {
if (!mBluetoothAdapter.isEnabled()) { //Bluetooth disabled
Log.e(TAG, "Bluetooth is Disabled");
textStatus.setText("Bluetooth is Disabled");
} else {
Log.i(TAG, "Connecting to Device: " + MACAddress);
btdevice = mBluetoothAdapter.getRemoteDevice(MACAddress);
Log.i(TAG, "Device: " + btdevice.getName());
Log.i(TAG, "Trying to Connect...");
textStatus.setText("Trying to Connect...");
Log.i(TAG, "Starting Thread");
try {
bThread = new Thread(new BluetoothClient(btdevice, true));
bThread.start();
} catch (IOException e) {
Log.e(TAG, "Could not create thread for bluetooth: " + e);
textStatus.setText("Could not create thread for bluetooth...");
}
btnStart.setText("Disconnect");
}
}
} else {
Log.i(TAG, "Disconnect button pressed");
btnStart.setText("Connect");
}
}
};
private OnClickListener btnSendListener = new OnClickListener() {
public void onClick(View v){
textStatus.setText("Sending Message to Thread.");
SendDataToBluetooth("something\r\n");
}
};
public class BluetoothClient implements Runnable {
public BluetoothClient(BluetoothDevice device, boolean IsAnHTCDevice) throws IOException {
if (IsAnHTCDevice) {
//This is a workaround for HTC devices, but it likes to throw an IOException "Connection timed out"
try {
Method m = device.getClass().getMethod("createRfcommSocket", new Class[] {int.class});
bsocket = (BluetoothSocket) m.invoke(device, Integer.valueOf(1));
} catch (Exception e) {
Log.e(BTAG, "Error at HTC/createRfcommSocket: " + e);
e.printStackTrace();
handler.sendMessage(handler.obtainMessage(MSG_BT_STATUS_MSG, "MethodException: " + e));
}
} else {
//This is the normal method, but on a Nexus One it almost always throws an IOException "Service discovery failed" message
try {
UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
bsocket = device.createRfcommSocketToServiceRecord(MY_UUID);
} catch (Exception e) {
Log.e(BTAG, "Error at createRfcommSocketToServiceRecord: " + e);
e.printStackTrace();
handler.sendMessage(handler.obtainMessage(MSG_BT_STATUS_MSG, "MethodException: " + e));
}
}
}
public void run() {
try {
Log.i(BTAG, "Cancelling Discovery");
mBluetoothAdapter.cancelDiscovery();
Log.i(BTAG, "Connecting to Socket");
bsocket.connect();
bis = bsocket.getInputStream();
bos = bsocket.getOutputStream();
Log.i(BTAG, "Socket created, streams assigned");
handler.sendMessage(handler.obtainMessage(MSG_BT_STATUS_MSG, "Device Connected"));
Log.i(BTAG, "Waiting for data...");
byte[] buffer = new byte[4096];
int read = bis.read(buffer, 0, 4096); // This is blocking
Log.i(BTAG, "Getting data...");
while (read != -1) {
byte[] tempdata = new byte[read];
System.arraycopy(buffer, 0, tempdata, 0, read);
handler.sendMessage(handler.obtainMessage(MSG_BT_GOT_DATA, tempdata));
read = bis.read(buffer, 0, 4096); // This is blocking
}
} catch (SocketTimeoutException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
} finally {
Log.i(BTAG, "Finished");
handler.sendMessage(handler.obtainMessage(MSG_BT_FINISHED));
}
}
}
public void SendDataToBluetooth(String cmd) { // You run this from the main thread.
try {
if (bsocket != null) {
bos.write(cmd.getBytes());
}
} catch (Exception e) {
Log.e("SendDataToBluetooth", "Message send failed. Caught an exception: " + e);
}
}
public Handler handler = new Handler() { // Handler for data coming from the network and bluetooth sockets
#Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_BT_GOT_DATA:
Log.i("handleMessage", "MSG_BT_GOT_DATA: " + (String) msg.obj);
textStatus.setText((String) msg.obj);
break;
case MSG_BT_STATUS_MSG:
Log.i("handleMessage", "MSG_BT_STATUS_MSG: " + (String) msg.obj);
textStatus.setText((String) msg.obj);
break;
case MSG_BT_FINISHED:
Log.i("handleMessage", "MSG_BT_FINISHED");
btnStart.setText("Connect");
break;
default:
super.handleMessage(msg);
}
}
};
#Override
protected void onDestroy() {
super.onDestroy();
if (bThread != null) { // If the thread is currently running, close the socket and interrupt it.
Log.i(BTAG, "Killing BT Thread");
try {
bis.close();
bos.close();
bsocket.close();
bsocket = null;
} catch (IOException e) {
Log.e(BTAG, "IOException");
e.printStackTrace();
} catch (Exception e) {
Log.e(BTAG, "Exception");
e.printStackTrace();
}
try {
Thread moribund = bThread;
bThread = null;
moribund.interrupt();
} catch (Exception e) {}
Log.i(BTAG, "BT Thread Killed");
}
}
}
I found that using the normal "bsocket = device.createRfcommSocketToServiceRecord(MY_UUID);" method would usually result in a "Service discovery failed" message for me, so I also tried the "bsocket = (BluetoothSocket) m.invoke(device, Integer.valueOf(1));" method. That works more often, but likes to time out when I try to connect.
What am I doing wrong here?
Try listening to the incoming data and writing to the device in separate threads. This way you are separating blocking calls.
Did you have a look at Bluetooth chat sample? The sample uses the similar threading technique.
If you are targeting 2.3 and up (which is currently installed on over 50% of android devices out there) you can use the createInsecureRfcommSocketToServiceRecord method to communicate with the device which will surely make it better and more connectable.

Categories

Resources