The takeaway from this post should be that to schedule future actions in a JavaFX application you don’t need an external timer (and thus an extra thread in your application), such as java.util.Timer
or java.util.concurrent.ScheduledExecutorService
. You can use Timeline or FxTimer, a convenient wrapper around Timeline used internally in ReactFX and recently exposed as public API.
One-time action
This is how to use Timeline
to schedule a one-time action:
Timeline timeline = new Timeline(new KeyFrame(
Duration.millis(2500),
ae -> doSomething()));
timeline.play();
Equivalently, you can use this ReactFX shortcut:
FxTimer.runLater(
Duration.ofMillis(2500),
() -> doSomething());
In addition to the latter being more readable, FxTimer
uses the new Java Time API introduced in JDK 8.
Periodic action
This is how to schedule a periodic action using Timeline
:
Timeline timeline = new Timeline(new KeyFrame(
Duration.millis(2500),
ae -> doSomething()));
timeline.setCycleCount(Animation.INDEFINITE);
timeline.play();
Using FxTimer
, it looks like this:
FxTimer.runPeriodically(
Duration.ofMillis(2500),
() -> doSomething());
Or, because we really like event streams:
EventStreams.ticks(Duration.ofMillis(2500))
.subscribe(tick -> doSomething());
Timer cancellation
Let’s see how one can cancel a scheduled action.
The Timeline
way:
Timeline timeline = new Timeline(new KeyFrame(
Duration.millis(2500),
ae -> doSomething()));
timeline.play();
// later
timeline.stop();
The FxTimer
way:
Timer timer = FxTimer.runLater(
Duration.ofMillis(2500),
() -> doSomething());
// later
timer.stop();
There is one important semantic difference between the above code snippets. In the first one, doSomething()
may still be executed after timeline.stop()
, even though both timeline.stop()
and doSomething()
are executed on the JavaFX application thread. It happens when stop()
is called after the timeline has reached the key frame, but before the associated action had a chance to be executed on the JavaFX application thread. EDIT: Note that this behavior makes sense for animations: if there is an action a associated with time t1 and stop()
is called at time t2 > t1, then, if not already done so, a should still be executed in order to advance the animation to a state corresponding to t2.
On the other hand, for timers it makes sense to cancel the timer upon stop()
even if the timer is already overdue. Therefore, FxTimer
takes an extra measure to ensure that the action is never executed after stop()
.