Concurrency in JavaFX - task is not finished - multithreading

I have two tasks, one to change screen opacity, the other to perform a complex computation. I wanted the computation to begin only after the screen fading has actually changed so I wrote the following:
Task<Void> task = new Task<Void>() {
#Override
protected Void call() throws Exception {
stage.getScene().getRoot().setOpacity(0.2);
stage.show();
return null;
}
};
Task<Void> task2 = new Task<Void>() {
#Override
protected Void call() throws Exception {
// Complex computation
return null;
}
};
task.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
#Override
public void handle(WorkerStateEvent paramT) {
// this busy waiting line was added in attempt to force the
// fading before beginning second task
while (stage.getScene().getRoot().getOpacity() != 0.2) {}
stage.show();
Platform.runLater(task2);
}
});
task2.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
#Override
public void handle(WorkerStateEvent paramT) {
stage.getScene().getRoot().setOpacity(1);
stage.show();
}
});
task.run();
However, sometimes I see the computation beginning and the screen fading only after it's done. Sometimes it does work. So I think there's a threading issue here. Note: When I'm on debug mode and stop at each task it really works everytime. What can be done to force the screen fade before the second task begins?
EDIT:
As suggested, I changed the code to the following:
Task<Void> task1 = new Task<Void>() {
#Override
protected Void call() throws Exception {
// Long computation
return null;
}
};
Task<Void> task = new Task<Void>() {
#Override
protected Void call() throws Exception {
FadeTransition ft = new FadeTransition(Duration.millis(1), stage.getScene().getRoot());
ft.setFromValue(1.0);
ft.setToValue(0.1);
ft.setCycleCount(1);
//ft.setAutoReverse(true);
ft.play();
ft.setOnFinished(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
new Thread(task1).run();
}
});
return null;
}
};
task1.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
#Override
public void handle(WorkerStateEvent paramT) {
FadeTransition ft1 = new FadeTransition(new Duration(0.1), stage.getScene().getRoot());
ft1.setFromValue(0.1);
ft1.setToValue(1.0);
ft1.setCycleCount(1);
//ft.setAutoReverse(true);
ft1.play();
//stage.show();
}
});
new Thread(task).run();
Still I see the same behavior.

Replace
new Thread(task).run();
with
new Thread(task).start();

Related

JavaFX - Cancel Task doesn't work

In a JavaFX application, I have a method which takes a long time on large input. I'm opening a dialog when it is loading and I'd like the user to be able to cancel/close out the dialog and the task will quit. I created a task and added its cancellation in the cancel button handling. But the cancellation doesn't happen, the task doesn't stop executing.
Task<Void> task = new Task<Void>() {
#Override
public Void call() throws Exception {
// calling a function that does heavy calculations in another class
};
task.setOnSucceeded(e -> {
startButton.setDisable(false);
});
}
new Thread(task).start();
cancelButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
System.out.println("Button handled");
task.cancel();
}
);
Why isn't the task getting canceled when the button clicked?
You have to check on the cancel state (see Task's Javadoc). Have a look at this MCVE:
public class Example extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
Task<Void> task = new Task<Void>() {
#Override
protected Void call() throws Exception {
new AnotherClass().doHeavyCalculations(this);
return null;
}
};
Button start = new Button("Start");
start.setOnMouseClicked(event -> new Thread(task).start());
Button cancel = new Button("Cancel");
cancel.setOnMouseClicked(event -> task.cancel());
primaryStage.setScene(new Scene(new HBox(start, cancel)));
primaryStage.show();
}
private class AnotherClass {
public void doHeavyCalculations(Task<Void> task) {
while (true) {
if (task.isCancelled()) {
System.out.println("Canceling...");
break;
} else {
System.out.println("Working...");
}
}
}
}
}
Note that…
You should use Task#updateMessage(String) rather than printing to System.out, here it's just for demonstration.
Directly injecting the Task object creates a cyclic dependency. However, you can use a proxy or something else that fits your situation.

Javafx Updating UI from a Thread Java 8

I interested in one interesting task. I have UI in JavaFx with another thread which updates UI. I started updates from Platform.runLater. Code:
private void startUpdateDaemon() {
updateUserStatus();
updateTable();
}
private void startUpdateDaemonTask() {
Task task = new Task<Void>() {
#Override
protected Void call() throws Exception {
while (true) {
Platform.runLater(() -> {
startUpdateDaemon();
});
Thread.sleep(1000);
}
}
};
Thread th = new Thread(task);
th.setDaemon(true);
th.start();
}
#Override
public void initialize(URL location, ResourceBundle resources) {
startUpdateDaemonTask();
}
Also I have place in another class where I updates UI:
private void startUpdateDaemonTask() {
Task task = new Task<Void>() {
#Override
protected Void call() throws Exception {
while (true) {
Platform.runLater(new Runnable() {
#Override
public void run() {
updateGameStatus();
}
});
Thread.sleep(1000);
}
}
};
Thread th = new Thread(task);
th.setDaemon(true);
th.start();
}
So, finally I have two places with call "Platform.runLater" and different methods inside.
My question is Can I create only "one" method with one time call "Platform.runLater" and send to this method different methods which will be call ?? May be I can write finish method with consumers and send to him methods 'startUpdateDaemon()' and 'updateGameStatus()'?
Thanks a lot.
You can add a Runnable parameter to your method. This parameter is given to you Platform.runLater:
private void startUpdateDaemonTask(Runnable runner) {
Task task = new Task<Void>() {
#Override
protected Void call() throws Exception {
while (true) {
Platform.runLater(runner);
Thread.sleep(1000);
}
}
};
Thread th = new Thread(task);
th.setDaemon(true);
th.start();
}
Now you can invoke this method with your method references:
startUpdateDaemonTask(this::startUpdateDaemon);
startUpdateDaemonTask(this::updateGameStatus);

