android threading and handler - Changing UI objects from a background thread - java

I've recently written a small android application to send and receive UDP messages over a local network.I have a UDP receiver thread that runs to listen for UDP packets, what I want to happen is a button to become enabled on the UI when a packet is received that contains a certain string of data. I know that this has to be done with a handler, the problem being that I have a small amount of knowledge about threads and very little knowledge about handlers. would someone be able to shed some light on how a handler could be put into my code? thanks
code:
public void startUDPlistener() {
// Creates the listener thread
LISTEN = true;
Thread listenThread = new Thread(new Runnable() {
#Override
public void run() {
try {
Log.i(LOG, "Listener started!");
DatagramSocket socket = new DatagramSocket(BROADCASTPORT);
socket.setSoTimeout(1500);
byte[] buffer = new byte[BUFFERSIZE];
DatagramPacket packet = new DatagramPacket(buffer, BUFFERSIZE);
while(LISTEN) {
try {
Log.i(LOG, "Listening for packets");
socket.receive(packet);
String data = new String(buffer, 0, packet.getLength());
Log.i(LOG, "UDP packet received from "+ packet.getAddress() +" packet contents: " + data);
}
catch(IOException e) {
Log.e(LOG, "IOException in Listener " + e);
}
}
Log.i(LOG, "Listener ending");
socket.disconnect();
socket.close();
return;
}
catch(SocketException e) {
Log.e(LOG, "SocketException in Listener " + e);
}
}
});
listenThread.start();
}

You basically just add one and send either a Message or a Runnable.
private final Handler mUiThreadHandler = new Handler(Looper.getMainLooper());
void fromAnyThread() {
final String importantDataFromBackGroundThread = "!!!";
mUiThreadHandler.post(new Runnable() {
#Override
public void run() {
System.out.println("Hi from Ui Thread:" + importantDataFromBackGroundThread);
}
});
}
The Handler handles all messages / runnables within the Thread it runs in. If you specify Looper.getMainLooper() you are guaranteed that this is the main thread.

Related

How can a Java thread object call a method from the original class?

