Reputation: 377
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);
}
}
}
});
Upvotes: 0
Views: 1459
Reputation: 82461
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.
Upvotes: 6
Reputation: 2245
You may be aware JavaFX rendering is handled by the thread named JavaFX Application Thread. You can see the threads in Eclipse Debug mode. So, if this thread is made to wait for another, then rendering will freeze. That is what is happening here.
BehaviorSubject
or some other suitable implementation of Subject
.onNext()
on the RxJava Subject
.Subject
gets the value, it reenables the buttons.Let me see if I can quickly put together a sample. In the meantime, you can try on your own.
Upvotes: 0
Reputation: 2294
I advise you to check this Oracle Tutorial on how to handle business logic outside of UI component.
Also, regarding your question using CompletableFuture would be of a great help to do such asynchronous work.
To be specific, whenComplete()
would help if you use the download process in a CompletableFuture
then display the message afterwards.
Here is a reference for whenComplete()
Upvotes: 2