I've been trying to figure it out for some time,
I'm trying to write a chat - server app, just for learning.
I have an obstacle that I cannot understand,
The while loop inside of the GUI class freeze, but just when it trying to read:
public void run(){
Platform.runLater(() -> {
do {
try {
msg = getFromServer.readUTF(); // <--- freeze GUI
chatWindow.appendText(msg);
} catch (IOException e) {
e.printStackTrace();
}
} while (true);
});
}
You can see that it's running in a thread, but i did try to run it in other ways...
Only the DataInputStream make it stuck,
msg = getFromServer.readUTF();
And this it the methud that it's coming from:
public void connectToServer(){
try {
serverConectionState = new Socket("127.0.0.1", 6789);
getFromServer = new DataInputStream(serverConectionState.getInputStream());
sendToServer = new DataOutputStream(serverConectionState.getOutputStream());
onlineOffline.setText("Online");
onlineOffline.setTextFill(javafx.scene.paint.Color.web("#0076a3"));
} catch (IOException ex){
chatWindow.appendText("server connection fail\n");
}
}
This class, is the Controller.class - if it's make any diffrent.
My first question in stackoverflow, after a lot of help from the community.
Thanks in advance
I'm assuming the run method you showed is part of a Runnable that is executed in a background thread.
You are running the entire loop on the FX Application Thread (by using Platform.runLater()). So you block that thread and prevent it from repainting. You should run only the UI updates on the FX Application Thread:
public void run(){
do {
try {
String msg = getFromServer.readUTF(); // <--- freeze GUI
Platform.runLater(() -> chatWindow.appendText(msg));
} catch (IOException e) {
e.printStackTrace();
}
} while (true);
}
instead of using platform.runlater you should use java task, so that you can run the code in different thread, without freezing the UI thread
Related
I'm trying to execute a thread inside my JavaFx application and I also need to update my listview, reason why I used a Platform.runLater inside it. The problem is that it seems to be too slow, since it jumps the if state inside it. The listView.setItems(model.getEmailList()); part is executed without problem, but ignore the the condition even if when I print the two value I wanna compare they are different. How can I improve it? Because I cannot move that if outside the Platform since I'm trying to display it in a thread of my JavaFX application.
new Thread() {
#Override
public void run() {
while (true) {
try {
int currentOnServer = model.askNumbOfEmail();
if (emailForClient != currentOnServer) {
model.reLoadData();
Platform.runLater(() -> {
listView.setItems(model.getEmailList());
if (currentOnServer > emailForClient) {
new Alert(Alert.AlertType.INFORMATION, "Hai recevuto un email!").showAndWait();
}
});
emailForClient = currentOnServer;
}
} catch (IOException ex) {
Thread.currentThread().interrupt();
return;
} catch (ParseException ex) {
System.out.println("ParseException ERROR!");
}
}
}
}.start();
Your if statement doesn't work because you're changing part of the condition in a separate thread:
emailForClient = currentOnServer
This is a common problem when you're working with threads. You need to modify the logic of your code to facilitate parallel execution. You can create a temp variable to store emailForClient and use it inside Platform.runLater instead:
model.reLoadData();
final int currentEmail = emailForClient; // I'm assuming emailForClient is an int
Platform.runLater(() -> {
listView.setItems(model.getEmailList());
if (currentOnServer > currentEmail) {
new Alert(Alert.AlertType.INFORMATION, "Hai recevuto un email!").showAndWait();
}
});
emailForClient = currentOnServer;
I'm trying to make the program wait for the user to click on the GUI.
But Thread.sleep() just sleeps the entire GUI. So all I can see is an empty window.
code:
while (!enteredField) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Swing events need to happen on the Event Dispatch Thread.
Have you tried
SwingUtilities.invokeAndWait(() -> Thread.sleep(20))
https://docs.oracle.com/javase/8/docs/api/javax/swing/SwingUtilities.html#invokeAndWait-- ?
I have had problem like this before. so you are not alone. I made mistakes like this.
Your Problem
while (!enteredField) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Solution
Thread thread = new Thread(() -> {
while (!enteredField) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
thread.start();
* Detailed Response*
' What you did was that you created a thread inside a while loop. Basically You paused the whole GUI for 10 milliseconds, the whole program for matter of fact. Furthermore, I do not understand what your objective is. You said that you want to wait for the user to click on the GUI.Suggestions would be, listen for the button click. for example Button.setOnAction(() -> {}); That would do he job instead of sleeping. I just the only case that you would want to pause or sleep is if you are animating somethings.
Good day,
I have an infinite loop for a ServerSocket, working fine... The problem is when I try to start the ServerSocket with a button. My user interface "Freeze" don't move, anything, but the server is up and fine, here I have a ScreenShot:
http://i.gyazo.com/15d331166dd3f651fc7bda4e3670be4d.png
When I press the button "Iniciar" means Start server, the User Interface Freezes (ServerSocket infinite loop). I can't change my code because its working fine.
public static void iniciarServer() {
try {
appendString("\nServidor iniciado.");
System.out.println("asdasd");
} catch (BadLocationException e1) {
e1.printStackTrace();
}
try {
ss = new ServerSocket(1234, 3);
while (true) {
System.out.println("Esperando conexiones...");
appendString("\nEsperando conexiones...");
Socket s = ss.accept();
System.out.println("Conexión entrante: " + s.getRemoteSocketAddress());
appendString("\nConexión entrante: " + s.getRemoteSocketAddress());
conexiones++;
//System.out.println("Debug: conexiones SERVER: " + conexiones);
MultiThread mt = new MultiThread(s, conexiones);
mt.start();
///////////////////////////////////////////////////////////////
}
} catch (IOException e) {
System.out.println("Error Server: " + e.getMessage());
} catch (BadLocationException e) {
e.printStackTrace();
}
stopServer();
}
appendString();
Is for add some text to the JTextPane, but doesnot work because the UI freezes.
Is there any way to do an user interface that don't freeze by the infinite loop?
Thanks!
Swing is a single threaded framework, meaning any blocking or long running operation executed within the context of the Event Dispatching Thread will prevent it from processing the Event Queue, making your application hang.
It's also not thread safe, so you should never try and modify the state of any UI component from out side of the EDT.
Take a look at Concurrency in Swing and Worker Threads and SwingWorker for more details
public class ServerSocketWorker extends SwingWorker<Void, String> {
private JTextArea ta;
public ServerSocketWorker(JTextArea ta) {
this.ta = ta;
}
#Override
protected void process(List<String> chunks) {
for (String text : chunks) {
ta.append(text);
}
}
#Override
protected Void doInBackground() throws Exception {
ss = new ServerSocket(1234, 3);
while (true) {
publish("\nEsperando conexiones...");
Socket s = ss.accept();
publish("\nConexión entrante: " + s.getRemoteSocketAddress());
conexiones++;
//System.out.println("Debug: conexiones SERVER: " + conexiones);
MultiThread mt = new MultiThread(s, conexiones);
mt.start();
///////////////////////////////////////////////////////////////
}
}
#Override
protected void done() {
stopServer(); //??
}
}
To start it, you could use something like...
public void iniciarServer() {
ServerSocketWorker worker = new ServerSocketWorker(textAreaToAppendTo);
worker.execute();
}
As an example
The method ServerSocket.accept() is a blocking method. This means that Socket s = ss.accept(); stops the current thread until a connection to the server socket is opened.
Event dispatching in Swing is single threaded, the while loop and the blocking operation mentioned above, will keep the thread 'busy' and block all other interactions with the UI.
You should run your entire while loop in a separate thread. When you want to stop the server, you should also ensure that the while loop is exited and the thread completes execution.
My Server is build around support to update the listening socket, by doing so I use the following method. my problem occurs after this method is called for the second time, this is first called at start up from the main method then later it is called by clicking a button in a JFrame. what happens is that the JFrame freezes when this method is called via button, as you can see by the code I tried to make this method run a server in a new thread but it hasn't changed my outcome. Does anyone know how to fix this? or at least what is causing it? also any code after the method is called in the main doesn't get execute, so I believe it is a thread problem. (MiniServer extends Thread and is used to handle each connected client individually)
public static void startListening(final int port)
{
listeningThread = new Thread()
{
public void run()
{
try {
serverSocket = new ServerSocket(port);
while(!stop)
{
boolean loop = true;
while (loop)
{
serverSocket.setSoTimeout(1000);
try{
clientSocket = serverSocket.accept();
loop = false;
} catch (SocketTimeoutException e){
}
}
if(!clientSocket.equals(null))
{
MiniServer mini = new MiniServer(clientSocket);
mini.start();
clientSocket = null;
}
}
} catch (IOException e) {
}
}
};
listeningThread.run();
}
You need to be calling listeningThread.start(), which will create a new thread. Right now, you're just calling the thread's run() method on the current thread. The first time you do that it works, since you're on the main thread. The second time, though, you're on the UI thread, reacting to the button press. This causes your UI thread to block.
I have a program where I am loading a file while at the same time I am displaying a window to inform the user that the file is being loaded. I decided to make a FileLoader class that was a SwingWorker which actually handled loading the file and a ProgressWindow that implements PropertyChangeListener to inform the user about the status of the SwingWorker that was passed into it.
My code currently looks like this:
FileLoader loader = new FileLoader(filePath);
new ProgressWindow(loader, "Loading File", "Loading File");
//ProgressWindow's constructor calls loader.execute() inherited from SwingWorker
doc = loader.get(); //GUI Freezes when called
The problem is that whenever I call loader.get(), it freezes the GUI, thus the progress bar in the Progress Window doesn't run and the whole thing is pointless. As far as I can tell, this is because the thread controlling the GUI is the same thread that calls loader.get(), which goes on hold while loader.execute() is running.
So far, I've tried creating a new thread for either the loader.get() command or the loader.execute() method, and calling SwingUtilities.invokeLater() on the thread, but then the whole program freezes.
I've considered creating a ChangeListener for when SwingWorker.isDone() and then running loader.get(), but this would require some reworking of my code that I would rather not do.
Could anyone tell me what the best way is to get this to work?
get() is like join() in that it will block until called, and will wait for the SwingWorker to finish before being called. Using it wrongly can completely nullify all the advantages of using a SwingWorker in the first place.
Solution: Don't call get() until you know that the SwingWorker is done with its processing, by either calling it in the SwingWorker's done() method, or if you need to call it from the calling code, then in a PropertyChangeListener that has been added to the SwingWorker when the SwingWorker's "state" property is SwingWorker.StateValue.DONE.
Something like:
final FileLoader loader = new FileLoader(filePath);
loader.addPropertyChangeListener(new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if ("state".equals(evt.getPropertyName())) {
// since DONE is enum, no need for equals(...) method
if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
try {
loader.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
}
});
new ProgressWindow(loader, "Loading File", "Loading File");
Note: code not compiled nor tested
Edit: try/catch added.
So far, I've tried creating a new thread for either the loader.get() command or the loader.execute() method, and calling SwingUtilities.invokeLater() on the thread, but then the whole program freezes.
If you call SwingUtilities.invokeLater() on the thread that will execute the thread in the EDT which freezes the GUI. Instead, run the thread by calling it's start() method and only use SwingUtilities.invokeLater() when you need to update the progress bar in the PropertyChangeListener.
I have create a WorkerThread class which take care of Threads and GUI current/main thread .
i have put my GUI application in construct() method of WorkerThread when an event fire to start XXXServer then all threads are activate and GUI work smoothlly wihout freeze. have a look.
/**
* Action Event
*
* #see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
*/
public void actionPerformed(ActionEvent ae) {
log.info("actionPerformed begin..." + ae.getActionCommand());
try {
if (ae.getActionCommand().equals(btnStart.getText())) {
final int portNumber = 9990;
try {
WorkerThread workerThread = new WorkerThread(){
public Object construct(){
log.info("Initializing the Server GUI...");
// initializing the Server
try {
xxxServer = new XXXServer(portNumber);
xxxServer.start();
btnStart.setEnabled(false);
} catch (IOException e) {
// TODO Auto-generated catch block
log.info("actionPerformed() Start button ERROR IOEXCEPTION..." + e.getMessage());
e.printStackTrace();
}
return null;
}
};workerThread.start();
} catch (Exception e) {
log.info("actionPerformed() Start button ERROR..." + e.getMessage());
e.printStackTrace();
}
} else if (ae.getActionCommand().equals(btnStop.getText())) {
log.info("Exit..." + btnStop.getText());
closeWindow();
}
} catch (Exception e) {
log
.info("Error in ServerGUI actionPerformed==="
+ e.getMessage());
}
}