I want to change my WebView content from a HTML file using thread in javafx

Here is my code. I am using scene builder. The code is not working.For first time it loads the hello1.html but in thread the hello2.html does not load.
public class TwavlController implements Initializable {
/**
* Initializes the controller class.
*/
#FXML public WebView webPane;
private Service<Void> back_thread;
private WebEngine engine;
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
engine = webPane.getEngine();
final String html_file = "hello1.html"; //HTML file to view in web view
URL urlHello = getClass().getResource(html_file);
engine.load(urlHello.toExternalForm());
run();
}
private File last_update,current;
public void run(){
back_thread = new Service<Void>() {
#Override
protected Task<Void> createTask() {
return new Task<Void>() {
#Override
protected Void call() throws Exception {
updateMessage("hello2.html");
return null;
}
};
}
};
engine.userAgentProperty().bind(back_thread.messageProperty());
back_thread.restart();
}
}
I'm not really clear what you're trying to do here, but I think maybe you are looking for
public void run(){
back_thread = new Service<Void>() {
#Override
protected Task<Void> createTask() {
return new Task<Void>() {
#Override
protected Void call() throws Exception {
Platform.runLater(() ->
engine.load(getClass().getResource("hello2.html").toExternalForm()));
return null;
}
};
}
};
back_thread.restart();
}

Binding Properties and using them during lengthy operations

In my JavaFX Application I want to disable a couple of Buttons during a refresh of the data from a database.
I am using the disableProperty of the Buttons I want to disable.
Here is the basic JavaFX Application, modefied to illustrate my point:
public class BindLengthy extends Application {
BooleanProperty disable = new SimpleBooleanProperty(false);
#Override
public void start(Stage primaryStage) {
Button btn = new Button();
btn.setText("Say 'Hello World'");
btn.disableProperty().bind(disable);
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
disable.set(true);
try {
Thread.sleep(5000);
} catch (InterruptedException ex) {
Logger.getLogger(BindLengthy.class.getName()).log(Level.SEVERE, null, ex);
}
btn.setText("Done");
}
});
//Do all the other stuff that needs to be done to launch the application
//Like adding btn to the scene and so on...
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
When executed, on the click the Button stays in the "fired" mode, waits for 5 Seconds and then changes text and disables. While I want the text to change later, I want to disableProperty Change to take effect immediately!
I tried putting the lengthy operation, represented by Thread.sleep(5000) into a task and start it on a new Thread(task), but then obviously the text is changes before the Thread awakens.
I can't put the btn.setText("Done")into the Threadas it wouldn't be executed on the JavaFX-Thread(which it needs to). So I tried joining the Thread, yet that gives the same result as not putting it into an extra Thread as well.
How can I force the diableProperty to register the new value before executing my long operation?
Use a Task and use its onSucceeded handler to update the UI:
public class BindLengthy extends Application {
BooleanProperty disable = new SimpleBooleanProperty(false);
#Override
public void start(Stage primaryStage) {
Button btn = new Button();
btn.setText("Say 'Hello World'");
btn.disableProperty().bind(disable);
btn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
disable.set(true);
Task<String> task = new Task<String>() {
#Override
public String call() throws Exception {
Thread.sleep(5000);
return "Done" ;
}
});
task.setOnFailed(e ->
Logger.getLogger(BindLengthy.class.getName())
.log(Level.SEVERE, null, task.getException()));
task.setOnSucceeded(e -> {
btn.setText(task.getValue());
disable.set(false);
});
Thread t = new Thread(task);
t.setDaemon(true);
t.start();
}
});
//Do all the other stuff that needs to be done to launch the application
//Like adding btn to the scene and so on...
primaryStage.show();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}

Javafx change text before and after run task

I would like to update some text of my status bar label before and after I run a task.
This is the example code:
label.setText("please wait...");
Task<Void> task = new Task<Void>() {
#Override
protected Void call() throws Exception {
veryLongOperation();
return null;
}
};
Thread thread = new Thread(task);
thread.start();
while (thread.isAlive()) {
System.out.println("waiting...");
Thread.currentThread().sleep(10);
}
System.out.println("work done!");
label.setText("work done!");
With this example the text value of label can't be changed... how can I update the text?
Have you tried to override the Task#suceeded method like this:
Task<Void> task = new Task<Void>() {
#Override
protected Void call() throws Exception {
veryLongOperation();
return null;
}
#Override
protected void succeeded() {
System.out.println("work done!");
label.setText("work done!");
}
};
new Thread(task).start();

Resources