Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

2 questions regarding socket programming with Android

Tags:

android

I've taken examples I've found on the internet for socket programming, and am trying to build my own Android client for a an Ethernet-enabled Arduino server. I have 2 problems however. First, the code for my main activity:

package com.domiflichi.TesterProject;

import java.io.BufferedWriter; // output
import java.io.BufferedReader; // input
import java.io.InputStreamReader; // input
import java.io.OutputStreamWriter; // output
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.Socket;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;

import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;







public class TesterProjectMain extends Activity implements OnClickListener
{

private Button connectPhones;

private TextView myTextView; // represents the 'status text'

private String serverIpAddress = "";

private boolean connected = false;


private Handler handler = new Handler();

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.client);


connectPhones = (Button) findViewById(R.id.connect_phones);

connectPhones.setOnClickListener(connectListener);
connectPhones.setOnClickListener(this);



myTextView = (TextView) findViewById(R.id.text1);

}



// This was created when using the 'implements OnClickListener' in the class
public void onClick(View v) {
if (v.getId() == R.id.connect_phones) {
if (!connected) {
serverIpAddress = "192.168.0.178";
if (!serverIpAddress.equals("")) {
Thread cThread = new Thread(new ClientThread());
cThread.start();
connectPhones.setEnabled(false); // Once the button is pressed, disable it. :)
}
}

} else if (v.getId() == R.id.status_req) {
// CODE HERE FOR STATUS REQUEST BUTTON?

} else if (v.getId() == R.id.cmd_toggle) {
// CODE HERE FOR TOGGLE DOOR BUTTON?

} else if (v.getId() == R.id.cmd_crack) {
// CODE HERE FOR CRACK BUTTON?

} else if (v.getId() == R.id.disconnect) {
// CODE HERE FOR DISCONNECT BUTTON?

}
}





public class ClientThread implements Runnable {

public void run() {
try {
InetAddress serverAddr = InetAddress.getByName(serverIpAddress);
Log.d("ClientActivity", "C: Connecting...");
Socket socket = new Socket(serverAddr, 23);
connected = true;

while (connected) {
try {
Log.d("ClientActivity", "C: Sending command.");
PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);
out.println("mypass*");
Log.d("ClientActivity", "C: Sent.");





BufferedReader r = new BufferedReader(new InputStreamReader(socket.getInputStream()));
final StringBuilder total = new StringBuilder();
String line;
while ((line = r.readLine()) != null) {
total.append(line);
Log.d("Server response", line.toString());

}





handler.post(new Runnable() {
public void run() {


if (total.toString().contentEquals("status:open")) {

myTextView.setText(R.string.status_open);
} else {
myTextView.setText(R.string.status_closed);
}

}
});







connected = false;

} catch (Exception e) {
Log.e("ClientActivity", "S: Error", e);
}

}


socket.close();
Log.d("ClientActivity", "C: Closed.");


} catch (Exception e) {
Log.e("ClientActivity", "C: Error", e);
connected = false;
}
}
}
}

So my 2 questions are:

  1. I have no idea how to get my buttons to interact with the socket where I send commands to my server. After I connect, I have (4) buttons that need to send various commands through the socket (which is running in a separate thread that was kicked off by clicking on the 'connect' button I have). (Look for the '// CODE HERE FOR STATUS REQUEST BUTTON?' comments for where I was expecting to put the code for the various buttons)

  2. I need to move the

    'handler.post(new Runnable() {'

block of code into my main loop:

(while ((line = r.readLine()) != null) {
total.append(line);
Log.d("Server response", line.toString());
}

However, when I do that, and change:

if (total.toString().contentEquals("status:open")) {

to

if (line.toString().contentEquals("status:open")) {

(because I want to actually only read one line at a time), Eclipse complains with the following message: Cannot refer to a non-final variable line inside an inner class defined in a different method

And if I try to change the line above that...: String line; to static String line;

Eclipse complains on the next line:

while ((line = r.readLine()) != null) {

saying: The final local variable line may already have been assigned

I can't even believe I've gotten this far as I'm a complete newbie, but now I've hit a wall.

like image 229
Domiflichi Avatar asked Nov 04 '22 03:11

Domiflichi


1 Answers

For your second question: create a new final to use in your runnable

while ((line = r.readLine()) != null) {
    total.append(line);
    Log.d("Server response", line.toString());
    final String status = line;
    handler.post(new Runnable() {
        public void run() {
            if (status.contentEquals("status:open")) {
                myTextView.setText(R.string.status_open);
            } else {
                myTextView.setText(R.string.status_closed);
            }
        }
    });
}

For your first question: You've picked quite a complicated task. Your Thread needs to check some condition that you set from outside so the thread can decide what he needs to do next. From those buttons you could change e.g. an AtomicInteger that is 0 as long as there is nothing to do, 1 if you want to switch on the light, 2 ... etc

Your Thread would check the value, reset it to 0 (in one .getAndSet(0)) and do what he is supposed to do.

Edit: that's how it would look like

the Thread

public class LoopingNetworkThread extends Thread {
    public static final int TASK_END = -1;
    public static final int TASK_NOOP = 0;
    public static final int TASK_LIGHTS_ON = 1;
    public static final int TASK_LIGHTS_OFF = 2;

    private final AtomicInteger mNextTask = new AtomicInteger(0);

    /* Executed in this threads context */
    @Override
    public void run() {
        openSocket();
        int currentTask;
        while ((currentTask = mNextTask.getAndSet(TASK_NOOP)) != TASK_END) {
            switch (currentTask) {
                case TASK_LIGHTS_ON:
                    sendLightsOn();
                    break;
                case TASK_LIGHTS_OFF:
                    sendLightsOff();
                    break;
                default:
                    keepAlive();
                    break;
            }
            // depending on your requirements sleep some time inbetween.
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                // ignore
            }
        }
        // the while ends once you set task to TASK_END
        closeSocket();
    }

    private void openSocket() {  }
    private void closeSocket() {  }
    private void keepAlive() {  }
    private void sendLightsOn() {  }
    private void sendLightsOff() {  }

    /* Executed in a different thread context */
    public int setNextTask(int task){
        // return what we overwrite here, maybe that is useful.
        return mNextTask.getAndSet(task);
    }
}

your Activity

public class YourActivity extends Activity {
    private LoopingNetworkThread mThread;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        mThread = new LoopingNetworkThread();
        View startButton = findViewById(R.id.button1);
        View stopButton = findViewById(R.id.button2);
        startButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                mThread.start();
            }
        });
        stopButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                mThread.setNextTask(LoopingNetworkThread.TASK_END);
            }
        });
    }
}
like image 178
zapl Avatar answered Nov 09 '22 02:11

zapl