I have a client-server chat program, where the server will create a a new thread for each accepted connection, where NewClient extends Thread to handle requests from a specific client.
Here is a snippet of the server code:
public void startServer() {
// thread to handle requests
new Thread(() -> {
try (ServerSocket serverSocket = new ServerSocket(port)) {
System.out.println("Server started at " + new Date());
while (true) {
// listen for connection request
Socket socket = serverSocket.accept();
// increment total clients connected
clientCount++;
// create new client
NewClient newClient = new NewClient(socket, clientCount);
// add client to active client list
activeClientList.add(newClient);
// start thread
newClient.start();
}
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
In NewClient, the thread will loop and check the input stream. If an input stream is detected, I want it to broadcast the message in the input stream to all clients.
Here is the run method of NewClient:
public void run() {
while(true) {
try {
// read from client
Data message = (Data) objectInputStream.readObject();
System.out.println("Received " + message.text + " from " + message.name);
// broadcast to all clients
// broadcastMessage(message)
} catch (IOException | ClassNotFoundException e) {
System.out.println("Failed to get input stream");
e.printStackTrace();
}
}
}
Back in the server class, there is a method called broadcastMessage(Data data), which will loop through all connected clients in an array list and use the the output streams to send the data to each client.
Here is the method:
public synchronized void broadcastMessage(Data data) {
Integer size = activeClientList.size();
// loop through all clients
for(int i=size-1;i>=0;i--) {
NewClient client = activeClientList.get(i);
if(!client.writeToClient(data)) {
activeClientList.remove(i);
// notify users of disconnect
Data update = new Data(data.name, data.name + " has disconnected.");
broadcastMessage(update);
}
}
}
I understand that if this was an anonymous thread, I can call the broadcast method from within the server. How can I call this method within a thread class?
Thank you in advanced for your feedback.
// create new client
NewClient newClient = new NewClient(socket, clientCount, this);
Thank you Johannes Kuhn for pointing out that the server can be passed into the thread constructor to call the method.

Android keep socket connection open on thread

thank you for your help.
I'm trying to stream sensor data from my android device to a server via TCP socket. I'm fairly new to Android and threads are a tough concept for me to grasp.
I have two methods, connectToServer() and sendDataToServer(). connectToServer() is called once at startup and sendDataToSever() is called over and over again at about 100 HZ. I would like to open the socket at connectToServer() and leave it open always, so that sendDataToServer() can send data on that socket repeatedly.
public static void connectToServer(){
sendThread = new Thread(new Runnable() {
public void run() {
mySocket = null;
os = null;
try {
mySocket = new Socket(PC_IP, PORT);
os = new DataOutputStream(mySocket.getOutputStream());
} catch (UnknownHostException exception) {
Log.d("sunnyDay", exception.getMessage());
} catch (IOException exception) {
Log.d("sunnyDay", exception.getMessage());
}
}
});
sendThread.start();
}
public static void sendDataToServer(byte[] data) {
String dataString = Arrays.toString(data);
// send this String to the server
sendThread = new Thread(new Runnable() {
public void run() {
if (mySocket != null && os != null) {
try {
os.writeBytes(dataString + "\n");
} catch (IOException exception) {
Log.d("sunnyDay", exception.getMessage());
}
}
}
});
sendThread.start();
}
The only way I've been able to repeatedly send data is by closing and reopening the socket every time in the same thread call, although I feel like this is not the solution.
I tried to have them both on the same thread so that the socket connection is still there, I'm assuming this is where I'm missing something about threads.
Any input is greatly appreciated.
Thanks!
os.flush() after os.writeBytes(dataString + "\n");

changing from runnable to async task

I've got an app reading from a socket. I've tested this on a phone with API 18 on. If I try it on a phone with API 24 it throws a NetworkOnMainThread exception.
I've read that I need to do this in AsyncTask for the later API's, or I'm doing something else wrong. I'm unsure what I need to do with my code to convert it to an AsyncTask for the way I'm waiting for input stream data?
This is my read input stream class.
class receiveData extends Thread {
private volatile boolean exit = false;
DataInputStream in;
byte[] fullBuffer = new byte[7];
byte[] buffer = new byte[100];
int bytes; // bytes returned from read()
int bytesCount = 0;
public void run(){
try {
if (socket.isConnected()) {
in = new DataInputStream(socket.getInputStream());
}
} catch(Exception e) {
Log.d(TAG, "in receiveData - run exception - " + e.toString());
}
while(!exit) {
try {
bytes = in.read(buffer);
System.arraycopy(buffer, 0, fullBuffer, bytesCount, bytes);
bytesCount = bytesCount + bytes;
if (bytesCount >= 7) {
h.obtainMessage(NOW_DATA_RECEIVED, bytesCount, -1, fullBuffer).sendToTarget(); // Send to message queue Handler
bytesCount = 0;
} else {
Log.d(TAG, "Receive Error");
bytesCount = 0;
}
} catch(Exception e) {
Log.d(TAG, "Read Error - " + e.toString());
}
}
}
public void stopRdThread() {
exit = true;
try {
socket.close();
} catch(Exception e) {
Log.d(TAG, "error closing socket - " + e.toString());
}
}
/* Call this from the main activity to send data to the remote device */
public void write(byte[] message) {
try {
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
out.write(message);
out.flush();
} catch (IOException e) {
Log.d(TAG, "...Error data send: " + e.getMessage() + "...");
}
}
}
I'm starting it like this.
private receiveData rd;
// start receiver thread
rd = new receiveData();
rd.start();
Like I said it's working great in API 18 but won't in API 24. All the examples I've seen are for one off tasks in the background, such as downloading a picture, not threads that are left running waiting for data.
Well all newest androids API's will throw exceptions when you try connect network in ui thread.
The only way is to use AsyncTask or use Handler;
final Handler handler = new Handler();
handler.post(new Runnable() {
#Override
public void run() {
//Do something
}
});
Note: If you want to use network and then display things on UI then you must use AsyncTask.

How to create and maintain a persistent socket connection in Android WiFiDirect?

I'm using WiFiDirect to connect two phones and transfer images between them. I'm able to connect the devices and transfer 1 image from client to server. I'm failing when I try to transfer more than 1 image.
I use onConnectionInfoAvailable method to launch my server and client threads.
#Override
public void onConnectionInfoAvailable(WifiP2pInfo info) {
Log.i(TAG, "GO address " + info.groupOwnerAddress.getHostAddress() + " isGroupOwner " + info.isGroupOwner);
this.info = info;
if (info.groupFormed && info.isGroupOwner) {
ServerRunnable serverRunnable = new ServerRunnable(imageToBeSet);
Thread serverThread = new Thread(serverRunnable);
serverThread.start();
} else {
//Start client thread and transfer image.
}
ServerRunnable :
public class ServerRunnable implements Runnable {
public void run() {
try {
ServerSocket serverSocket = new ServerSocket(PORT);
Socket clientSocket = serverSocket.accept();
while (connected) {
try {
InputStream inputStream = clientSocket.getInputStream();
final byte[] result = streamToByteArray(inputStream);
MainActivity.this.runOnUiThread(new Runnable() {
#Override
public void run() {
Bitmap bitmap = BitmapFactory.decodeByteArray(result, 0, result.length);
clientImage.setImageBitmap(bitmap);
}
});
} catch (Exception e) {
//Handle exception
}
}
} catch (Exception e) {
//Handle exception
}
}
}
ClientRunnable :
public class ClientRunnable implements Runnable {
public void run() {
try {
Log.d("ClientActivity", "C: Connecting...");
Thread.sleep(2000l);
socket.bind(null);
socket.connect(new InetSocketAddress(host, PORT), 5000);
while (connected) {
if(outputStream == null) {
try {
outputStream = socket.getOutputStream();
} catch (Exception e) {
// Handle exception
}
}
}
} catch (Exception e) {
// Handle exception
}
}
public void write(byte[] bytes) {
try {
outputStream.write(bytes, 0, bytes.length);
} catch (IOException e) {
e.printStackTrace();
}
}
}
The above write method does not work. The OutputStream is connected to the server socket when I debug I see the info about me server (ip address and post number). Not sure why the write is not being read by my server. The socket shows as connected too.
The first transfer works fine. I have a layout with Previous and Next buttons. When the button is pressed, I want to read the image byte array and transfer that. I'm unable to do this part.
If anyone knows how I can create and maintain a persistent socket connection, or any other way to transfer multiple images from client to server using WiFiDirect please let me know. I based this off of the WiFiDirectDemo in Android SDK samples.
Thanks,
Akash
P.S.: I've also looked at the WiFiP2PDemo which uses a WifiP2pDnsSdService. I'm currently trying to understand how they are keeping the sockets open for continuous chat.

Android Socket Programming, continuous server-client communication (on Button-hit)

So, i have a Android-App(Client) and a Java-program(Server), with a One-time socket communication, whenever the android app connects to my server in a special activity (working fine).
Because my server is embedded in a bigger program (with Swing-components, where the server takes its informations from), i have this (reduced) code here:
//somewhere in my Swing-Application
Server myServer = new Server();
myServer.start();
//...
public class Server extends Thread {
#Override
public void run() {
try {
ServerSocket serverSocket = new ServerSocket(8090);
try {
while (true) {
System.out.println("Server is waiting for connections...");
socket = serverSocket.accept();
startHandler(socket);
}
} finally {
serverSocket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
private void startHandler(final Socket socket) throws IOException {
System.out.println("Client connected to Server");
Thread thread = new Thread() {
#Override
public void run() {
try {
writer = new OutputStreamWriter(socket.getOutputStream(), "UTF-8");
//doing something usefull, i am sending a JSON-String, which i´ll parse in my app.
writer.write(someStringContainingJSONString);
writer.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
closeSocket();
}
}
private void closeSocket() {
try {
socket.close();
} catch (IOException e) {
}
}
};
thread.start();
}
In my Android-App i have:
public void onCreate(Bundle savedInstanceState) {
viewJSON = new Runnable() {
#Override
public void run() {
getJSON();
}
};
Thread thread = new Thread(null, viewJSON, "MagentoBackground");
thread.start();
myProgressDialog = ProgressDialog.show(myActivity.this, "Please wait...", "Retrieving data ...", true);
}
private void getJSON() {
try {
socket = new Socket(serverIPAddress, SERVER_PORT);
BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8"));
String help = reader.readLine();
// parse this String according to JSON, is working fine!
}
// catch and so on...
Now, i want the app, to recieve data, whenever i hit a button "send data" from my Swing-Application, to have the newest data available.
On the other hand, i want the server to recieve data (also a JSON-String) when i make changes in my Android app. The String should also be send when i hit a specific button.
How can i do that? The problem is the threading issue(otherwise my swing application wouldn´t work) combined with networking. If i don´t close the socket, i cannot continue with my program properly (or at least, it seems so with my code right now)
Can you help me out here?
Thank you very much in advance for your help and thoughts.
Best, Andrea

Categories

Resources