Reputation: 13
First off, I'm a complete newbie to Android, Java and posting on Stacks Overflow so apologies if I'm doing anything wrong!
I'm trying to create a simple app for a project that utilises sockets. I want to set up a client on an Android device that sends commands to a server running on a PC. I've written my server in Python and have tested it, so I know that side of the project works. I'm having issues with the implementation of the Android app.
I followed this guide to write my client app: https://examples.javacodegeeks.com/android/core/socket-core/android-socket-example/
When I run my app, my server acknowledges that a client has connected to it. However, when I try to send data, nothing is received by the server. This is what my code looks like (it's pretty much the same as that in the link above):
MainActivity.java
package com.example.user.sockettest;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
public class MainActivity extends Activity {
private Socket socket;
private static final int SERVERPORT = 8000;
private static final String SERVER_IP = "192.168.0.17";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(new ClientThread()).start();
}
public void onClick(View view) {
try {
EditText et = (EditText) findViewById(R.id.editText);
String str = et.getText().toString();
PrintWriter out = new PrintWriter(new BufferedWriter(
new OutputStreamWriter(socket.getOutputStream())),
true);
out.println(str);
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
class ClientThread implements Runnable {
@Override
public void run() {
try {
InetAddress serverAddr = InetAddress.getByName(SERVER_IP);
socket = new Socket(serverAddr, SERVERPORT);
} catch (UnknownHostException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<EditText
android:id="@+id/editText"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="TestData" >
</EditText>
<Button
android:id="@+id/myButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="onClick"
android:text="Send" >
</Button>
</LinearLayout>
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.user.sockettest">
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="17" />
<uses-permission android:name="android.permission.INTERNET" >
</uses-permission>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" >
</uses-permission>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity android:name="com.example.user.sockettest.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
As you can see, I've set permissions as others have recommended on other threads but this does not seem to help. The socket runs on a separate thread so I doubt it's a blocking issue either.
My experience with socket programming is mainly in Python so I'm sure I'm missing something very basic in its Java implementation!
Any help or pointers would be really appreciated!
EDIT: Update code that is working now, thanks to Gabe Sechan's answer
public class MainActivity extends Activity {
private Socket socket;
boolean sendData = false; // Flag to indicate whether data should be sent
String message;
private static final int SERVERPORT = 8000;
private static final String SERVER_IP = "192.168.0.17";
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new Thread(new ClientThread()).start();
}
public void onClick(View view) {
EditText et = (EditText) findViewById(R.id.editText);
message = et.getText().toString();
sendData = true;
}
class ClientThread implements Runnable {
@Override
public void run() {
try {
InetAddress serverAddr = InetAddress.getByName(SERVER_IP);
socket = new Socket(serverAddr, SERVERPORT);
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
// Loop forever checking if a message needs to be send
while (socket.isConnected()){
if (sendData){
// If a message is ready to be sent, call sendMessage method
sendMessage();
}
}
} catch (UnknownHostException e1) {
e1.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();
}
}
private void sendMessage(){
// Send message over socket
try {
PrintWriter out = new PrintWriter(socket.getOutputStream());
out.println(message);
out.flush();
// Reset flag
sendData=false;
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
Upvotes: 1
Views: 1744
Reputation: 93569
I'm surprised you aren't crashing. The click handler runs on the main thread. The main thread can't do network IO- it should throw an exception to try. The BufferedWriter might be saving you there- it may not be writing to the actual socket and buffering everything if the messages are small, because you aren't flushing the stream.
So the first thing you need to do is do the writes on another thread- probably your ClientThread, and use some form of message passing to send write commands to it. The second thing you need is to simplify your output writers- you really don't need to wrap it in anything, just writing directly to the output stream is sufficient for your use.
Upvotes: 2