I am trying to provide feedback in a JavaFX 8 application when a user chooses a menu item that launches a blocking process in another thread. In my real application it's a file download, but I have created a test case using minimal code by way of example:
import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.control.MenuButton;
import javafx.scene.control.ToolBar;
import javafx.scene.control.MenuItem;
import javafx.stage.Stage;
public class BlockingThreadTestCase extends Application {
public static void main(String[] args) {
launch(args);
}
@Override
public void start(Stage primaryStage) throws Exception {
MenuItem menuItem = new MenuItem("Start");
MenuButton menuButton = new MenuButton();
menuButton.setText("Async Process");
menuButton.getItems().addAll(menuItem);
menuItem.setOnAction(event -> {
menuButton.setText("Running...");
Platform.runLater(() -> {
try {
// Simulate a blocking process
Thread.sleep(5000);
} catch (Exception e) {
e.printStackTrace();
}
menuButton.setText(menuButton.getText() + "Done!");
});
});
final ToolBar toolbar = new ToolBar(menuButton);
final Scene scene = new Scene(toolbar);
primaryStage.setScene(scene);
primaryStage.setWidth(150);
primaryStage.show();
}
}
Here's how it's supposed to work: When you select the "Start" menu item, the main menu text should immediately change to "Running...", and then it should append "Done!" after the 5-second sleep that simulates my file download.
What is actually happening is both text updates are firing after the blocking process is done, even though I'm using Platform.runLater(). What am I doing wrong?
Platform.runLaterwill execute theRunnableblock ON the Event Dispatching Thread, which means it will block your UI. This isn't how you want to delayed callbacks, I'm not familiar with JavaFX, but you want some kind ofTimerwhich can be executed (with a delay of 5seconds) and which when triggered, can update the state of you menu button within the the context of the EDT