Issue
I want the user to wait until my method call to download a file, which might take around 2 to 3 minutes, completes. I created a new thread for the method call and calling it inside run()
method. Based on the return value, a message should be displayed, whether successful or error. Hence I had to use join()
. But this still freezes since the UI is still waiting for the method call to complete. How can I overcome this issue? (Basically, I need to use a thread for UI to not to freeze, and at the same time, I want to display a message based on the method's return value)
btn.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
processMsg.setText("Do not close. This may take few minutes...");
Thread t1 = new Thread(new Runnable(){
@Override
public void run() {
try {
ret = /*Method-call-which-takes-long-time*/
} catch (IOException ex) {
Logger.getLogger(JavaFXApplication1.class.getName()).log(Level.SEVERE, null, ex);
} catch (SQLException ex) {
Logger.getLogger(JavaFXApplication1.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
t1.start();
try {
t1.join();
} catch (InterruptedException ex) {
Logger.getLogger(JavaFXApplication1.class.getName()).log(Level.SEVERE, null, ex);
}
if (ret != null){
if (ret.equals("TRUE")){
pane.getChildren().remove(processMsg);
processMsg.setText("File downloaded at location: "+ chosenDir.getAbsolutePath());
pane.add(processMsg,0,11,2,1);
}
else{
pane.getChildren().remove(processMsg);
processMsg.setText(ret);
pane.add(processMsg,0,11,2,1);
}
}
}
});
Solution
Based on the return value, a message should be displayed, whether successful or error. Hence I had to use
join()
.
This assumption is wrong. There are ways to update the GUI from a background thread. Platform.runLater
is the most flexible one, but in this case I simply recommend using Task
, which allows you to add handlers via onSucceeded
and onFailed
properties that are invoked when the call
logic completes with or without throwing an exception:
Task<MyReturnType> task = new Task<MyReturnType>() {
@Override
protected MyReturnType call() throws Exception {
return someLongCalculation(); // may throw an exception
}
};
task.setOnSucceeded(evt -> {
// we're on the JavaFX application thread here
MyReturnType result = task.getValue();
label.setText(result.toString());
});
task.setOnFailed(evt -> {
// we're on the JavaFX application thread here
label.setText("Error: " + task.getException().getMessage());
});
new Thread(task).start(); // alternatively use ExecutorService
Task.updateValue
, Task.updateMessage
and Task.updateProgress
allow you to communicate partial results back that can be observed using the value
, message
and progress
properties; those properties are updated on the JavaFX application thread.
Answered By - fabian