How to unit test that a JavaFX application launches - java

I've got a JavaFX application, and I want to test if it launches or not. How would I go about doing that? Is it possible with just JUnit, or can TestFX help me in that?
My main issue is: How do I shut down the application right after it has (succesfully) launched?
Example application class:
public class MovieDB extends Application {
#Override
public void start(final Stage primaryStage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(MovieDBController.class.getResource("MovieDB.fxml"), ResourceBundle.getBundle("bundles/bundle", new Locale("en")));
Parent root = fxmlLoader.load();
Scene scene = new Scene(root, 1024, 768);
StyleManager.getInstance().addUserAgentStylesheet(getClass().getResource("/css/MovieDB.css").getPath());
primaryStage.setTitle("MovieDB");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

Since the Application.launch method does not return until the application has exited, either via a call to Platform.exit or all of the application windows have been closed so you have to wrap it into another thread in order to terminate it.
If you call Platform.exit right after the JavaFX application launches you will get IllegalStateException. If you wait for a while so your JavaFX application can be initialized and then call Platform.exit, both your JavaFX application and your wrapper thread will be terminated without completing or throwing any exception. I couldn't find a way to work that out by using Platform.exit.
However, I managed to do it by using Thread.interrupt. Simply run your JavaFX application inside a wrapper thread, wait for a while and then interrupt your wrapper thread. This way the JavaFX application will be interrupted and throw InterruptedException. If it does not throw then there is an issue launching your JavaFX application.
Note that it may take longer than you wait for JVM to launch JavaFX application so this method does not guarantee that the JavaFX application is interrupted after it is properly launched which might result in a false negative situation.
Test Class
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.application.Application;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
public class JavaFXTest {
// Wrapper thread updates this if
// the JavaFX application runs without a problem.
// Declared volatile to ensure that writes are visible to every thread.
private volatile boolean success = false;
/**
* Test that a JavaFX application launches.
*/
#Test
public void testMain() {
Thread thread = new Thread() { // Wrapper thread.
#Override
public void run() {
try {
Application.launch(JavaFXTest.class); // Run JavaFX application.
success = true;
} catch(Throwable t) {
if(t.getCause() != null && t.getCause().getClass().equals(InterruptedException.class)) {
// We expect to get this exception since we interrupted
// the JavaFX application.
success = true;
return;
}
// This is not the exception we are looking for so log it.
Logger.getLogger(JavaFXTest.class.getName()).log(Level.SEVERE, null, t);
}
}
};
thread.setDaemon(true);
thread.start();
try {
Thread.sleep(3000); // Wait for 3 seconds before interrupting JavaFX application
} catch(InterruptedException ex) {
// We don't care if we wake up early.
}
thread.interrupt();
try {
thread.join(1); // Wait 1 second for our wrapper thread to finish.
} catch(InterruptedException ex) {
// We don't care if we wake up early.
}
assertTrue(success);
}
}
JavaFX Application Class
import java.io.IOException;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class JavaFX extends Application {
public static void main(String[] args) {
launch(args);
}
#Override
public void start(Stage primaryStage) throws IOException {
primaryStage.setTitle("JavaFX");
Label label = new Label("Hello World!");
StackPane root = new StackPane();
root.getChildren().add(label);
primaryStage.setScene(new Scene(root, 250, 250));
primaryStage.show();
}
}

Assuming that primaryStage is the only open stage, the JavaFX thread will automatically shut down when you call primaryStage.hide(). This is because JavaFX is set by default to shutdown when all stages are hidden, which can be changed by calling Platform.setImplicitExit(false).
More info here.

Related

JavaFX application freezes when using Robot mouse move [duplicate]

I try to run in JavaFX application background thread periodically, which modifies some GUI property.
I think I know how to use Task and Service classes from javafx.concurrent and can't figure it out how to run such periodic task without using Thread#sleep() method. It would be nice if I can use some Executor from Executors fabricate methods (Executors.newSingleThreadScheduledExecutor())
I tried to run Runnable every 5 sec, which restarts javafx.concurrent.Service but it hangs immediately as service.restart or even service.getState() is called.
So finally I use Executors.newSingleThreadScheduledExecutor(), which fires my Runnable every 5 sec and that Runnable runs another Runnable using:
Platform.runLater(new Runnable() {
//here i can modify GUI properties
}
It looks very nasty :( Is there a better way to do this using Task or Service classes?
You can use Timeline for that task:
Timeline fiveSecondsWonder = new Timeline(
new KeyFrame(Duration.seconds(5),
new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
System.out.println("this is called every 5 seconds on UI thread");
}
}));
fiveSecondsWonder.setCycleCount(Timeline.INDEFINITE);
fiveSecondsWonder.play();
for the background processes (which don't do anything to the UI) you can use old good java.util.Timer:
new Timer().schedule(
new TimerTask() {
#Override
public void run() {
System.out.println("ping");
}
}, 0, 5000);
Preface: This question is often the duplicate target for questions which ask how to perform periodic actions in JavaFX, whether the action should be done in the background or not. While there are already great answers to this question, this answer attempts to consolidate all the given information (and more) into a single answer and explain/show the differences between each approach.
This answer focuses on the APIs available in JavaSE and JavaFX and not third-party libraries such as ReactFX (showcased in Tomas Mikula's answer).
Background Information: JavaFX & Threads
Like most mainstream GUI frameworks, JavaFX is single-threaded. This means there's a single thread dedicated to reading and writing the state of the UI and processing user-generated events (e.g. mouse events, key events, etc.). In JavaFX this thread is called the "JavaFX Application Thread", sometimes shortened to just "FX thread", but other frameworks may call it something else. Some other names include "UI thread", "event-dispatch thread", and "main thread".
It is absolutely paramount that anything connected to the GUI showing on screen is only ever accessed or manipulated on the JavaFX Application Thread. The JavaFX framework is not thread-safe and using a different thread to improperly read or write the state of the UI can lead to undefined behavior. Even if you don't see any externally-visible problems, access to state shared between threads without the necessary synchronization is broken code.
Many GUI objects, however, can be manipulated on any thread as long as they aren't "live". From the documentation of javafx.scene.Node:
Node objects may be constructed and modified on any thread as long they are not yet attached to a Scene in a Window that is showing [emphasis added]. An application must attach nodes to such a Scene or modify them on the JavaFX Application Thread.
But other GUI objects, such as Window and even some subclasses of Node (e.g. WebView), are more strict. For instance, from the documentation of javafx.stage.Window:
Window objects must be constructed and modified on the JavaFX Application Thread.
If you're unsure about the threading rules of a GUI object, its documentation should provide the needed information.
Since JavaFX is single-threaded you also have to make sure never to block or otherwise monopolize the FX thread. If the thread is not free to do its job then the UI is never redrawn and new user-generated events can't be processed. Not following this rule can lead to the infamous unresponsive/frozen UI and your users are not happy.
It's virtually always wrong to sleep the JavaFX Application Thread.
Periodic Tasks
There are two different kinds of periodic tasks, at least for the purposes of this answer:
Periodic foreground "tasks".
This could include things such as a "blinking" node or periodically switching between images.
Periodic background tasks.
An example might be periodically checking a remote server for updates and, if there are any, downloading the new information and displaying it to the user.
Periodic Foreground Tasks
If your periodic task is short and simple then using a background thread is overkill and just adds unnecessary complexity. The more appropriate solution is to use the javafx.animation API. Animations are asynchronous but stay entirely within the JavaFX Application Thread. In other words, animations provide a way to "loop" on the FX thread, with delays between each iteration, without actually using loops.
There are three classes uniquely suited to periodic foreground tasks.
Timeline
A Timeline is made up of one or more KeyFrames. Each KeyFrame has a specified time of when it should complete. Each one can also have an "on finished" handler which is invoked after the specified amount of time has elapsed. This means you can create a Timeline with a single KeyFrame that periodically executes an action, looping as many times as you want (including forever).
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class App extends Application {
#Override
public void start(Stage primaryStage) {
Rectangle rect = new Rectangle(100, 100);
// toggle the visibility of 'rect' every 500ms
Timeline timeline =
new Timeline(new KeyFrame(Duration.millis(500), e -> rect.setVisible(!rect.isVisible())));
timeline.setCycleCount(Animation.INDEFINITE); // loop forever
timeline.play();
primaryStage.setScene(new Scene(new StackPane(rect), 200, 200));
primaryStage.show();
}
}
Since a Timeline can have more than one KeyFrame it's possible to have actions being executed at different intervals. Just keep in mind that the times of each KeyFrame do not stack. If you have one KeyFrame with a time of two seconds followed by another KeyFrame with a time of two seconds, both KeyFrames will finish two seconds after the animation is started. To have the second KeyFrame finish two seconds after the first one, its time needs to be four seconds.
PauseTransition
Unlike the other animation classes, a PauseTransition is not used to actually animate anything. It's main purpose is to be used as a child of SequentialTransition to put a pause between two other animations. However, like all subclassses of Animation it can have an "on finished" handler that's executed after it completes, allowing it to be used for periodic tasks.
import javafx.animation.PauseTransition;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class App extends Application {
#Override
public void start(Stage primaryStage) {
Rectangle rect = new Rectangle(100, 100);
// toggle the visibility of 'rect' every 500ms
PauseTransition pause = new PauseTransition(Duration.millis(500));
pause.setOnFinished(
e -> {
rect.setVisible(!rect.isVisible());
pause.playFromStart(); // loop again
});
pause.play();
primaryStage.setScene(new Scene(new StackPane(rect), 200, 200));
primaryStage.show();
}
}
Notice the on-finished handler invokes playFromStart(). This is necessary to "loop" the animation again. The cycleCount property can't be used since the on-finished handler is not invoked at the end of each cycle, it's only invoked at the end of the last cycle. The same thing is true of Timeline; the reason it works with Timeline above is because the on-finished handler isn't registered with the Timeline but with the KeyFrame.
Since the cycleCount property can't be used for PauseTransition for multiple cycles it makes it more difficult to loop only a certain number of times (rather than forever). You have to keep track of the state yourself and only invoke playFromStart() when appropriate. Keep in mind that local variables declared outside a lambda expression or anonymous class but used inside said lambda expression or anonymous class must be final or effectively final.
AnimationTimer
The AnimationTimer class is the lowest level of JavaFX's animation API. It's not a subclass of Animation and thus doesn't have any of the properties that were used above. Instead, it has an abstract method that, when the timer is started, is invoked once per frame with the timestamp (in nanoseconds) of the current frame: #handle(long). In order to execute something periodically with AnimationTimer (other than once per frame) will require manually calculating the time differences between invocations of handle using the method's argument.
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class App extends Application {
#Override
public void start(Stage primaryStage) {
Rectangle rect = new Rectangle(100, 100);
// toggle the visibility of 'rect' every 500ms
AnimationTimer timer =
new AnimationTimer() {
private long lastToggle;
#Override
public void handle(long now) {
if (lastToggle == 0L) {
lastToggle = now;
} else {
long diff = now - lastToggle;
if (diff >= 500_000_000L) { // 500,000,000ns == 500ms
rect.setVisible(!rect.isVisible());
lastToggle = now;
}
}
}
};
timer.start();
primaryStage.setScene(new Scene(new StackPane(rect), 200, 200));
primaryStage.show();
}
}
For most use cases similar to the above, using either Timeline or PauseTransition would be the better option.
Periodic Background Tasks
If your periodic task is time-consuming (e.g. expensive computations) or blocking (e.g. I/O) then a background thread needs to be used. JavaFX comes with some concurrency utilities built-in to aid with communication between background threads and the FX thread. These utilities are described in:
The Concurrency in JavaFX tutorial, and
The documentation of the classes in the javafx.concurrent package.
For periodic background tasks that need to communicate with the FX thread, the class to use is javafx.concurrent.ScheduledService. That class will execute its task periodically, restarting after successful execution, based on a specified period. If configured to do so it will even retry a configurable amount of times after failed executions.
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.concurrent.ScheduledService;
import javafx.concurrent.Task;
import javafx.concurrent.Worker.State;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;
public class App extends Application {
// maintain a strong reference to the service
private UpdateCheckService service;
#Override
public void start(Stage primaryStage) {
service = new UpdateCheckService();
service.setPeriod(Duration.seconds(5));
Label resultLabel = new Label();
service.setOnRunning(e -> resultLabel.setText(null));
service.setOnSucceeded(
e -> {
if (service.getValue()) {
resultLabel.setText("UPDATES AVAILABLE");
} else {
resultLabel.setText("UP-TO-DATE");
}
});
Label msgLabel = new Label();
msgLabel.textProperty().bind(service.messageProperty());
ProgressBar progBar = new ProgressBar();
progBar.setMaxWidth(Double.MAX_VALUE);
progBar.progressProperty().bind(service.progressProperty());
progBar.visibleProperty().bind(service.stateProperty().isEqualTo(State.RUNNING));
VBox box = new VBox(3, msgLabel, progBar);
box.setMaxHeight(Region.USE_PREF_SIZE);
box.setPadding(new Insets(3));
StackPane root = new StackPane(resultLabel, box);
StackPane.setAlignment(box, Pos.BOTTOM_LEFT);
primaryStage.setScene(new Scene(root, 400, 200));
primaryStage.show();
service.start();
}
private static class UpdateCheckService extends ScheduledService<Boolean> {
#Override
protected Task<Boolean> createTask() {
return new Task<>() {
#Override
protected Boolean call() throws Exception {
updateMessage("Checking for updates...");
for (int i = 0; i < 1000; i++) {
updateProgress(i + 1, 1000);
Thread.sleep(1L); // fake time-consuming work
}
return Math.random() < 0.5; // 50-50 chance updates are "available"
}
};
}
}
}
Here's a note from the documentation of ScheduledService:
Timing for this class is not absolutely reliable. A very busy event thread might introduce some timing lag into the beginning of the execution of the background Task, so very small values for the period or delay are likely to be inaccurate. A delay or period in the hundreds of milliseconds or larger should be fairly reliable.
And another:
The ScheduledService introduces a new property called lastValue. The lastValue is the value that was last successfully computed. Because a Service clears its value property on each run, and because the ScheduledService will reschedule a run immediately after completion (unless it enters the cancelled or failed states), the value property is not overly useful on a ScheduledService. In most cases you will want to instead use the value returned by lastValue.
The last note means binding to the value property of a ScheduledService is in all likelihood useless. The example above works despite querying the value property because the property is queried in the onSucceeded handler, before the service is rescheduled.
No Interaction with UI
If the periodic background task does not need to interact with the UI then you can use the standard APIs of Java instead. More specifically, either:
The java.util.Timer class (not javax.swing.Timer),
Or the more modern java.util.concurrent.ScheduledExecutorService interface.
Note that ScheduledExecutorService supports thread pools, unlike Timer which only supports a single thread.
ScheduledService is not an Option
If for whatever reason you can't use ScheduledService, but need to need to interact with the UI anyway, then you need to make sure the code interacting with the UI, and only that code, is executed on the FX thread. This can be accomplished by using Platform#runLater(Runnable).
Run the specified Runnable on the JavaFX Application Thread at some unspecified time in the future. This method, which may be called from any thread, will post the Runnable to an event queue and then return immediately to the caller. The Runnables are executed in the order they are posted. A runnable passed into the runLater method will be executed before any Runnable passed into a subsequent call to runLater. If this method is called after the JavaFX runtime has been shutdown, the call will be ignored: the Runnable will not be executed and no exception will be thrown.
NOTE: applications should avoid flooding JavaFX with too many pending Runnables. Otherwise, the application may become unresponsive. Applications are encouraged to batch up multiple operations into fewer runLater calls. Additionally, long-running operations should be done on a background thread where possible, freeing up the JavaFX Application Thread for GUI operations.
[...]
Heed the note from the above documentation. The javafx.concurent.Task class avoids this by coalescing updates to its message, progress, and value properties. This is currently implemented by using an AtomicReference and strategic get-and-set operations. If interested, you can take a look at the implementation (JavaFX is open source).
I would Prefer the PauseTransition:
PauseTransition wait = new PauseTransition(Duration.seconds(5));
wait.setOnFinished((e) -> {
/*YOUR METHOD*/
wait.playFromStart();
});
wait.play();
Here is a solution using Java 8 and ReactFX. Say that you want to periodically recompute the value of Label.textProperty().
Label label = ...;
EventStreams.ticks(Duration.ofSeconds(5)) // emits periodic ticks
.supplyCompletionStage(() -> getStatusAsync()) // starts a background task on each tick
.await() // emits task results, when ready
.subscribe(label::setText); // performs label.setText() for each result
CompletionStage<String> getStatusAsync() {
return CompletableFuture.supplyAsync(() -> getStatusFromNetwork());
}
String getStatusFromNetwork() {
// ...
}
Compared to Sergey's solution, you don't dedicate the whole thread to getting status from the network, but instead use the shared thread pool for that.
You can use ScheduledService too. I am using this alternative after noticing that during the use of Timeline and PauseTransition occurred some UI freezes in my application, especially when the user interacts with the elements of a MenuBar (on JavaFX 12). Using the ScheduledService these problems no longer occurred.
class UpdateLabel extends ScheduledService<Void> {
private Label label;
public UpdateLabel(Label label){
this.label = label;
}
#Override
protected Task<Void> createTask(){
return new Task<Void>(){
#Override
protected Void call(){
Platform.runLater(() -> {
/* Modify you GUI properties... */
label.setText(new Random().toString());
});
return null;
}
}
}
}
And then, use it:
class WindowController implements Initializable {
private #FXML Label randomNumber;
#Override
public void initialize(URL u, ResourceBundle res){
var service = new UpdateLabel(randomNumber);
service.setPeriod(Duration.seconds(2)); // The interval between executions.
service.play()
}
}
Was not easy find the way to programing this kind of behavior may be because my process reads I/O, works in milliseconds and I felt was often interrupted by GUI thread, but I made it by creating a BackgroundProcess class & with the help of ScheduledExecutorService.
In the controlle side, I use PauseTransition to read volatile (no contention) info only.
Sample code :
public class HelloApplication extends Application {
final ScheduledExecutorService scheduledExecutor = Executors.newSingleThreadScheduledExecutor();
final BackgroundProcess backgroundProcess = new BackgroundProcess();
#Override
public void start(Stage stage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("hello-view.fxml"));
Scene scene = new Scene(fxmlLoader.load(), 720, 610);
HelloController helloController = fxmlLoader.getController();
helloController.setBackgroundProcess(backgroundProcess);
stage.setTitle("Hello!");
stage.setScene(scene);
stage.show();
scheduledExecutor.scheduleWithFixedDelay(
backgroundProcess,
0, 111, TimeUnit.MILLISECONDS);
}
#Override
public void stop() throws Exception {
super.stop();
scheduledExecutor.shutdown();
}
...
}
public class BackgroundProcess implements Runnable{
volatile String status = "";
#Override
public void run() {
status = newStatus();
}
...
}
public class HelloController {
#FXML
protected void initialize() {
PauseTransition refresh = new PauseTransition(Duration.millis(111));
wait.setOnFinished((e) -> {
statusLabel.setText(backgroundProcess.status);
refresh.playFromStart();
});
refresh.play();
}
...
}
To read synchronized (contention) info I use ScheduledService to prepare the info and prevent interruptions in the JavaFX thread.
This is a more complex sample code:
public class HelloApplication extends Application {
final ScheduledExecutorService scheduledExecutor = Executors.newSingleThreadScheduledExecutor();
final BackgroundProcess backgroundProcess = new BackgroundProcess();
#Override
public void start(Stage stage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("hello-view.fxml"));
Scene scene = new Scene(fxmlLoader.load(), 720, 610);
HelloController helloController = fxmlLoader.getController();
helloController.setBackgroundProcess(backgroundProcess);
stage.setTitle("Hello!");
stage.setScene(scene);
stage.show();
scheduledExecutor.scheduleWithFixedDelay(
backgroundProcess,
0, 111, TimeUnit.MILLISECONDS);
}
#Override
public void stop() throws Exception {
super.stop();
scheduledExecutor.shutdown();
}
...
}
public class BackgroundProcess implements Runnable{
volatile String status = "";
LinkedTransferQueue<String> queue = new LinkedTransferQueue();
#Override
public void run() {
status = newStatus();
addToQueue();
}
...
}
public class HelloController {
static class SynchronizedInformation {
ArrayList<String> list;
}
private SynchronizedInformation prepareSynchronizedInformation() {
if (backgroundProcess.queue.isEmpty()) {
return null;
}
final SynchronizedInformation r = new SynchronizedInformation();
int size = backgroundProcess.queue.size();
r.list = new ArrayList<>(size);
String line;
while (r.list.size() < size && null != (line = backgroundProcess.queue.poll())) {
r.list.add(line);
}
return r;
}
private void refreshSynchronizedInformation(SynchronizedInformation synchronizedInformation) {
if (null != synchronizedInformation) {
synchronizedInformation.list.forEach(textArea::appendText);
}
statusLabel.setText(backgroundProcess.incoming);
}
#FXML
protected void initialize() {
ScheduledService<SynchronizedInformation> svc = new ScheduledService<>() {
#Override
protected Task<SynchronizedInformation> createTask() {
return new Task<SynchronizedInformation>() {
#Override
protected SynchronizedInformation call() throws Exception {
return prepareSynchronizedInformation();
}
};
}
};
svc.setDelay(Duration.millis(111));
svc.setOnSucceeded(e -> refreshSynchronizedInformation(svc.getValue()));
svc.start();
...
}

JavaFX stage opened on different thread makes program to finish [duplicate]

I try to run in JavaFX application background thread periodically, which modifies some GUI property.
I think I know how to use Task and Service classes from javafx.concurrent and can't figure it out how to run such periodic task without using Thread#sleep() method. It would be nice if I can use some Executor from Executors fabricate methods (Executors.newSingleThreadScheduledExecutor())
I tried to run Runnable every 5 sec, which restarts javafx.concurrent.Service but it hangs immediately as service.restart or even service.getState() is called.
So finally I use Executors.newSingleThreadScheduledExecutor(), which fires my Runnable every 5 sec and that Runnable runs another Runnable using:
Platform.runLater(new Runnable() {
//here i can modify GUI properties
}
It looks very nasty :( Is there a better way to do this using Task or Service classes?
You can use Timeline for that task:
Timeline fiveSecondsWonder = new Timeline(
new KeyFrame(Duration.seconds(5),
new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
System.out.println("this is called every 5 seconds on UI thread");
}
}));
fiveSecondsWonder.setCycleCount(Timeline.INDEFINITE);
fiveSecondsWonder.play();
for the background processes (which don't do anything to the UI) you can use old good java.util.Timer:
new Timer().schedule(
new TimerTask() {
#Override
public void run() {
System.out.println("ping");
}
}, 0, 5000);
Preface: This question is often the duplicate target for questions which ask how to perform periodic actions in JavaFX, whether the action should be done in the background or not. While there are already great answers to this question, this answer attempts to consolidate all the given information (and more) into a single answer and explain/show the differences between each approach.
This answer focuses on the APIs available in JavaSE and JavaFX and not third-party libraries such as ReactFX (showcased in Tomas Mikula's answer).
Background Information: JavaFX & Threads
Like most mainstream GUI frameworks, JavaFX is single-threaded. This means there's a single thread dedicated to reading and writing the state of the UI and processing user-generated events (e.g. mouse events, key events, etc.). In JavaFX this thread is called the "JavaFX Application Thread", sometimes shortened to just "FX thread", but other frameworks may call it something else. Some other names include "UI thread", "event-dispatch thread", and "main thread".
It is absolutely paramount that anything connected to the GUI showing on screen is only ever accessed or manipulated on the JavaFX Application Thread. The JavaFX framework is not thread-safe and using a different thread to improperly read or write the state of the UI can lead to undefined behavior. Even if you don't see any externally-visible problems, access to state shared between threads without the necessary synchronization is broken code.
Many GUI objects, however, can be manipulated on any thread as long as they aren't "live". From the documentation of javafx.scene.Node:
Node objects may be constructed and modified on any thread as long they are not yet attached to a Scene in a Window that is showing [emphasis added]. An application must attach nodes to such a Scene or modify them on the JavaFX Application Thread.
But other GUI objects, such as Window and even some subclasses of Node (e.g. WebView), are more strict. For instance, from the documentation of javafx.stage.Window:
Window objects must be constructed and modified on the JavaFX Application Thread.
If you're unsure about the threading rules of a GUI object, its documentation should provide the needed information.
Since JavaFX is single-threaded you also have to make sure never to block or otherwise monopolize the FX thread. If the thread is not free to do its job then the UI is never redrawn and new user-generated events can't be processed. Not following this rule can lead to the infamous unresponsive/frozen UI and your users are not happy.
It's virtually always wrong to sleep the JavaFX Application Thread.
Periodic Tasks
There are two different kinds of periodic tasks, at least for the purposes of this answer:
Periodic foreground "tasks".
This could include things such as a "blinking" node or periodically switching between images.
Periodic background tasks.
An example might be periodically checking a remote server for updates and, if there are any, downloading the new information and displaying it to the user.
Periodic Foreground Tasks
If your periodic task is short and simple then using a background thread is overkill and just adds unnecessary complexity. The more appropriate solution is to use the javafx.animation API. Animations are asynchronous but stay entirely within the JavaFX Application Thread. In other words, animations provide a way to "loop" on the FX thread, with delays between each iteration, without actually using loops.
There are three classes uniquely suited to periodic foreground tasks.
Timeline
A Timeline is made up of one or more KeyFrames. Each KeyFrame has a specified time of when it should complete. Each one can also have an "on finished" handler which is invoked after the specified amount of time has elapsed. This means you can create a Timeline with a single KeyFrame that periodically executes an action, looping as many times as you want (including forever).
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class App extends Application {
#Override
public void start(Stage primaryStage) {
Rectangle rect = new Rectangle(100, 100);
// toggle the visibility of 'rect' every 500ms
Timeline timeline =
new Timeline(new KeyFrame(Duration.millis(500), e -> rect.setVisible(!rect.isVisible())));
timeline.setCycleCount(Animation.INDEFINITE); // loop forever
timeline.play();
primaryStage.setScene(new Scene(new StackPane(rect), 200, 200));
primaryStage.show();
}
}
Since a Timeline can have more than one KeyFrame it's possible to have actions being executed at different intervals. Just keep in mind that the times of each KeyFrame do not stack. If you have one KeyFrame with a time of two seconds followed by another KeyFrame with a time of two seconds, both KeyFrames will finish two seconds after the animation is started. To have the second KeyFrame finish two seconds after the first one, its time needs to be four seconds.
PauseTransition
Unlike the other animation classes, a PauseTransition is not used to actually animate anything. It's main purpose is to be used as a child of SequentialTransition to put a pause between two other animations. However, like all subclassses of Animation it can have an "on finished" handler that's executed after it completes, allowing it to be used for periodic tasks.
import javafx.animation.PauseTransition;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class App extends Application {
#Override
public void start(Stage primaryStage) {
Rectangle rect = new Rectangle(100, 100);
// toggle the visibility of 'rect' every 500ms
PauseTransition pause = new PauseTransition(Duration.millis(500));
pause.setOnFinished(
e -> {
rect.setVisible(!rect.isVisible());
pause.playFromStart(); // loop again
});
pause.play();
primaryStage.setScene(new Scene(new StackPane(rect), 200, 200));
primaryStage.show();
}
}
Notice the on-finished handler invokes playFromStart(). This is necessary to "loop" the animation again. The cycleCount property can't be used since the on-finished handler is not invoked at the end of each cycle, it's only invoked at the end of the last cycle. The same thing is true of Timeline; the reason it works with Timeline above is because the on-finished handler isn't registered with the Timeline but with the KeyFrame.
Since the cycleCount property can't be used for PauseTransition for multiple cycles it makes it more difficult to loop only a certain number of times (rather than forever). You have to keep track of the state yourself and only invoke playFromStart() when appropriate. Keep in mind that local variables declared outside a lambda expression or anonymous class but used inside said lambda expression or anonymous class must be final or effectively final.
AnimationTimer
The AnimationTimer class is the lowest level of JavaFX's animation API. It's not a subclass of Animation and thus doesn't have any of the properties that were used above. Instead, it has an abstract method that, when the timer is started, is invoked once per frame with the timestamp (in nanoseconds) of the current frame: #handle(long). In order to execute something periodically with AnimationTimer (other than once per frame) will require manually calculating the time differences between invocations of handle using the method's argument.
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class App extends Application {
#Override
public void start(Stage primaryStage) {
Rectangle rect = new Rectangle(100, 100);
// toggle the visibility of 'rect' every 500ms
AnimationTimer timer =
new AnimationTimer() {
private long lastToggle;
#Override
public void handle(long now) {
if (lastToggle == 0L) {
lastToggle = now;
} else {
long diff = now - lastToggle;
if (diff >= 500_000_000L) { // 500,000,000ns == 500ms
rect.setVisible(!rect.isVisible());
lastToggle = now;
}
}
}
};
timer.start();
primaryStage.setScene(new Scene(new StackPane(rect), 200, 200));
primaryStage.show();
}
}
For most use cases similar to the above, using either Timeline or PauseTransition would be the better option.
Periodic Background Tasks
If your periodic task is time-consuming (e.g. expensive computations) or blocking (e.g. I/O) then a background thread needs to be used. JavaFX comes with some concurrency utilities built-in to aid with communication between background threads and the FX thread. These utilities are described in:
The Concurrency in JavaFX tutorial, and
The documentation of the classes in the javafx.concurrent package.
For periodic background tasks that need to communicate with the FX thread, the class to use is javafx.concurrent.ScheduledService. That class will execute its task periodically, restarting after successful execution, based on a specified period. If configured to do so it will even retry a configurable amount of times after failed executions.
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.concurrent.ScheduledService;
import javafx.concurrent.Task;
import javafx.concurrent.Worker.State;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;
public class App extends Application {
// maintain a strong reference to the service
private UpdateCheckService service;
#Override
public void start(Stage primaryStage) {
service = new UpdateCheckService();
service.setPeriod(Duration.seconds(5));
Label resultLabel = new Label();
service.setOnRunning(e -> resultLabel.setText(null));
service.setOnSucceeded(
e -> {
if (service.getValue()) {
resultLabel.setText("UPDATES AVAILABLE");
} else {
resultLabel.setText("UP-TO-DATE");
}
});
Label msgLabel = new Label();
msgLabel.textProperty().bind(service.messageProperty());
ProgressBar progBar = new ProgressBar();
progBar.setMaxWidth(Double.MAX_VALUE);
progBar.progressProperty().bind(service.progressProperty());
progBar.visibleProperty().bind(service.stateProperty().isEqualTo(State.RUNNING));
VBox box = new VBox(3, msgLabel, progBar);
box.setMaxHeight(Region.USE_PREF_SIZE);
box.setPadding(new Insets(3));
StackPane root = new StackPane(resultLabel, box);
StackPane.setAlignment(box, Pos.BOTTOM_LEFT);
primaryStage.setScene(new Scene(root, 400, 200));
primaryStage.show();
service.start();
}
private static class UpdateCheckService extends ScheduledService<Boolean> {
#Override
protected Task<Boolean> createTask() {
return new Task<>() {
#Override
protected Boolean call() throws Exception {
updateMessage("Checking for updates...");
for (int i = 0; i < 1000; i++) {
updateProgress(i + 1, 1000);
Thread.sleep(1L); // fake time-consuming work
}
return Math.random() < 0.5; // 50-50 chance updates are "available"
}
};
}
}
}
Here's a note from the documentation of ScheduledService:
Timing for this class is not absolutely reliable. A very busy event thread might introduce some timing lag into the beginning of the execution of the background Task, so very small values for the period or delay are likely to be inaccurate. A delay or period in the hundreds of milliseconds or larger should be fairly reliable.
And another:
The ScheduledService introduces a new property called lastValue. The lastValue is the value that was last successfully computed. Because a Service clears its value property on each run, and because the ScheduledService will reschedule a run immediately after completion (unless it enters the cancelled or failed states), the value property is not overly useful on a ScheduledService. In most cases you will want to instead use the value returned by lastValue.
The last note means binding to the value property of a ScheduledService is in all likelihood useless. The example above works despite querying the value property because the property is queried in the onSucceeded handler, before the service is rescheduled.
No Interaction with UI
If the periodic background task does not need to interact with the UI then you can use the standard APIs of Java instead. More specifically, either:
The java.util.Timer class (not javax.swing.Timer),
Or the more modern java.util.concurrent.ScheduledExecutorService interface.
Note that ScheduledExecutorService supports thread pools, unlike Timer which only supports a single thread.
ScheduledService is not an Option
If for whatever reason you can't use ScheduledService, but need to need to interact with the UI anyway, then you need to make sure the code interacting with the UI, and only that code, is executed on the FX thread. This can be accomplished by using Platform#runLater(Runnable).
Run the specified Runnable on the JavaFX Application Thread at some unspecified time in the future. This method, which may be called from any thread, will post the Runnable to an event queue and then return immediately to the caller. The Runnables are executed in the order they are posted. A runnable passed into the runLater method will be executed before any Runnable passed into a subsequent call to runLater. If this method is called after the JavaFX runtime has been shutdown, the call will be ignored: the Runnable will not be executed and no exception will be thrown.
NOTE: applications should avoid flooding JavaFX with too many pending Runnables. Otherwise, the application may become unresponsive. Applications are encouraged to batch up multiple operations into fewer runLater calls. Additionally, long-running operations should be done on a background thread where possible, freeing up the JavaFX Application Thread for GUI operations.
[...]
Heed the note from the above documentation. The javafx.concurent.Task class avoids this by coalescing updates to its message, progress, and value properties. This is currently implemented by using an AtomicReference and strategic get-and-set operations. If interested, you can take a look at the implementation (JavaFX is open source).
I would Prefer the PauseTransition:
PauseTransition wait = new PauseTransition(Duration.seconds(5));
wait.setOnFinished((e) -> {
/*YOUR METHOD*/
wait.playFromStart();
});
wait.play();
Here is a solution using Java 8 and ReactFX. Say that you want to periodically recompute the value of Label.textProperty().
Label label = ...;
EventStreams.ticks(Duration.ofSeconds(5)) // emits periodic ticks
.supplyCompletionStage(() -> getStatusAsync()) // starts a background task on each tick
.await() // emits task results, when ready
.subscribe(label::setText); // performs label.setText() for each result
CompletionStage<String> getStatusAsync() {
return CompletableFuture.supplyAsync(() -> getStatusFromNetwork());
}
String getStatusFromNetwork() {
// ...
}
Compared to Sergey's solution, you don't dedicate the whole thread to getting status from the network, but instead use the shared thread pool for that.
You can use ScheduledService too. I am using this alternative after noticing that during the use of Timeline and PauseTransition occurred some UI freezes in my application, especially when the user interacts with the elements of a MenuBar (on JavaFX 12). Using the ScheduledService these problems no longer occurred.
class UpdateLabel extends ScheduledService<Void> {
private Label label;
public UpdateLabel(Label label){
this.label = label;
}
#Override
protected Task<Void> createTask(){
return new Task<Void>(){
#Override
protected Void call(){
Platform.runLater(() -> {
/* Modify you GUI properties... */
label.setText(new Random().toString());
});
return null;
}
}
}
}
And then, use it:
class WindowController implements Initializable {
private #FXML Label randomNumber;
#Override
public void initialize(URL u, ResourceBundle res){
var service = new UpdateLabel(randomNumber);
service.setPeriod(Duration.seconds(2)); // The interval between executions.
service.play()
}
}
Was not easy find the way to programing this kind of behavior may be because my process reads I/O, works in milliseconds and I felt was often interrupted by GUI thread, but I made it by creating a BackgroundProcess class & with the help of ScheduledExecutorService.
In the controlle side, I use PauseTransition to read volatile (no contention) info only.
Sample code :
public class HelloApplication extends Application {
final ScheduledExecutorService scheduledExecutor = Executors.newSingleThreadScheduledExecutor();
final BackgroundProcess backgroundProcess = new BackgroundProcess();
#Override
public void start(Stage stage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("hello-view.fxml"));
Scene scene = new Scene(fxmlLoader.load(), 720, 610);
HelloController helloController = fxmlLoader.getController();
helloController.setBackgroundProcess(backgroundProcess);
stage.setTitle("Hello!");
stage.setScene(scene);
stage.show();
scheduledExecutor.scheduleWithFixedDelay(
backgroundProcess,
0, 111, TimeUnit.MILLISECONDS);
}
#Override
public void stop() throws Exception {
super.stop();
scheduledExecutor.shutdown();
}
...
}
public class BackgroundProcess implements Runnable{
volatile String status = "";
#Override
public void run() {
status = newStatus();
}
...
}
public class HelloController {
#FXML
protected void initialize() {
PauseTransition refresh = new PauseTransition(Duration.millis(111));
wait.setOnFinished((e) -> {
statusLabel.setText(backgroundProcess.status);
refresh.playFromStart();
});
refresh.play();
}
...
}
To read synchronized (contention) info I use ScheduledService to prepare the info and prevent interruptions in the JavaFX thread.
This is a more complex sample code:
public class HelloApplication extends Application {
final ScheduledExecutorService scheduledExecutor = Executors.newSingleThreadScheduledExecutor();
final BackgroundProcess backgroundProcess = new BackgroundProcess();
#Override
public void start(Stage stage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("hello-view.fxml"));
Scene scene = new Scene(fxmlLoader.load(), 720, 610);
HelloController helloController = fxmlLoader.getController();
helloController.setBackgroundProcess(backgroundProcess);
stage.setTitle("Hello!");
stage.setScene(scene);
stage.show();
scheduledExecutor.scheduleWithFixedDelay(
backgroundProcess,
0, 111, TimeUnit.MILLISECONDS);
}
#Override
public void stop() throws Exception {
super.stop();
scheduledExecutor.shutdown();
}
...
}
public class BackgroundProcess implements Runnable{
volatile String status = "";
LinkedTransferQueue<String> queue = new LinkedTransferQueue();
#Override
public void run() {
status = newStatus();
addToQueue();
}
...
}
public class HelloController {
static class SynchronizedInformation {
ArrayList<String> list;
}
private SynchronizedInformation prepareSynchronizedInformation() {
if (backgroundProcess.queue.isEmpty()) {
return null;
}
final SynchronizedInformation r = new SynchronizedInformation();
int size = backgroundProcess.queue.size();
r.list = new ArrayList<>(size);
String line;
while (r.list.size() < size && null != (line = backgroundProcess.queue.poll())) {
r.list.add(line);
}
return r;
}
private void refreshSynchronizedInformation(SynchronizedInformation synchronizedInformation) {
if (null != synchronizedInformation) {
synchronizedInformation.list.forEach(textArea::appendText);
}
statusLabel.setText(backgroundProcess.incoming);
}
#FXML
protected void initialize() {
ScheduledService<SynchronizedInformation> svc = new ScheduledService<>() {
#Override
protected Task<SynchronizedInformation> createTask() {
return new Task<SynchronizedInformation>() {
#Override
protected SynchronizedInformation call() throws Exception {
return prepareSynchronizedInformation();
}
};
}
};
svc.setDelay(Duration.millis(111));
svc.setOnSucceeded(e -> refreshSynchronizedInformation(svc.getValue()));
svc.start();
...
}

Running java Application with Multiple UI Threads

I'm trying to coding a very simple Client-Server Email project in java.
I've already code the communication between client and server using socket and now I'm tryng to code some test which includes also a very simple UI.
My idea is to create many threads as many clients I have and I want that every sigle thread starts opening a simple UI window created with Java FX but I have some problems.
This is the main class:
import java.io.*;
public class ClientController{
public static void main(String args[]) throws IOException {
ParallelClient c1=new ParallelClient("aaaa#gmail.com");
ParallelClient c2=new ParallelClient("bbbb#gmail.com");
c1.start();
c2.start();
}
}
This is the ParallelClient class:
import ...
public class ParallelClient extends Thread{
private String user;
public ParallelClient(String user){
this.user=user;
}
public void run(){
ClientApp app=new ClientApp();
try {
app.start(new Stage());
} catch (Exception e) {
e.printStackTrace();
}
...
}
...
}
And this is the ClientApp class which set the new window:
import ...
public class ClientApp extends Application {
#Override
public void start(Stage stage) throws Exception {
try {
Parent root = FXMLLoader.load(getClass().getResource("ui/client-management.fxml"));
stage.setTitle("ClientMail");
stage.setScene(new Scene(root, 1080, 720));
stage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
}
When I try to run the code I get the followung problem and I can't understand how to fix it:
Exception in thread "Thread-0" Exception in thread "Thread-1" java.lang.NoClassDefFoundError: Could not initialize class javafx.stage.Screen
at javafx.stage.Window.<init>(Window.java:1439)
at javafx.stage.Stage.<init>(Stage.java:252)
at javafx.stage.Stage.<init>(Stage.java:240)
at model.ParallelClient.run(ParallelClient.java:25)
java.lang.ExceptionInInitializerError
at javafx.stage.Window.<init>(Window.java:1439)
at javafx.stage.Stage.<init>(Stage.java:252)
at javafx.stage.Stage.<init>(Stage.java:240)
at model.ParallelClient.run(ParallelClient.java:25)
Caused by: java.lang.IllegalStateException: This operation is permitted on the event thread only; currentThread = Thread-1
at com.sun.glass.ui.Application.checkEventThread(Application.java:441)
at com.sun.glass.ui.Screen.setEventHandler(Screen.java:369)
at com.sun.javafx.tk.quantum.QuantumToolkit.setScreenConfigurationListener(QuantumToolkit.java:728)
at javafx.stage.Screen.<clinit>(Screen.java:74)
... 4 more
There are several problems with the structure of the application as you have posted it in the question.
The Application class represents the lifecycle of the entire application, which is managed via calls to its init(), start() and stop() methods. There should be only one Application instance in the entire application, and typically this instance is created by the JavaFX startup mechanism so you should not instantiate the Application subclass yourself.
JavaFX applications require the JavaFX runtime to be started, which includes launching the JavaFX Application Thread. This is done via a call to the static Application.launch() method, which must be called only once. The launch() method starts the JavaFX runtime, creates the instance of the Application class, calls init(), and then calls start() on the FX Application Thread. (In JavaFX 9 and later, you can also start the runtime by calling Platform.startup(), but use cases for this are rare).
Note that in your application, there is no call to Application.launch() (or Platform.startup()), so the JavaFX runtime is never started.
Certain operations can only be performed on the FX Application Thread. These include creating Stages and Scenes, and any modifications of properties of UI elements that are already displayed. Thus you cannot "run each client" in a separate thread. This is the cause of your exception: you are trying to create a new Stage on a thread that is not the FX Application Thread.
Each client does not need a new thread to display the UI (and, as described above, cannot do that). You likely do need to perform each client's communication with the server on a separate thread (because those are operations that take a long time, and you should not block the FX Application thread). You can do that by creating a new thread for each client's server communication, or using a shared executor service so that each client can get a thread from a pool (this is probably the preferred approach).
So your structure should look something like this:
public class Client {
private Parent ui ;
private ExecutorService exec ; // for handling server communication
private final String user ;
public Client(String user, ExecutorService exec) {
this.user = user ;
this.exec = exec ;
try {
ui = FXMLLoader.load(getClass().getResource("ui/client-management.fxml"));
} catch (IOException e) {
e.printStackTrace();
}
}
public Client(String user) {
this(user, Executors.newSingleThreadExecutor());
}
public Parent getUI() {
return ui ;
}
public void showInNewWindow() {
Scene scene = new Scene(ui);
Stage stage = new Stage();
stage.setScene(scene);
stage.show();
}
public void checkForNewEmail() {
Task<List<Email>> newEmailTask = new Task<>() {
#Override
protected List<Email> call() throws Exception {
List<Email> newEmails = new ArrayList<>();
// contact server and retrieve any new emails
return newEmails ;
}
};
newEmailTask.setOnSucceeded(e -> {
List<Email> newEmails = newEmailTask.getValue();
// update UI with new emails...
});
exec.submit(newEmailTask);
}
// etc ...
}
Then your ClientController class could do something like this:
public class ClientController {
private ExecutorService exec = Executors.newCachedThreadPool();
public void startClients() {
Client clientA = new Client("aaaa#gmail.com", exec);
Client clientB = new Client("bbbb#gmail.com", exec);
clientA.showInNewWindow();
clientB.showInNewWindow();
}
}
and your app class can do
public class ClientApp extends Application {
#Override
public void start(Stage primaryStage) {
ClientController controller = new ClientController();
controller.startClients();
}
public static void main(String[] args) {
Application.launch(args);
}
}

Executing multiple scripts with WebEngine (or a script that depends on another script)

I want to use the WebEngine (of JavaFX) to execute some JavaScript (and jQuery) on web pages and process the results (with Java code).
I have a problem with callback functions that execute some script by themselves.
For the purpose of illustrating my problem as simply as possible, I made a minimized code that shows the undesired result (it is only a part of a bigger project).
So, I created three classes:
Browser - a class that wraps the WebEngine and serves as a browser that load the web page and execute scripts.
JQueryFunction - a class that lists a couple of functions to be used with jQuery callback functions and could be implemented in multiple ways. (Basically they should be functional interfaces that the user implements his way - but for simplicity I made it normal functions).
Test - a class with main method that executes two scripts.
Browser
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Worker.State;
import javafx.scene.Scene;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javafx.stage.Stage;
import netscape.javascript.JSObject;
public class Browser extends Application {
private static WebEngine webEngine;
private static JSObject window;
#Override
public void start(Stage primaryStage) throws Exception {
WebView browser = new WebView();
webEngine = browser.getEngine();
webEngine.getLoadWorker().stateProperty().addListener(
new ChangeListener<State>() {
#Override
public void changed(ObservableValue<? extends State> observable, State oldState, State newState) {
if (newState == State.SUCCEEDED) {
window = (JSObject) webEngine.executeScript("window;");
// The following lines inject jQuery (for pages that don't use already)
webEngine.executeScript("var script = document.createElement(\"script\");");
webEngine.executeScript("script.src = \"http://code.jquery.com/jquery-1.12.0.min.js\";");
webEngine.executeScript("document.getElementsByTagName(\"body\")[0].appendChild(script);");
}
}
});
primaryStage.setScene(new Scene(browser));
primaryStage.show();
//Platform.setImplicitExit(false);
}
public static JSObject getWindow() {
return window;
}
public static Object executeScript(String script) throws InterruptedException, ExecutionException {
FutureTask<Object> task = new FutureTask<>(new Callable<Object>() {
#Override
public Object call() throws Exception {
return webEngine.executeScript(script);
}
});
Platform.runLater(task);
return task.get();
}
public static void load(String url) {
Platform.runLater(new Runnable() {
#Override
public void run() {
webEngine.load(url);
}
});
}
public static void start() {
new Thread(new Runnable() {
#Override
public void run() {
try {
Application.launch(Browser.class);
} catch (IllegalStateException e) {}
}
}).start();
}
}
JQueryFunction
import java.util.concurrent.ExecutionException;
public class JQueryFunction {
public String function1(int index, String text) {
return (index+1) + ": " + text;
}
public String function2(int index, String text) throws InterruptedException, ExecutionException {
return (String) Browser.executeScript("$($(\".BlueArrows:eq(2) li\").find(\"a:first\")[index]).text();");
}
}
Test
public class Test {
public static void main(String[] args) throws Exception {
Browser.start();
Thread.sleep(1000); // Only used here to simplify the code
Browser.load("https://docs.oracle.com/javase/tutorial/");
Thread.sleep(3500); // Only used here to simplify the code
Browser.getWindow().setMember("javaApp", new JQueryFunction());
Browser.executeScript("$(\".BlueArrows:eq(0) li\").find(\"a:first\").text(function(index, text) { return javaApp.function1(index, text); });");
Browser.executeScript("$(\".BlueArrows:eq(0) li\").find(\"a:first\").text(function(index, text) { return javaApp.function2(index, text); });");
}
}
When I run the test, the first executeScript runs as expected and change the text of some elements (prepending index number).
But the second executeScript is stuck forever and stick the GUI and actually the whole JavaFX application.
I understand why this happens...
The executeScript method calls the WebEngine (via Platform.runLater) to execute a jQuery that iterates over the elements and invokes a Java function (with different parameters each time).
The first execution (which invokes function1) gets the returned String from function1 and applies it on the element's text. (perfectly as expected)!
The second execution (which invokes function2) executes the jQuery function that invokes the Java function, but the Java function needs to execute some more JavaScript (or jQuery), but the WebEngine won't execute until the first execution is done.
But the first execution won't finish because it depends on the result of the second execution.
WebEngine is programmed in a way that he will execute in one thread only (within the FX thread) one task after another (serially).
Is there any way to solve it?
Why is WebEngine restricted to work only under JavaFX application?
And why does it have to work only with one thread?
One possible way to avoid the "deadlock" you are experiencing is not to call Platform.runLater when you're already running in the GUI Thread (JavaFX Application Thread). Something like:
public static Object executeScript(String script) throws InterruptedException, ExecutionException {
if(Platform.isFxApplicationThread()) {
return webEngine.executeScript(script);
}
FutureTask<Object> task = new FutureTask<>(new Callable<Object>() {
#Override
public Object call() throws Exception {
return webEngine.executeScript(script);
}
});
Platform.runLater(task);
return task.get();
}

JavaFX - Sporadically updating UI [duplicate]

I try to run in JavaFX application background thread periodically, which modifies some GUI property.
I think I know how to use Task and Service classes from javafx.concurrent and can't figure it out how to run such periodic task without using Thread#sleep() method. It would be nice if I can use some Executor from Executors fabricate methods (Executors.newSingleThreadScheduledExecutor())
I tried to run Runnable every 5 sec, which restarts javafx.concurrent.Service but it hangs immediately as service.restart or even service.getState() is called.
So finally I use Executors.newSingleThreadScheduledExecutor(), which fires my Runnable every 5 sec and that Runnable runs another Runnable using:
Platform.runLater(new Runnable() {
//here i can modify GUI properties
}
It looks very nasty :( Is there a better way to do this using Task or Service classes?
You can use Timeline for that task:
Timeline fiveSecondsWonder = new Timeline(
new KeyFrame(Duration.seconds(5),
new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
System.out.println("this is called every 5 seconds on UI thread");
}
}));
fiveSecondsWonder.setCycleCount(Timeline.INDEFINITE);
fiveSecondsWonder.play();
for the background processes (which don't do anything to the UI) you can use old good java.util.Timer:
new Timer().schedule(
new TimerTask() {
#Override
public void run() {
System.out.println("ping");
}
}, 0, 5000);
Preface: This question is often the duplicate target for questions which ask how to perform periodic actions in JavaFX, whether the action should be done in the background or not. While there are already great answers to this question, this answer attempts to consolidate all the given information (and more) into a single answer and explain/show the differences between each approach.
This answer focuses on the APIs available in JavaSE and JavaFX and not third-party libraries such as ReactFX (showcased in Tomas Mikula's answer).
Background Information: JavaFX & Threads
Like most mainstream GUI frameworks, JavaFX is single-threaded. This means there's a single thread dedicated to reading and writing the state of the UI and processing user-generated events (e.g. mouse events, key events, etc.). In JavaFX this thread is called the "JavaFX Application Thread", sometimes shortened to just "FX thread", but other frameworks may call it something else. Some other names include "UI thread", "event-dispatch thread", and "main thread".
It is absolutely paramount that anything connected to the GUI showing on screen is only ever accessed or manipulated on the JavaFX Application Thread. The JavaFX framework is not thread-safe and using a different thread to improperly read or write the state of the UI can lead to undefined behavior. Even if you don't see any externally-visible problems, access to state shared between threads without the necessary synchronization is broken code.
Many GUI objects, however, can be manipulated on any thread as long as they aren't "live". From the documentation of javafx.scene.Node:
Node objects may be constructed and modified on any thread as long they are not yet attached to a Scene in a Window that is showing [emphasis added]. An application must attach nodes to such a Scene or modify them on the JavaFX Application Thread.
But other GUI objects, such as Window and even some subclasses of Node (e.g. WebView), are more strict. For instance, from the documentation of javafx.stage.Window:
Window objects must be constructed and modified on the JavaFX Application Thread.
If you're unsure about the threading rules of a GUI object, its documentation should provide the needed information.
Since JavaFX is single-threaded you also have to make sure never to block or otherwise monopolize the FX thread. If the thread is not free to do its job then the UI is never redrawn and new user-generated events can't be processed. Not following this rule can lead to the infamous unresponsive/frozen UI and your users are not happy.
It's virtually always wrong to sleep the JavaFX Application Thread.
Periodic Tasks
There are two different kinds of periodic tasks, at least for the purposes of this answer:
Periodic foreground "tasks".
This could include things such as a "blinking" node or periodically switching between images.
Periodic background tasks.
An example might be periodically checking a remote server for updates and, if there are any, downloading the new information and displaying it to the user.
Periodic Foreground Tasks
If your periodic task is short and simple then using a background thread is overkill and just adds unnecessary complexity. The more appropriate solution is to use the javafx.animation API. Animations are asynchronous but stay entirely within the JavaFX Application Thread. In other words, animations provide a way to "loop" on the FX thread, with delays between each iteration, without actually using loops.
There are three classes uniquely suited to periodic foreground tasks.
Timeline
A Timeline is made up of one or more KeyFrames. Each KeyFrame has a specified time of when it should complete. Each one can also have an "on finished" handler which is invoked after the specified amount of time has elapsed. This means you can create a Timeline with a single KeyFrame that periodically executes an action, looping as many times as you want (including forever).
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class App extends Application {
#Override
public void start(Stage primaryStage) {
Rectangle rect = new Rectangle(100, 100);
// toggle the visibility of 'rect' every 500ms
Timeline timeline =
new Timeline(new KeyFrame(Duration.millis(500), e -> rect.setVisible(!rect.isVisible())));
timeline.setCycleCount(Animation.INDEFINITE); // loop forever
timeline.play();
primaryStage.setScene(new Scene(new StackPane(rect), 200, 200));
primaryStage.show();
}
}
Since a Timeline can have more than one KeyFrame it's possible to have actions being executed at different intervals. Just keep in mind that the times of each KeyFrame do not stack. If you have one KeyFrame with a time of two seconds followed by another KeyFrame with a time of two seconds, both KeyFrames will finish two seconds after the animation is started. To have the second KeyFrame finish two seconds after the first one, its time needs to be four seconds.
PauseTransition
Unlike the other animation classes, a PauseTransition is not used to actually animate anything. It's main purpose is to be used as a child of SequentialTransition to put a pause between two other animations. However, like all subclassses of Animation it can have an "on finished" handler that's executed after it completes, allowing it to be used for periodic tasks.
import javafx.animation.PauseTransition;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import javafx.util.Duration;
public class App extends Application {
#Override
public void start(Stage primaryStage) {
Rectangle rect = new Rectangle(100, 100);
// toggle the visibility of 'rect' every 500ms
PauseTransition pause = new PauseTransition(Duration.millis(500));
pause.setOnFinished(
e -> {
rect.setVisible(!rect.isVisible());
pause.playFromStart(); // loop again
});
pause.play();
primaryStage.setScene(new Scene(new StackPane(rect), 200, 200));
primaryStage.show();
}
}
Notice the on-finished handler invokes playFromStart(). This is necessary to "loop" the animation again. The cycleCount property can't be used since the on-finished handler is not invoked at the end of each cycle, it's only invoked at the end of the last cycle. The same thing is true of Timeline; the reason it works with Timeline above is because the on-finished handler isn't registered with the Timeline but with the KeyFrame.
Since the cycleCount property can't be used for PauseTransition for multiple cycles it makes it more difficult to loop only a certain number of times (rather than forever). You have to keep track of the state yourself and only invoke playFromStart() when appropriate. Keep in mind that local variables declared outside a lambda expression or anonymous class but used inside said lambda expression or anonymous class must be final or effectively final.
AnimationTimer
The AnimationTimer class is the lowest level of JavaFX's animation API. It's not a subclass of Animation and thus doesn't have any of the properties that were used above. Instead, it has an abstract method that, when the timer is started, is invoked once per frame with the timestamp (in nanoseconds) of the current frame: #handle(long). In order to execute something periodically with AnimationTimer (other than once per frame) will require manually calculating the time differences between invocations of handle using the method's argument.
import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.StackPane;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
public class App extends Application {
#Override
public void start(Stage primaryStage) {
Rectangle rect = new Rectangle(100, 100);
// toggle the visibility of 'rect' every 500ms
AnimationTimer timer =
new AnimationTimer() {
private long lastToggle;
#Override
public void handle(long now) {
if (lastToggle == 0L) {
lastToggle = now;
} else {
long diff = now - lastToggle;
if (diff >= 500_000_000L) { // 500,000,000ns == 500ms
rect.setVisible(!rect.isVisible());
lastToggle = now;
}
}
}
};
timer.start();
primaryStage.setScene(new Scene(new StackPane(rect), 200, 200));
primaryStage.show();
}
}
For most use cases similar to the above, using either Timeline or PauseTransition would be the better option.
Periodic Background Tasks
If your periodic task is time-consuming (e.g. expensive computations) or blocking (e.g. I/O) then a background thread needs to be used. JavaFX comes with some concurrency utilities built-in to aid with communication between background threads and the FX thread. These utilities are described in:
The Concurrency in JavaFX tutorial, and
The documentation of the classes in the javafx.concurrent package.
For periodic background tasks that need to communicate with the FX thread, the class to use is javafx.concurrent.ScheduledService. That class will execute its task periodically, restarting after successful execution, based on a specified period. If configured to do so it will even retry a configurable amount of times after failed executions.
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.concurrent.ScheduledService;
import javafx.concurrent.Task;
import javafx.concurrent.Worker.State;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.Region;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Duration;
public class App extends Application {
// maintain a strong reference to the service
private UpdateCheckService service;
#Override
public void start(Stage primaryStage) {
service = new UpdateCheckService();
service.setPeriod(Duration.seconds(5));
Label resultLabel = new Label();
service.setOnRunning(e -> resultLabel.setText(null));
service.setOnSucceeded(
e -> {
if (service.getValue()) {
resultLabel.setText("UPDATES AVAILABLE");
} else {
resultLabel.setText("UP-TO-DATE");
}
});
Label msgLabel = new Label();
msgLabel.textProperty().bind(service.messageProperty());
ProgressBar progBar = new ProgressBar();
progBar.setMaxWidth(Double.MAX_VALUE);
progBar.progressProperty().bind(service.progressProperty());
progBar.visibleProperty().bind(service.stateProperty().isEqualTo(State.RUNNING));
VBox box = new VBox(3, msgLabel, progBar);
box.setMaxHeight(Region.USE_PREF_SIZE);
box.setPadding(new Insets(3));
StackPane root = new StackPane(resultLabel, box);
StackPane.setAlignment(box, Pos.BOTTOM_LEFT);
primaryStage.setScene(new Scene(root, 400, 200));
primaryStage.show();
service.start();
}
private static class UpdateCheckService extends ScheduledService<Boolean> {
#Override
protected Task<Boolean> createTask() {
return new Task<>() {
#Override
protected Boolean call() throws Exception {
updateMessage("Checking for updates...");
for (int i = 0; i < 1000; i++) {
updateProgress(i + 1, 1000);
Thread.sleep(1L); // fake time-consuming work
}
return Math.random() < 0.5; // 50-50 chance updates are "available"
}
};
}
}
}
Here's a note from the documentation of ScheduledService:
Timing for this class is not absolutely reliable. A very busy event thread might introduce some timing lag into the beginning of the execution of the background Task, so very small values for the period or delay are likely to be inaccurate. A delay or period in the hundreds of milliseconds or larger should be fairly reliable.
And another:
The ScheduledService introduces a new property called lastValue. The lastValue is the value that was last successfully computed. Because a Service clears its value property on each run, and because the ScheduledService will reschedule a run immediately after completion (unless it enters the cancelled or failed states), the value property is not overly useful on a ScheduledService. In most cases you will want to instead use the value returned by lastValue.
The last note means binding to the value property of a ScheduledService is in all likelihood useless. The example above works despite querying the value property because the property is queried in the onSucceeded handler, before the service is rescheduled.
No Interaction with UI
If the periodic background task does not need to interact with the UI then you can use the standard APIs of Java instead. More specifically, either:
The java.util.Timer class (not javax.swing.Timer),
Or the more modern java.util.concurrent.ScheduledExecutorService interface.
Note that ScheduledExecutorService supports thread pools, unlike Timer which only supports a single thread.
ScheduledService is not an Option
If for whatever reason you can't use ScheduledService, but need to need to interact with the UI anyway, then you need to make sure the code interacting with the UI, and only that code, is executed on the FX thread. This can be accomplished by using Platform#runLater(Runnable).
Run the specified Runnable on the JavaFX Application Thread at some unspecified time in the future. This method, which may be called from any thread, will post the Runnable to an event queue and then return immediately to the caller. The Runnables are executed in the order they are posted. A runnable passed into the runLater method will be executed before any Runnable passed into a subsequent call to runLater. If this method is called after the JavaFX runtime has been shutdown, the call will be ignored: the Runnable will not be executed and no exception will be thrown.
NOTE: applications should avoid flooding JavaFX with too many pending Runnables. Otherwise, the application may become unresponsive. Applications are encouraged to batch up multiple operations into fewer runLater calls. Additionally, long-running operations should be done on a background thread where possible, freeing up the JavaFX Application Thread for GUI operations.
[...]
Heed the note from the above documentation. The javafx.concurent.Task class avoids this by coalescing updates to its message, progress, and value properties. This is currently implemented by using an AtomicReference and strategic get-and-set operations. If interested, you can take a look at the implementation (JavaFX is open source).
I would Prefer the PauseTransition:
PauseTransition wait = new PauseTransition(Duration.seconds(5));
wait.setOnFinished((e) -> {
/*YOUR METHOD*/
wait.playFromStart();
});
wait.play();
Here is a solution using Java 8 and ReactFX. Say that you want to periodically recompute the value of Label.textProperty().
Label label = ...;
EventStreams.ticks(Duration.ofSeconds(5)) // emits periodic ticks
.supplyCompletionStage(() -> getStatusAsync()) // starts a background task on each tick
.await() // emits task results, when ready
.subscribe(label::setText); // performs label.setText() for each result
CompletionStage<String> getStatusAsync() {
return CompletableFuture.supplyAsync(() -> getStatusFromNetwork());
}
String getStatusFromNetwork() {
// ...
}
Compared to Sergey's solution, you don't dedicate the whole thread to getting status from the network, but instead use the shared thread pool for that.
You can use ScheduledService too. I am using this alternative after noticing that during the use of Timeline and PauseTransition occurred some UI freezes in my application, especially when the user interacts with the elements of a MenuBar (on JavaFX 12). Using the ScheduledService these problems no longer occurred.
class UpdateLabel extends ScheduledService<Void> {
private Label label;
public UpdateLabel(Label label){
this.label = label;
}
#Override
protected Task<Void> createTask(){
return new Task<Void>(){
#Override
protected Void call(){
Platform.runLater(() -> {
/* Modify you GUI properties... */
label.setText(new Random().toString());
});
return null;
}
}
}
}
And then, use it:
class WindowController implements Initializable {
private #FXML Label randomNumber;
#Override
public void initialize(URL u, ResourceBundle res){
var service = new UpdateLabel(randomNumber);
service.setPeriod(Duration.seconds(2)); // The interval between executions.
service.play()
}
}
Was not easy find the way to programing this kind of behavior may be because my process reads I/O, works in milliseconds and I felt was often interrupted by GUI thread, but I made it by creating a BackgroundProcess class & with the help of ScheduledExecutorService.
In the controlle side, I use PauseTransition to read volatile (no contention) info only.
Sample code :
public class HelloApplication extends Application {
final ScheduledExecutorService scheduledExecutor = Executors.newSingleThreadScheduledExecutor();
final BackgroundProcess backgroundProcess = new BackgroundProcess();
#Override
public void start(Stage stage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("hello-view.fxml"));
Scene scene = new Scene(fxmlLoader.load(), 720, 610);
HelloController helloController = fxmlLoader.getController();
helloController.setBackgroundProcess(backgroundProcess);
stage.setTitle("Hello!");
stage.setScene(scene);
stage.show();
scheduledExecutor.scheduleWithFixedDelay(
backgroundProcess,
0, 111, TimeUnit.MILLISECONDS);
}
#Override
public void stop() throws Exception {
super.stop();
scheduledExecutor.shutdown();
}
...
}
public class BackgroundProcess implements Runnable{
volatile String status = "";
#Override
public void run() {
status = newStatus();
}
...
}
public class HelloController {
#FXML
protected void initialize() {
PauseTransition refresh = new PauseTransition(Duration.millis(111));
wait.setOnFinished((e) -> {
statusLabel.setText(backgroundProcess.status);
refresh.playFromStart();
});
refresh.play();
}
...
}
To read synchronized (contention) info I use ScheduledService to prepare the info and prevent interruptions in the JavaFX thread.
This is a more complex sample code:
public class HelloApplication extends Application {
final ScheduledExecutorService scheduledExecutor = Executors.newSingleThreadScheduledExecutor();
final BackgroundProcess backgroundProcess = new BackgroundProcess();
#Override
public void start(Stage stage) throws IOException {
FXMLLoader fxmlLoader = new FXMLLoader(HelloApplication.class.getResource("hello-view.fxml"));
Scene scene = new Scene(fxmlLoader.load(), 720, 610);
HelloController helloController = fxmlLoader.getController();
helloController.setBackgroundProcess(backgroundProcess);
stage.setTitle("Hello!");
stage.setScene(scene);
stage.show();
scheduledExecutor.scheduleWithFixedDelay(
backgroundProcess,
0, 111, TimeUnit.MILLISECONDS);
}
#Override
public void stop() throws Exception {
super.stop();
scheduledExecutor.shutdown();
}
...
}
public class BackgroundProcess implements Runnable{
volatile String status = "";
LinkedTransferQueue<String> queue = new LinkedTransferQueue();
#Override
public void run() {
status = newStatus();
addToQueue();
}
...
}
public class HelloController {
static class SynchronizedInformation {
ArrayList<String> list;
}
private SynchronizedInformation prepareSynchronizedInformation() {
if (backgroundProcess.queue.isEmpty()) {
return null;
}
final SynchronizedInformation r = new SynchronizedInformation();
int size = backgroundProcess.queue.size();
r.list = new ArrayList<>(size);
String line;
while (r.list.size() < size && null != (line = backgroundProcess.queue.poll())) {
r.list.add(line);
}
return r;
}
private void refreshSynchronizedInformation(SynchronizedInformation synchronizedInformation) {
if (null != synchronizedInformation) {
synchronizedInformation.list.forEach(textArea::appendText);
}
statusLabel.setText(backgroundProcess.incoming);
}
#FXML
protected void initialize() {
ScheduledService<SynchronizedInformation> svc = new ScheduledService<>() {
#Override
protected Task<SynchronizedInformation> createTask() {
return new Task<SynchronizedInformation>() {
#Override
protected SynchronizedInformation call() throws Exception {
return prepareSynchronizedInformation();
}
};
}
};
svc.setDelay(Duration.millis(111));
svc.setOnSucceeded(e -> refreshSynchronizedInformation(svc.getValue()));
svc.start();
...
}

Categories

Resources