This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 4 years ago.
I'm trying to implement a TCP connection for one of my projects. Following a few tutorials I found an example that is used quite often. I've been trying to make it work but even in a minimalistic project I'm getting an NullPointerException.
The Code for my MainActivity is as following:
package f.l.tcptest;
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;
public class MainActivity extends AppCompatActivity {
TcpClient mTcpClient;
Button button;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new ConnectTask().execute("");
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mTcpClient.sendMessage("testing");
}
});
}
public class ConnectTask extends AsyncTask<String, String, TcpClient> {
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected TcpClient doInBackground(String... message) {
//we create a TCPClient object
TcpClient mTcpClient = new TcpClient(new TcpClient.OnMessageReceived() {
#Override
//here the messageReceived method is implemented
public void messageReceived(String message) {
//this method calls the onProgressUpdate
publishProgress(message);
}
});
mTcpClient.run();
return null;
}
#Override
protected void onProgressUpdate(String... values) {
super.onProgressUpdate(values);
//response received from server
Log.d("test", "response " + values[0]);
//process server response here....
}
}
}
And for the TCPClient:
package f.l.tcptest;
import android.util.Log;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
import static android.content.ContentValues.TAG;
public class TcpClient {
public static final String SERVER_IP = "192.168.1.102"; //server IP address
public static final int SERVER_PORT = 1337;
// message to send to the server
private String mServerMessage;
// sends message received notifications
private OnMessageReceived mMessageListener = null;
// while this is true, the server will continue running
private boolean mRun = false;
// used to send messages
private PrintWriter mBufferOut;
// used to read messages from the server
private BufferedReader mBufferIn;
/**
* Constructor of the class. OnMessagedReceived listens for the messages received from server
*/
public TcpClient(OnMessageReceived listener) {
mMessageListener = listener;
}
/**
* Sends the message entered by client to the server
*
* #param message text entered by client
*/
public void sendMessage(final String message) {
Runnable runnable = new Runnable() {
#Override
public void run() {
if (mBufferOut != null) {
Log.d(TAG, "Sending: " + message);
mBufferOut.println(message + "\r\n");
mBufferOut.flush();
}
}
};
Thread thread = new Thread(runnable);
thread.start();
}
/**
* Close the connection and release the members
*/
public void stopClient() {
mRun = false;
if (mBufferOut != null) {
mBufferOut.flush();
mBufferOut.close();
}
mMessageListener = null;
mBufferIn = null;
mBufferOut = null;
mServerMessage = null;
}
public void run() {
mRun = true;
try {
//here you must put your computer's IP address.
InetAddress serverAddr = InetAddress.getByName(SERVER_IP);
Log.e("TCP Client", "C: Connecting...");
//create a socket to make the connection with the server
Socket socket = new Socket(serverAddr, SERVER_PORT);
try {
//sends the message to the server
mBufferOut = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
//receives the message which the server sends back
mBufferIn = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//in this while the client listens for the messages sent by the server
while (mRun) {
mServerMessage = mBufferIn.readLine();
if (mServerMessage != null && mMessageListener != null) {
//call the method messageReceived from MyActivity class
mMessageListener.messageReceived(mServerMessage);
}
}
Log.e("RESPONSE FROM SERVER", "S: Received Message: '" + mServerMessage + "'");
} catch (Exception e) {
Log.e("TCP", "S: Error", e);
} finally {
//the socket must be closed. It is not possible to reconnect to this socket
// after it is closed, which means a new socket instance has to be created.
socket.close();
}
} catch (Exception e) {
Log.e("TCP", "C: Error", e);
}
}
//Declare the interface. The method messageReceived(String message) will must be implemented in the MyActivity
//class at on asynckTask doInBackground
public interface OnMessageReceived {
public void messageReceived(String message);
}
}
When i start the app, it connects to the server and maintains the connection. If i press the button to send the test message the app crashes and shows the following:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: f.l.tcptest, PID: 16391
java.lang.NullPointerException: Attempt to invoke virtual method 'void f.l.tcptest.TcpClient.sendMessage(java.lang.String)' on a null object reference
at f.l.tcptest.MainActivity$1.onClick(MainActivity.java:28)
at android.view.View.performClick(View.java:6294)
at android.view.View$PerformClick.run(View.java:24770)
at android.os.Handler.handleCallback(Handler.java:790)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6494)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
You declared the field in your activity, but you never initialize it. Check your AsyncTask's doInBackground method. You are creating a new local field called mTcpClient, you are not accessing the MainActivity's field. This is why you got NullPointException.
Here is the fixed code:
package f.l.tcptest;
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;
public class MainActivity extends AppCompatActivity {
TcpClient mTcpClient;
Button button;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new ConnectTask().execute("");
Button button = (Button) findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mTcpClient.sendMessage("testing");
}
});
}
public class ConnectTask extends AsyncTask<String, String, TcpClient> {
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected TcpClient doInBackground(String... message) {
//we create a TCPClient object
// You should use the global one, do not create the local instance if you want to use it on click event
mTcpClient = new TcpClient(new TcpClient.OnMessageReceived() {
#Override
//here the messageReceived method is implemented
public void messageReceived(String message) {
//this method calls the onProgressUpdate
publishProgress(message);
}
});
mTcpClient.run();
return null;
}
#Override
protected void onProgressUpdate(String... values) {
super.onProgressUpdate(values);
//response received from server
Log.d("test", "response " + values[0]);
//process server response here....
}
}
}
Additional Note for Android:
But this approach is not good. You should follow best practices. Maybe you can use the intent service instead of AsyncTasks and sending message on UI thread.
Related
So basically I want to create some kind of controller for my pc, where on pc, runs java socket server. The server is working, I have tried it. Also, Port Forwarding in the router is allright. Source code is here :
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Scanner;
public class Server {
public static final String ANSI_RESET = "\u001B[0m";
public static final String ANSI_GREEN = "\u001B[32m";
public static final String ANSI_BOLD = "\033[1m";
public static final String ANSI_RED = "\u001B[31m";
public static void main(String[] args) throws IOException {
// Creating server instance
ServerSocket serverSocket = new ServerSocket(2000);
Socket socket = serverSocket.accept();
System.out.println(ANSI_GREEN + "Controller connected" + ANSI_RESET);
// Input Output streams
DataInputStream dataInputStream = new DataInputStream(socket.getInputStream());
DataOutputStream dataOutputStream = new DataOutputStream(socket.getOutputStream());
String command = "";
do {
command = dataInputStream.readUTF();
switch (command.trim()){
case "volume up":
Runtime.getRuntime().exec("cmd /c start src\\sk\\dzurik\\main\\volup.bat");
break;
case "volume down":
Runtime.getRuntime().exec("cmd /c start src\\sk\\dzurik\\main\\voldown.bat");
break;
default:
// Unknown command
break;
}
}while (!command.equals("stop"));
socket.close();
System.out.println(ANSI_RED + "Controller disconnected" + ANSI_RESET);
}
}
And it is supposed to interact with the android app, but I got an error and I can't quite figure out why is that so, here is the source code for ActivityMain :
import androidx.appcompat.app.AppCompatActivity;
import android.content.Context;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
public class MainActivity extends AppCompatActivity {
private boolean connected;
private DataOutputStream dataOutputStream;
private Socket socket;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Pripájanie na server
try {
socket = new Socket("172.0.0.1",2000);
dataOutputStream = new DataOutputStream(socket.getOutputStream());
} catch (IOException e) {
Toast.makeText(MainActivity.this, "Not Connected!",
Toast.LENGTH_LONG).show();
}
Button VolumeUp = (Button) findViewById(R.id.VolumeUpButton);
VolumeUp.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
try {
send("volume up");
} catch (IOException e) {
e.printStackTrace();
}
}
});
Button VolumeDown = (Button) findViewById(R.id.VolumeDownButton);
VolumeDown.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
try {
send("volume down");
} catch (IOException e) {
e.printStackTrace();
}
}
});
Button DisconnectButton = (Button) findViewById(R.id.DisconnectButton);
DisconnectButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
try {
send("stop");
} catch (IOException e) {
e.printStackTrace();
}
}
});
}
public void send(String command) throws IOException {
if (socket != null){
dataOutputStream.writeUTF(command);
}
else Toast.makeText(MainActivity.this, "Socket is not defined",
Toast.LENGTH_SHORT).show();
}
public void disconnect() throws IOException {
if (socket != null){
socket.close();
}
else Toast.makeText(MainActivity.this, "Socket is not defined",
Toast.LENGTH_SHORT).show();
}
}
So if you could help me with it, I would be thankful, <3
Well the reason for that was silly. When I'm loading data from Stream it says nextLine which means the ending point is \n so I could never get that line because I haven't put it there.
I'm with some doubt how I can execute one action.
When receive one income call, I want to pass the number for my class it manager the Bluetooth connection with Arduino.
After send the number, I'd like to send it by bluetooth connection
I tried use intent but maybe I am using deprecated version.
Android Api 7.1
Brodcaster Receiver Class
package com.jidea.glass;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.widget.Toast;
public class TelephonyReceiver extends BroadcastReceiver
{
#Override
public void onReceive(Context arg0, Intent intent) {
// TODO Auto-generated method stub
try {
if (intent != null && intent.getAction().equals("android.intent.action.NEW_OUTGOING_CALL")) {
//Toast.makeText(context, "Outgoign call", 1000).show();
String number = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
} else {
//get the phone state
String newPhoneState = intent.hasExtra(TelephonyManager.EXTRA_STATE) ? intent.getStringExtra(TelephonyManager.EXTRA_STATE) : null;
Bundle bundle = intent.getExtras();
if (newPhoneState != null && newPhoneState.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
//read the incoming call number
String phoneNumber = bundle.getString(TelephonyManager.EXTRA_INCOMING_NUMBER);
Log.i("PHONE RECEIVER", "Telephone is now ringing " + phoneNumber);
//Toast.makeText(arg0, "Tele disponivel " + phoneNumber, Toast.LENGTH_LONG).show();
if(phoneNumber!=null | phoneNumber.equals("")){
PASS HERE TO BLUETOOTH
}
}
Bluetooth class
package com.jidea.glass;
import android.app.ProgressDialog;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothSocket;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
//import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import java.io.IOException;
import java.util.UUID;
public class CustomProcess extends AppCompatActivity
{
String address = null;
private ProgressDialog progress;
BluetoothAdapter myBluetooth = null;
BluetoothSocket btSocket = null;
private boolean isBtConnected = false;
//SPP UUID. Look for it
static final UUID myUUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
//observable
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent newint = getIntent();
address = newint.getStringExtra(MainActivity.EXTRA_ADDRESS); //receive the address of the bluetooth device
setContentView(R.layout.activity_custom_process);
//msg(address);
new ConnectBT().execute(); //Call the class to connect
}
public void isConnected() {//View view
if (btSocket!=null)
{
try
{
btSocket.getOutputStream().write("Conectado".toString().getBytes());
}
catch (IOException e)
{
msg("Error");
}
}
}
public void haveCall(String Phone) {//View view
Object aLig="Ligação \n"+Phone;
if (btSocket!=null)
{
try
{
btSocket.getOutputStream().write(aLig.toString().getBytes());
}
catch (IOException e)
{
msg("Error");
}
}
}
private void msg(String s)
{
Toast.makeText(getApplicationContext(),s,Toast.LENGTH_LONG).show();
}
private class ConnectBT extends AsyncTask<Void, Void, Void> // UI thread
{
private boolean ConnectSuccess = true; //if it's here, it's almost connected
#Override
protected void onPreExecute()
{
progress = ProgressDialog.show(CustomProcess.this, "Connecting...", "Please wait!!!"); //show a progress dialog
}
#Override
protected Void doInBackground(Void... devices) //while the progress dialog is shown, the connection is done in background
{
try
{
if (btSocket == null || !isBtConnected)
{
myBluetooth = BluetoothAdapter.getDefaultAdapter();//get the mobile bluetooth device
BluetoothDevice dispositivo = myBluetooth.getRemoteDevice(address);//connects to the device's address and checks if it's available
btSocket = dispositivo.createInsecureRfcommSocketToServiceRecord(myUUID);//create a RFCOMM (SPP) connection
BluetoothAdapter.getDefaultAdapter().cancelDiscovery();
btSocket.connect();//start connection
}
}
catch (IOException e)
{
ConnectSuccess = false;//if the try failed, you can check the exception here
}
return null;
}
#Override
protected void onPostExecute(Void result) //after the doInBackground, it checks if everything went fine
{
super.onPostExecute(result);
if (!ConnectSuccess)
{
msg("Connection Failed. Is it a SPP Bluetooth? Try again.");
finish();
}
else
{
msg("Conectado");
isBtConnected = true;
isConnected();
}
progress.dismiss();
}
}
}
First you should create an intent in your BroadcastReceiver and then use LocalBroadcastManager in order to be able to send that intent wherever you want to listen.
val intent = Intent(ACTION)
intent.putExtra("DATA KEY", DATA)
LocalBroadcastManager.getInstance(context).sendBroadcast(intent)
don't forget to register your broadcast receiver in where you want to listen
LocalBroadcastManager.getInstance(context).registerReceiver(your broadcast receiver instance, IntentFilter(ACTION))
I solved it done a Java Internal Listener with Interface the codes.
package com.jidea.glass;
public interface TelephonyListener
{
/**
* To call this method when new message received and send back
* #param message Message
*/
void callReceived(String message);
}
public class CustomProcess extends AppCompatActivity implements MessageListener, TelephonyListener
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MessageReceiver.bindListener(this);
TelephonyReceiver.bindListener(this);
Intent newint = getIntent();
address = newint.getStringExtra(MainActivity.EXTRA_ADDRESS); //receive the address of the bluetooth device
setContentView(R.layout.activity_custom_process);
//msg(address);
new ConnectBT().execute(); //Call the class to connect
#Override
public void messageReceived(String message)
{
Toast.makeText(this, "Nova Mensagem: " + message, Toast.LENGTH_SHORT).show();
}
#Override
public void callReceived(String message) {
Toast.makeText(this , "Tele disponivel " + message, Toast.LENGTH_LONG).show();
haveCall(message);
}
Continues in my class Receiver it extends BroadcastReceiver I Donw
public class TelephonyReceiver extends BroadcastReceiver
{
private static TelephonyListener mListener;
#Override
public void onReceive(Context arg0, Intent intent) {
// TODO Auto-generated method stub
}
public static void bindListener(TelephonyListener listener){
mListener = listener;
}
i started with android a short time ago. This time i tried to connect a c# tcp server with a java tcp client. To debug i worte a c# client and server and if i use the c# client to talk to the server, there is no problem. But my java socket has problems with connecting. I copied the java tcp code for example from the net.
c# code:
namespace MTCliserv2
{
class MTCliserv2
{
static void Main(string[] args)
{
string txt;
Console.Write("MTCliserv2 Server or Client (s/c): ");
txt = Console.ReadLine();
if (txt == "s")
Server();
else
Client();
Console.Write("\nHit any Key to continue "); Console.ReadKey();
}
static IPAddress GetIPAddress()
{
IPAddress ipAdr = Dns.Resolve("localhost").AddressList[0];
ipAdr = IPAddress.Parse("192.168.1.1");
return ipAdr;
}
static void Server()
{
TcpListener server;
Socket socke;
IPAddress ipAdr = GetIPAddress();
try
{
server = new TcpListener(ipAdr, 13);
server.Start();
Console.WriteLine("Server {0} gestartet", server.LocalEndpoint);
while (true)
{
socke = server.AcceptSocket(); // Aufruf blockiert!!
mit diesem Client abwickelt
new ConnTalk(socke);
}
}
catch (Exception e)
{
Console.WriteLine("Error {0}", e);
}
}
static void PrintConnectionInfo(Socket aSock)
{
Console.WriteLine("Anfrage von {0}", aSock.RemoteEndPoint);
}
static void Client()
{
const string serverName = "localhost";
try
{
TcpClient client = new TcpClient("192.168.1.1", 13);
NetworkStream strm = client.GetStream();
Socket sc = client.Client;
StreamReader strmRd = new StreamReader(strm);
StreamWriter strmWr = new StreamWriter(strm);
string txt, txt2;
Console.WriteLine("Connected to {0}", sc.RemoteEndPoint);
while (true)
{
Console.Write("Msg= ");
txt = Console.ReadLine();
if (txt == "end")
break;
strmWr.WriteLine(txt); strmWr.Flush();
txt2 = strmRd.ReadLine();
Console.WriteLine("Answer: {0}", txt2);
}
client.Close(); strmRd.Close(); strmWr.Close();
}
catch (Exception e)
{
Console.WriteLine(e);
}
}
}
// Pro Verbindung wird 1 ConnTalk-objekt erzeugt
class ConnTalk
{
Thread m_Thr;
Socket m_Sc;
public ConnTalk(Socket aSc)
{
m_Sc = aSc;
m_Thr = new Thread(this.TalkWithClient);
m_Thr.Start();
}
void TalkWithClient()
{
NetworkStream strm = new NetworkStream(m_Sc);
StreamReader strmRd = new StreamReader(strm);
StreamWriter strmWr = new StreamWriter(strm);
string txt;
Console.WriteLine("Talking with {0}", m_Sc.RemoteEndPoint);
try
{
while (true)
{
txt = strmRd.ReadLine();
Console.WriteLine("Msg: {0}", txt);
txt += " Echo!! \r\n";
strmWr.Write(txt); strmWr.Flush();
}
}
catch (Exception ex)
{
Console.WriteLine("Exception in TalkWithClient");
}
Console.WriteLine("{0} Disconnected", m_Sc.RemoteEndPoint);
m_Sc.Close(); strm.Close(); strmRd.Close(); strmWr.Close();
return;
}
}
}
And here is the java code:
package com.example.mogri.tcpsocket;
import android.Manifest;
import android.content.pm.PackageManager;
import android.os.AsyncTask;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
private EditText ettext;
private TextView tvlog;
TcpClient mTcpClient;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if ( ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.INTERNET) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.INTERNET},225);
}
if ( ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_NETWORK_STATE) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.ACCESS_NETWORK_STATE},225);
}
ettext = (EditText) findViewById(R.id.etText);
tvlog = (TextView) findViewById(R.id.tVlog);
Button btnsend = (Button) findViewById(R.id.btnsend);
btnsend.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String input = ettext.getText().toString();
mTcpClient.sendMessage(input);
log(input);
}
});
new ConnectTask().execute("");
}
public void log(String add){
tvlog.setText( tvlog.getText()+"\n"+add);
}
public class ConnectTask extends AsyncTask<String, String, TcpClient> {
#Override
protected TcpClient doInBackground(String... message) {
//we create a TCPClient object
mTcpClient = new TcpClient(new TcpClient.OnMessageReceived() {
#Override
//here the messageReceived method is implemented
public void messageReceived(String message) {
//this method calls the onProgressUpdate
publishProgress(message);
}
});
mTcpClient.run();
return null;
}
#Override
protected void onProgressUpdate(String... values) {
super.onProgressUpdate(values);
//response received from server
Log.d("test", "response " + values[0]);
//process server response here....
}
}
}
The tcpclient class:
package com.example.mogri.tcpsocket;
import android.util.Log;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;
/**
* Created by mogri on 01.04.2017.
*/
public class TcpClient {
public static final String SERVER_IP = "192.168.1.1"; //server IP address
public static final int SERVER_PORT = 13;
// message to send to the server
private String mServerMessage;
// sends message received notifications
private OnMessageReceived mMessageListener = null;
// while this is true, the server will continue running
private boolean mRun = false;
// used to send messages
private PrintWriter mBufferOut;
// used to read messages from the server
private BufferedReader mBufferIn;
public TcpClient(OnMessageReceived listener) {
mMessageListener = listener;
}
public void sendMessage(String message) {
if (mBufferOut != null && !mBufferOut.checkError()) {
mBufferOut.println(message);
mBufferOut.flush();
}
}
/**
* Close the connection and release the members
*/
public void stopClient() {
mRun = false;
if (mBufferOut != null) {
mBufferOut.flush();
mBufferOut.close();
}
mMessageListener = null;
mBufferIn = null;
mBufferOut = null;
mServerMessage = null;
}
public void run() {
mRun = true;
try {
//here you must put your computer's IP address.
InetAddress serverAddr = InetAddress.getByName(SERVER_IP);
Log.e("TCP Client", "C: Connecting...");
//create a socket to make the connection with the server
Socket socket = new Socket(serverAddr, SERVER_PORT);
try {
//sends the message to the server
mBufferOut = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
//receives the message which the server sends back
mBufferIn = new BufferedReader(new InputStreamReader(socket.getInputStream()));
//in this while the client listens for the messages sent by the server
while (mRun) {
mServerMessage = mBufferIn.readLine();
if (mServerMessage != null && mMessageListener != null) {
//call the method messageReceived from MyActivity class
mMessageListener.messageReceived(mServerMessage);
}
}
Log.e("RESPONSE FROM SERVER", "S: Received Message: '" + mServerMessage + "'");
} catch (Exception e) {
Log.e("TCP", "S: Error", e);
} finally {
//the socket must be closed. It is not possible to reconnect to this socket
// after it is closed, which means a new socket instance has to be created.
socket.close();
}
} catch (Exception e) {
Log.e("TCP", "C: Error", e);
}
}
//Declare the interface. The method messageReceived(String message) will must be implemented in the MyActivity
//class at on asynckTask doInBackground
public interface OnMessageReceived {
public void messageReceived(String message);
}
}
This question already has answers here:
NetworkOnMainThreadException [duplicate]
(5 answers)
Closed 7 years ago.
On Being a Duplicate: Please note that I have acknowledged other Q&As (Thread UI) but my case is different as I have tried to use the solution but it doesn't work at all instances (as it works for writing but doesn't for reading.
Updated: I know exactly what is going wrong please read the bold My Question part
I'm trying to make an Android Socket Client that connects to a Java server via WiFi.The final client will send and receive commands and logs as there will be a textbox that user can write SQL commands in it and by hitting a button command will be sent from the app to the Desktop server and show the log in another textbox.
so far I have been trying to use This Answer from another question to make that Client, and adding following lines in the TCP client.
Scanner in = new Scanner(socket.getInputStream());//in doInBackground
//also changed send method a little
public String send(String command)
{
if ( connected ){
out.println(command);
out.flush();
String back = in.nextLine().toString();
return back;
}
return "not Connected";
}
The connection is made successfully and the send method does send the command while server receives it.but the String that server writes back throw the mentioned exception in the title despite the fact that I am using the AsyncTask.
My Question
Why Scanner throws this exception while PrintWriter works correctly ?
Main Activity
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
public class MainActivity extends ActionBarActivity {
EditText serverIP, servMsg;
String ip, serverOut;
Button connectBtn;
TcpClient tcpClient;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
serverIP = (EditText) findViewById(R.id.serverIP);
connectBtn = (Button) findViewById(R.id.connectBtn);
servMsg = (EditText) findViewById(R.id.servMsg);
}
public void onConnectBtn(View view){
try{
ip = serverIP.getText().toString();
tcpClient = new TcpClient();
tcpClient.connect(getApplicationContext(), ip, 8082);
serverOut = tcpClient.send("HELLO FROM cat:123");
servMsg.setText(serverOut);
tcpClient.disconnect(getApplicationContext());
}
catch (Exception e){
Toast.makeText(getApplicationContext(),
"Exception on runnning thread: " + e.getMessage().toString(), Toast.LENGTH_LONG).show();
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.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();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
TCP Client
import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Handler;
import android.util.Log;
import android.widget.Toast;
public class TcpClient {
private static final String TAG = TcpClient.class.getSimpleName();
private Socket socket;
private PrintWriter out;
private Scanner in;
private boolean connected;
public TcpClient() {
socket = null;
out = null;
connected = false;
}
public void connect(Context context, String host, int port) {
new ConnectTask(context).execute(host, String.valueOf(port));
}
private class ConnectTask extends AsyncTask<String, Void, Void> {
private Context context;
public ConnectTask(Context context) {
this.context = context;
}
#Override
protected void onPreExecute() {
showToast(context, "Connecting..");
super.onPreExecute();
}
#Override
protected void onPostExecute(Void result) {
if (connected) {
showToast(context, "Connection successful");
}
super.onPostExecute(result);
}
private String host;
private int port;
#Override
protected Void doInBackground(String... params) {
try {
String host = params[0];
int port = Integer.parseInt(params[1]);
socket = new Socket(host, port);
out = new PrintWriter(socket.getOutputStream(), true);
in = new Scanner(socket.getInputStream());
} catch (UnknownHostException e) {
showToast(context, "Don't know about host: " + host + ":" + port);
Log.e(TAG, e.getMessage());
} catch (IOException e) {
showToast(context, "Couldn't get I/O for the connection to: " + host + ":" + port);
Log.e(TAG, e.getMessage());
}
connected = true;
return null;
}
}
public void disconnect(Context context) {
if (connected) {
try {
out.close();
socket.close();
connected = false;
} catch (IOException e) {
showToast(context, "Couldn't get I/O for the connection");
Log.e(TAG, e.getMessage());
}
}
}
/**
* Send command to a Pure Data audio engine.
*/
public String send(String command)
{
if ( connected ){
out.println(command);
out.flush();
String back = in.nextLine().toString();
return back;
}
return "not Connected";
}
private void showToast(final Context context, final String message) {
new Handler(context.getMainLooper()).post(new Runnable() {
#Override
public void run() {
Toast.makeText(context, message, Toast.LENGTH_LONG).show();
}
});
}
}
Also the Layout right now consist of two TextEdits one in which user types IP of the computer(which I checked with correct IPs) and the second one is going to show Server response.
I believe you are not "flushing", which is essentially forcing the buffer to empty out its contents. Please take a look at this working chat server/client code that uses TCP/IP. On a side note, I would recommend not extending ActionBarActivity because it is deprecated (Google says they will move away from it and won't be supporting it soon). Please see 3rd link.
ChatServer.java
ChatClient.java
ActionBar Activity deprecation
I need my server to keep track of each of it's client's connection. I've been advised to use Threads. So what I'm trying to achieve is to create a Thread for each client, which should run till the client connection exists. But what is happening is that for each message any client sends, a new client connection gets created in the doInBackground() function. So instead of having one single thread for one single client, I'm getting one thread for any client message sent to the server. Can you suggest a method in with which my server would be able to distinguish different messages sent from different clients?
Java Server Code :
package com.nss.academyassistserver;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.net.Socket;
public class AcademyAssistServer {
public static ServerSocket serverSocket;
public static Socket clientSocket;
static final int PORT = 4444;
public static void main(String[] args) {
// TODO Auto-generated method stub
try {
serverSocket = new ServerSocket(PORT); // Server socket
} catch (IOException e) {
System.out.println("Could not listen on port: "+PORT+" \n");
}
System.out.println("Server started. Listening to the port "+PORT);
while (true) {
try {
clientSocket = serverSocket.accept();
System.out.println("New connection accepted."); // accept the client connection
} catch (IOException ex) {
System.out.println("Problem in message reading");
}
//new thread for a client
new EchoThread(clientSocket).start();
}
}
}
class EchoThread extends Thread {
InputStreamReader inputStreamReader;
BufferedReader bufferedReader;
String fromClient;
Socket clientSocket;
public EchoThread(Socket clientSocket) {
this.clientSocket = clientSocket;
}
public void run() {
try {
inputStreamReader = new InputStreamReader(clientSocket.getInputStream());
bufferedReader = new BufferedReader(inputStreamReader); // get the client message
} catch (IOException e) {
return;
}
while (!Thread.currentThread().isInterrupted()) {
System.out.println("I am thread " + Thread.currentThread().getId());
try {
fromClient = bufferedReader.readLine();
if ((fromClient == null) || fromClient.equalsIgnoreCase("exit")) {
System.out.println("You're welcome, bye!");
return;
} else {
System.out.println(fromClient);
Thread.currentThread().interrupt();
}
} catch (IOException e) {
e.printStackTrace();
return;
}
}
try {
clientSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Client Activity Code:
package com.nss.academyassist;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Locale;
//import statements for client
import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.Toast;
//import statements for client
import android.os.AsyncTask;
public class MainActivity extends Activity implements OnClickListener {
EditText question;
//Client sockets
private Socket client;
private PrintWriter printwriter;
private String toTag;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
question = (EditText)findViewById(R.id.editText1);
Button query = (Button)findViewById(R.id.button2);
query.setOnClickListener(this);
}
private class SendMessage extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... params) {
try {
client = new Socket("Server IP Address", 4444);
printwriter = new PrintWriter(client.getOutputStream(), true);
printwriter.write(toTag); // 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;
}
}
public void onClick(View v)
{
switch(v.getId())
{
case R.id.button2:
toTag = question.getText().toString();
// Output the result
Toast.makeText(getApplicationContext(), toTag,
Toast.LENGTH_LONG).show();
//Invoke the execute method of AsynTask, which will run the doInBackground method of SendMessage Class
SendMessage sendMessageTask = new SendMessage();
sendMessageTask.execute();
break;
}
}
}
You can use IP to tell the clients. Use
clientSocket.getInetAddress().getHostAddress()
You can also keep the client not closed in the android code. For example, you may open the Socket in onCreate and close it in onDestroy.
The cause for your problem is in your client code. Using new Socket(..) your client will create a new connection each time it sends a tag to the the server. So instead of that you could create a single connection that is reused:
public class MainActivity extends Activity implements OnClickListener {
/* .. your other variables .. */
private Socket client;
private PrintWriter printwriter;
#Override
protected void onCreate(Bundle savedInstanceState) {
/* .. no change here .. */
}
public void onStart()
{
if(this.client != null)
{
try {
client = new Socket("Server IP Address", 4444);
printwriter = new PrintWriter(client.getOutputStream(), true);
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void onClose()
{
this.printwriter.close();
this.printwriter = null;
this.client.close();
this.client = null;
}
private class SendMessage extends AsyncTask<Void, Void, Void> {
#Override
protected Void doInBackground(Void... params) {
try {
printwriter.write(toTag); // write the message to output stream
printwriter.write("\n"); // delimiter
printwriter.flush();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
public void onClick(View v)
{
switch(v.getId())
{
case R.id.button2:
toTag = question.getText().toString();
// Output the result
Toast.makeText(getApplicationContext(), toTag,
Toast.LENGTH_LONG).show();
//Invoke the execute method of AsynTask, which will run the doInBackground method of SendMessage Class
SendMessage sendMessageTask = new SendMessage();
sendMessageTask.execute();
break;
}
}
}
In addition to that you should append some delimiter to your tag/message in order for the server to be able to distinguish the content from different messages.
Since you are using BufferedReader.readLine() which seperates lines
by any one of a line feed ('\n'), a carriage return ('\r'), or a
carriage return followed immediately by a linefeed
I added a line that appends a line feed after the tag in the example above for that purpose.