I am new to Android Development, but not new to Java.
One golden rule is to never block the GUI thread, nor the main thread.
The problem I am facing is that my app can send messages to a server, and the server displays the messages. When my server(PC) sends messages back to my android app, the app receives the message, and this is evident due to the log method.
The console, displays the message sent from the server, but the GUI app does not, this is because it freezes.
What I have done is set a thread, in side another thread.
`Main Thread > Thread(Connects to server) > Thread (Listens for incoming messages) > Thread (The Android Gui Thread).
The problem here is; the program freezes and the activity stops responding. Butt the console window does display the messages sent from the server.
My question is, why is the app freezing, when I'm not blocking any threads?
private void ConnectButtonPress() {
btnConnect.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
btnConnect.setVisibility(View.GONE);
btnDisconnect.setVisibility(View.VISIBLE);
btnSend.setEnabled(true);
enterET.setEnabled(true);
new Thread(new Runnable() {
public void run(){
try {
socket = new Socket(IPAddressET.getText().toString(), Integer.parseInt(portET.getText().toString())); //connect to server
input = new Scanner(socket.getInputStream());
pw = new PrintWriter(socket.getOutputStream(), true);
pw.println(usersName); //write the message to output stream
pw.flush();
new Thread(new Runnable() {
#Override
public void run() {
while (true){//this is where the problem is
try {
input = new Scanner(socket.getInputStream());
} catch (IOException e) {
e.printStackTrace();
}
String message = input.nextLine();
Log.i("LOL",message);
runOnUiThread(new Runnable() {
#Override
public void run() {
String message = input.nextLine();
displayMessages.append(message +"\n");
}
});
}
}
}).start();
//pw.close();
//pw.close(); //closing the connection
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
});
}
Well, this block:
runOnUiThread(new Runnable() {
#Override
public void run() {
String message = input.nextLine();
displayMessages.append(message +"\n");
}
});
is run indefinitely inside the while loop, and hence blocks the main thread. Why do you need to build your displayMessages in the main thread? I guess you can just put displayMessages.append(message +"\n"); inside your while loop.
If I'm not wrong, input.nextLine(); blocks the thread until a message is received from the server.
So, if I'm correct, when you receive a new message from the server, you run on the main thread to change the text view you have... but in the runOnUiThread you again call the input.nextLine();, when you already have read its value(String message = input.nextLine();), just before the runOnUiThread(new Runnable() {... call.
This basically causes, your main thread to wait for a new message from the server. You just have to use the value you already have, or, I suppose you can also play with the hasNextLine().
Related
I am updating an app that runs in Android 6.0 to Android 7.1. The app communicates via TCP, and the original code creates a new thread in which it creates a client socket. When I try to run it in Android 7.1, I get a NetworkOnMainThreadException. I thought this was odd, as I thought the TCP communication was happening on a new thread, and I do not get the Exception in Android 6.0. Snippets from the code are below.
// in MainActivity.java
public void ConnectToTCP() {
m_objThreadClient = new Thread(new Runnable() {
public void run() {
try {
clientSocket = new Socket("192.168.20.2", 5003);
BufferedReader input = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
String message; // message from TCP
// ...some code omitted for brevity...
clientSocket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
m_objThreadClient.start();
}
I tried moving the TCP communication to a HandlerThread, which many solutions online suggested. I wanted to avoid AsyncTask, as I saw warnings about using it to handle tasks longer than a few seconds. However, when I run the app with the HandlerThread, I get a NetworkOnMainThreadException in both Android 6.0 and Android 7.1. The exception is thrown in the line that creates the Client Socket. I used TCPThread.isAlive() to check that the thread exists, and it does.
// in TCPThread.java
public class TCPThread<T> extends HandlerThread {
private static final String TAG = "TCPThread";
private boolean mHasQuit = false;
public TCPThread() {
super(TAG);
}
#Override
public boolean quit() {
mHasQuit = true;
return super.quit();
}
public void connectToTCP() {
// I change some button appearances and UI things on the main thread, so I created a Handler for it
Handler mainUiHandler = new Handler(Looper.getMainLooper());
try {
// the next line is where the NetworkOnMainException gets thrown
clientSocket = new Socket("192.168.20.2", 5003);
BufferedReader input = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
String message; // message from TCP
// ...some code omitted for brevity...
clientSocket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
// in MainActivity.java
public void ConnectToTCP() {
TCPThread<String> mTCPThread = new TCPThread<>();
// this is a method defined in a different activity
String currentStatus = activityConnect.currentConnectStatus();
if (currentStatus.equals("Connected")) { // if connected and wanting to disconnect
mTCPThread.quitSafely();
} else {
mTCPThread.start();
mTCPThread.getLooper();
mTCPThread.connectToTCP();
}
I have a basic GUI in Java where there is a JButton,I have given a functionality to start the Server with that button. But when I click the button the program freezes. Is it because of the while loop? If so how can I overcome this?
Server Code
void connect_clients()
{
try {
ServerSocket listener = new ServerSocket(7700);
try {
while (true) {
Socket socket = listener.accept();
try {
PrintWriter out =
new PrintWriter(socket.getOutputStream(), true);
out.println(new Date().toString());
}
finally {
socket.close();
}
}
}
finally {
listener.close();
}
}
catch (IOException ex) {
Logger.getLogger(Test_Frame.class.getName()).log(Level.SEVERE, null, ex);
}
}
Your program is freezing because you are blocking the UI thread. You need to post this on a separate thread:
public void postListen()
{
new Thread(new Runnable()
{
public void run()
{
connect_clients();
}
}).start();
}
Call that method instead and it should run the connect_clients() method on a separate thread. The new thread will block until a client connects.
Here is method explanation of ServerSocket.accept():
Listens for a connection to be made to this socket and accepts it. The
method blocks until a connection is made.
Until there is data input to socket, your program will freeze. If it's another problem, please check your logs. There may be another problem.
I have a problem with a part of my code. My program have a thread that is getting input from the keyboard and have several threads that are waiting for that input.
The users selects first to what thread he is going to send that input. So lets says that we have 3 threads (0,1,2) plus the thread that gets the keyboard input. The user will select first what thread he wants to interact with and after that he will send the actual data to that thread.
I have a piece of code that is taking care of that process. I use ´LinkedBlockingQueue´ to achieve it.
The keyboard thread puts data in the Queue and the "workers" (the other 3 threads) get that data from that queue.
The problem is that all the threads are listening for that same Queue so I put an ID in that Queue to let the threads know if the data is directed to them or to other thread.
Here is the code:
Thread Thread_OUT = new Thread(new Runnable() {
#SuppressWarnings("unchecked")
#Override
public void run() {
while(true) {
try {
Object recibido= sharedQueue.take();
sharedQueue.put(recibido);
//System.out.println("Im the thread "+ clientID+" and I got "+recibido.toString());
if(Integer.parseInt(recibido.toString())==clientID){ // If it is for me I get the data
String x = CommandShellServer.data.get(clientID); // just get the data (it is in a hashmap)
CommandShellServer.data.clear(); // empty the hashmap
sharedQueue.clear();
OUT = do_something(x);
}
else{ // If it is not I will forward it to other thread
Thread.currentThread().wait(100);
// sharedQueue.put(recibido);
// sharedQueue.clear();
}
As you can see in the code what I do is checking if the thread that is handling the information is the one that is directed to If it is, I process it, and if it is no I put that the data again in the queue to let the other threads to check for it.
If I select the thread 0 to interact with it works. If I select others it doesn't.
Get rid of the shared queue, and let each thread have its own. Then, when you get an input, just dispatch it to the queue of appropriate thread that is intended to receive it.
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package Application;
import java.util.ArrayList;
import java.util.Scanner;
/**
*
* #author husseyn
*/
public class producteurConsomateur {
static Scanner clavier;
static ArrayList<String> queu;
public static void main(String[] args) {
queu=new ArrayList<>();
new Thread(){
#Override
public void run() {
clavier=new Scanner(System.in);
while (true) {
try {
sleep(1000);
} catch (Exception e) {
}
System.out.print("tape message :");
String nextLine = clavier.nextLine();
queu.add(nextLine);
// notifyAll();
}
}
}.start();
new Thread(){
#Override
public void run() {
while (true) {
try {
try {
wait();
} catch (Exception e) {
}
synchronized(this){
String get = queu.get(0);
String[] messageFormat = get.split(":");
String id=messageFormat[0];
if (id.toLowerCase().equals("id1")) {
String message=messageFormat[0];
queu.remove(0);
System.out.println("message recived to thread ID1 :"+message);
}}
} catch (Exception e) {
}
}
}
}.start();
new Thread(){
#Override
public void run() {
while (true) {
try {
try {
wait();
} catch (Exception e) {
}
synchronized(this){
String get = queu.get(0);
String[] messageFormat = get.split(":");
String id=messageFormat[0];
if (id.toLowerCase().equals("id3")) {
String message=messageFormat[0];
queu.remove(0);
System.out.println("message recived to thread ID3 :"+message);
}}
} catch (Exception e) {
}
}
}
}.start();
new Thread(){
#Override
public void run() {
while (true) {
try {
try {
wait();
} catch (Exception e) {
}
synchronized(this){
String get = queu.get(0);
String[] messageFormat = get.split(":");
String id=messageFormat[0];
if (id.toLowerCase().equals("id2")) {
String message=messageFormat[0];
queu.remove(0);
System.out.println("message recived to thread ID2 :"+message);
}}
} catch (Exception e) {
}
}
}
}.start();
}
}
And here I use a shared queue but you have to respect the message format is like id1:hello or id2:lol
I'm trying to do something potentially stupid, but I reckon it's a good idea, so bear with me. I tried to implement it, but I hit an awkward issue with sockets closing between threads - so I want some fresh eyes on the case.
Scenario
I want to write an object from a Client to a Server via sockets. There may be more than one Client communicating with the Server concurrently.
The object, a Message, is handled by the Server through its handling mechanisms. It is proposed that instead of the Server's main thread looking out for new incoming connections, a Listener thread is set up. Once it spots an incoming connection, it alerts the Server, storing the socket in a queue without receiving the data, so it can go back to listening quickly.
In its own time, the Server picks up the waiting socket, spawns a new thread, reads the Message, and closes the socket.
The code
Here's my first thoughts on how this should be implemented. There is a fundamental flaw in it which I will explain below.
Ignore the use of public fields - I'm just trying to make the code short for you guys
public class Server {
public boolean messageWaiting = false;
public static void main(String[] args) {
new Server().run();
}
public void run() {
Listener l = new Listener();
l.listen(this);
try {
while (true) {
System.out.println("I'm happily doing my business!");
Thread.sleep(1000);
if (messageWaiting) {
acceptMessages(l);
}
}
} catch (InterruptedException die) {}
}
private void acceptMessages(Listener l) {
while (!l.waiting.isEmpty()) {
try (
Socket client = l.waiting.poll();
ObjectInputStream ois = new ObjectInputStream(client.getInputStream())
) {
// Handle messages in new threads! (or a thread pool)
new Thread() {
public void run() {
try {
System.out.println(ois.readObject());
} catch (Exception ex) {
ex.printStackTrace();
}
}
}.start();
} catch (Exception ex) {
// Oh no! The socket has already been closed!
ex.printStackTrace();
}
}
}
}
public class Listener {
public ConcurrentLinkedQueue<Socket> waiting = new ConcurrentLinkedQueue<>();
public void listen(final Server callback) {
new Thread() {
public void run() {
try (ServerSocket rxSock = new ServerSocket(7500)) {
while (!isInterrupted()) {
try (Socket client = rxSock.accept()) {
// Once a new socket arrives, add it to the waiting queue
waiting.add(client);
// Alert the server
callback.messageWaiting = true;
} catch (IOException ex) {
ex.printStackTrace();
}
}
} catch (IOException ex) {
ex.printStackTrace();
}
}
}.start();
}
}
public class Client {
public static void main(String[] args) {
try (
Socket txSock = new Socket(InetAddress.getLoopbackAddress(), 7500);
ObjectOutputStream oos = new ObjectOutputStream(txSock.getOutputStream())
) {
oos.writeObject("This is a Message, trust me.");
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
What's wrong with this?
This:
I'm happily doing my business!
I'm happily doing my business!
java.net.SocketException: Socket is closed
at java.net.Socket.getInputStream(Unknown Source)
at Server.acceptMessages(Server.java:30)
at Server.run(Server.java:20)
at Server.main(Server.java:9)
This is because the Java 7 try blocks I'm using close the sockets once they're finished. So why don't I do this manually? Try yourself - you end up with a warning saying you're only ever going to call close() on a null object!
So, how do I avoid the whole issue of my incoming socket being closed before the Server thread picks up on it? Or is this a bad idea anyway and I should do something else?
Your statement in Listener
try (Socket client = rxSock.accept()) { ...
Is a try-with-resources for the client socket. As soon as you add it to the queue and exit the try block, the socket gets auto-closed.
what does this runtime-error mean?
I already googled it, some say, it belongs to timers, other say its a socket error and more say, it belongs to pictures. I have sockets and timers (lot of timers) and i have no idea, which of these causes it. SOmetimes it works for over an hour, and other times just for 5 minutes. Any Ideas?
A basic impression of what this error is about is enough. If i would post all the code, where it could happen, this page would be multiple kilometres long ( a little extreme of course, but it is lot of code.)
Found right now, it could belong to too many open files but im not using any extern files in my app.
Seems to be a memory leak, belonging to this part:
public static Runnable connection() throws IOException {
Log.e("Communication", "connection");
new Thread(new Runnable() {
public void run() {
Looper.prepare();
try {
serv = new ServerSocket(port); sock = serv.accept();
reader(); } catch (IOException e) {
e.printStackTrace();
}
}
}).start();
return null;
After deleting a part of the code above everything works fine again. Deleted the looper.prepare() and my app does not die anymore.
public static void sendJsonList(final List<String> jsonStrlist,
final String resturl) {
Thread t = new Thread() {
public void run() {
Looper.prepare();
/* Your HTTP clients code */
try {
for (String jsonStr : jsonStrlist) {
/* Loop logic */
response = client.execute(post);
if (response != null) {
/*reponse handler logic */
}
}
} catch (Exception e) {
e.printStackTrace();
}
Looper.loop();
}
};
t.start();
}