Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is using Timeline for scheduling correct

Tags:

timer

javafx

I've found a lot of suggestions to use Timeline with KeyFrame.onFinished to schedule tasks in the JavaFX. But the AnimationTimer doc says, that its handle method is called 60 times per second.

It is unclear from documentation but it seems that Timeline uses AnimationTimer internally. Does that means that the timeline solution for scheduling imposes CPU-intensive polling mode? If that is actually how JavaFX works, what other methods for scheduling is recommended?

like image 919
ayvango Avatar asked Apr 01 '16 13:04

ayvango


1 Answers

Advised Usage Rules for Scheduling in JavaFX

Given how Timeline works (see below), some basic rules can be derived:

  1. Use a Timeline when you want to schedule quick operations that modify attributes of the scene graph.

  2. Do not use a Timeline when you want to do any of the following:

    1. Perform operations that are themselves time-consuming (e.g. the work for a given scheduled activity takes longer than 1/30th of a second).
    2. Perform work which really doesn't have anything to do with the scene graph.
    3. Have real-time scheduling at a resolution greater than a pulse (1/60th of a second).
    4. Control your scheduling using traditional java.util.concurrent mechanisms or a third party library.
    5. Run your scheduled tasks on their own threads.

Also, for a one-off execution that updates the scene graph after a fixed delay, I like to use a PauseTransition.

Answers to specific questions and concerns

It is unclear from documentation but it seems that Timeline uses AnimationTimer internally.

No, Timeline does not use AnimationTimer internally. However, Timeline is still pulse based and will receive code to update the current state of the timeline will be invoked on each pulse (usually 60 times a second). To understand what a pulse is read the background section below.

Does that means that the timeline solution for scheduling imposes CPU-intensive polling mode?

Yes, there is some overhead, but it is likely minimal enough that it can be disregarded. The JavaFX system is going to be running some work on each pulse anyway, regardless whether you create any timelines yourself. Unless you are heavily animating objects or doing a lot of work on each pulse, then the time to process each pulse will be completely trivial as there is simply very little work to be done on each pulse.

If that is actually how JavaFX works, what other methods for scheduling is recommended?

There are numerous alternatives to Timeline:

  1. Use a javafx.concurrent facility such as a Task or Service, and specifically a ScheduledService.
    • This is good if you want to provide feedback (such as progress, message updates and return values) to the JavaFX UI.
  2. Use a java.util.concurrent facility.
    • This is good if you don't need the additional features of javafx.concurrent for progress and messaging or if you want the additional control provided by java.util.concurrent facilities.
    • If you use this, you can still get intermittent or final feedback to your JavaFX UI by invoking Platform.runLater().
  3. Use a java.util.TimerTask.
    • Simpler API than java.util.concurrent, no direct interface for JavaFX feedback, though again you can use Platform.runLater() for that.
  4. Use a third party scheduling system (such as Quartz):
    • More complicated than other solutions, but also more flexible, though it adds a dependent library and requires Platform.runLater if you want to modify scene graph elements.

If you do use something other than a timeline, you need to take care:

  1. You invoke Platform.runLater if you want to modify anything on the scene graph from your non-JavaFX application threads.
  2. You don't invoke Platform.runLater too often so as to overload the JavaFX application thread (e.g. by coalescing update calls).

Background Info

Timeline Operation

For example, let's say you have a Timeline which has a Keyframe that has a duration of 1 second. The internal Timeline will still be updated each pulse. This is required because a Timeline is more than just a scheduler, it has other properties and tasks. For instance the Timeline has a currentTime property. The currentTime will be updated each pulse, so that it always reflects the current time accurately. Similarly, internal logic with the Timeline will check to see if there are any KeyValues associated with the Timeline, and update their values on each pulse, in accordance with their associated Interpolator. And the internal logic will check on each pulse to see if the Timeline is finished, to call the onFinished handler. It will also evaluate the current time versus the duration of each keyframe and, if there is a match, fire the appropriate action event for the keyframe.

So the Timeline is acting like a scheduler in that it can execute key frames at specific times, but it is more than a scheduler as it also maintains it's current time, current state and ability to continuously vary associated key values. Additionally, you can vary the rate, direction and number of cycles of a timeline, as well as pause it in position, then resume it, which also differs from a traditional scheduler.

An aspect of the Timeline is that because it is based upon callbacks from pulses in the JavaFX system; everything runs on the JavaFX application thread. Therefore, when you use timelines (even 1000 of them), no additional threads are spawned, so from that perspective it is lightweight. Also all logic invoked by the timeline occurs on the JavaFX application thread. Because everything runs on the JavaFX application thread, this makes the Timeline nice for manipulating elements and attributes of the active JavaFX scene graph (such manipulations must be performed on the JavaFX application thread). However, a timeline would be a bad choice if you wanted to do a lot of CPU time-consuming work or blocking I/O in the associated timeline keyframes. This is because the JavaFX application thread will be blocked while that work occurs, freezing your UI.

Pulses

The JavaFX runtime system is based upon pulses.

A pulse is an event that indicates to the JavaFX scene graph that it is time to synchronize the state of the elements on the scene graph with Prism. A pulse is throttled at 60 frames per second (fps) maximum and is fired whenever animations are running on the scene graph. Even when animation is not running, a pulse is scheduled when something in the scene graph is changed. For example, if a position of a button is changed, a pulse is scheduled.

When a pulse is fired, the state of the elements on the scene graph is synchronized down to the rendering layer. A pulse enables application developers a way to handle events asynchronously. This important feature allows the system to batch and execute events on the pulse.

The JavaFX animation system stores a list of all of the animations and animation timers which been created and not garbage collected. On each pulse it will iterate through this list. Each animation timer will have it's handle callback invoked on each pulse. Each timeline or transition will similarly have its internal state update and update any associated key values or fire key frame action events as appropriate.

like image 71
jewelsea Avatar answered Sep 21 '22 12:09

jewelsea