Reputation: 693
I've noticed a Platform.runLater() doesn't update the stage/screen immediately after running, so I'm guessing the painting is happening elsewhere or on another queued event. I'm curious as to when or how the actual painting or rendering to the screen is queued or signaled, after the runnable completes.
The following code will print 1.begin, 1.end, 2.begin, 2.end, 3.begin, 3.end
to the console, but the label never shows 1
, though the second runLater() waits.
package main;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import java.util.concurrent.CountDownLatch;
public class SmallRunLater extends Application {
SimpleStringProperty labelValue = new SimpleStringProperty("0");
@Override
public void start(Stage stage) throws InterruptedException {
Scene scene = new Scene(new Group());
stage.setWidth(550);
stage.setHeight(550);
final VBox vbox = new VBox();
vbox.setSpacing(5);
vbox.setPadding(new Insets(10, 0, 0, 10));
Label label = new Label();
label.textProperty().bind(labelValue);
Button startBtn = new Button("Start");
vbox.getChildren().addAll(startBtn, label);
startBtn.setOnAction((action) -> {
try {
Task task = new Task<Void>() {
@Override
protected Void call() throws Exception {
SmallWork work = new SmallWork();
work.doWork(labelValue);
return null;
}
};
new Thread(task).start();
} catch (Exception e) {
e.printStackTrace();
}
});
((Group) scene.getRoot()).getChildren().addAll(vbox);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
class SmallWork {
public void doWork(SimpleStringProperty labelValue) throws InterruptedException {
Platform.runLater(() -> {
System.out.println("1.begin");
labelValue.set("1");
System.out.println("1.end");
});
runNow(() -> {
System.out.println("2.begin");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
labelValue.set("2");
System.out.println("2.end");
});
Platform.runLater(() -> {
System.out.println("3.begin");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
labelValue.set("3");
System.out.println("3.end");
});
}
public static void runNow(Runnable r){
final CountDownLatch doneLatch = new CountDownLatch(1);
Platform.runLater(() -> {
try {
r.run();
} catch (Exception e) {
e.printStackTrace();
} finally {
doneLatch.countDown();
}
});
try {
doneLatch.await();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Upvotes: 2
Views: 498
Reputation: 3407
Yes, you are right, Platform.runLater()
(as implied by the name) doesn't run right away and just pushes the Runnable
to the internal queue. There is an internal render tick deep down. The Runnable
objects will be executed in the update tick just before render. The fact that label never shows "1" simply coincides with the fact that your runNow()
gets called immediately, in the same tick, so the two Runnable
objects get pushed to same queue and executed in the same tick within JavaFX
internal loop. Hence, the following happens:
I have tried running the code above and sometimes I can see 1, which means the two Runnable
objects were pushed in different ticks. Something like that:
Upvotes: 3