Best Practices for JavaFX Mobile Applications

As everybody who is interested in JavaFX will know by now, JavaFX Mobile was released a short while
ago. It was a hell of a ride, that’s for sure. I felt so exhausted, I did not even have the energy to blog during the release…

But by now I feel recovered and want to start a little series about lessons we have learned while preparing the release and give some hints how to improve the performance of JavaFX Mobile applications.

WARNING: The tips I am giving here are true for the current version of JavaFX Mobile, which is part of the JavaFX 1.1 SDK. In future versions the behavior will change, the
current bad performance of the mentioned artifacts will be optimized away or at least significantly improved. Everything I am writing about here is a snap-shot, nothing should be understood as
final!

Item 1: Avoid unnecessary bindings

Bindings are very convenient, without any doubt one of the most valuable innovations in JavaFX Script. Unfortunately they come with a price. The generated boiler-plate code is usually not as small and
fast as a manual implementation would be. Especially complex dependency-structures tend to impose a severe penalty on performance and footprint.

For this reason it is recommended to avoid bindings as much as possible. Often the same functionality can be implemented with triggers. One should not use bindings to avoid the hassle of dealing with the initialization order. And it certainly makes no sense to bind to a constant value.

Lazy bindings are most of the time (but not always!) faster if a bound variable is updated more often then read, but they are still not as fast as manual implementations.

Example

A common use-case is a number of nodes which positions and sizes depend on the stage-size. A typical implementation uses bindings to achieve that.

Here we will look at a simple example, which resembles such a situation. The scene consists of three rectangles which are laid out diagonally from the top-left to the bottom-right. The size of the rectangle is a quarter of the screen-size. Code Sample 1 shows an implementation with bindings.

 1 def rectangleWidth: Number = bind stage.width * 0.25;
 2 def rectangleHeight: Number = bind stage.height * 0.25;
 3
 4 def stage: Stage = Stage {
 5     scene: Scene {
 6         content: for (i in [0..2])
 7             Rectangle {
 8                 x: bind stage.width * (0.125 + 0.25*i)
 9                 y: bind stage.height * (0.125 + 0.25*i)
10                 width: bind rectangleWidth
11                 height: bind rectangleHeight
12             }
13     }
14 }

Code Sample 1: Layout calculated with bindings

The first question one should think about is wether the bindings are really necessary. On a real device the screen-size changes only when the screen orientation is switched (provided that the device supports this functionality). If our application does not support screen rotation, the layout can be defined constant.

One possible solution to reduce the number of bindings is shown in Code Sample 2. Two variables width and height are introduced and bound to stage.width and stage.height respectively. Their only purpose is to provide triggers for stage.width and stage.height, since we do not want to override the original triggers. Position and size of the rectangles are calculated manually in the triggers.

 1 def r = for (i in [0..2]) Rectangle {}
 2
 3 def stage = Stage {
 4     scene: Scene {content: r}
 5 }
 6
 7 def height = bind stage.height on replace {
 8     def rectangleHeight = height * 0.25;
 9     for (i in [0..2]) {
10         r[i].height = rectangleHeight;
11         r[i].y = height * (0.125 + 0.25*i)
12     }
13 }
14
15 def width = bind stage.width on replace {
16     def rectangleWidth = width * 0.25;
17     for (i in [0..2]) {
18         r[i].width = rectangleWidth;
19         r[i].x = width * (0.125 + 0.25*i)
20     }
21 }

Code Sample 2: Layout calculated in trigger

Without any doubt, the code in Code Sample 1 is more elegant. But measuring the performance of both snippets in the emulator, it turned out the code in Code Sample 2 is almost twice as fast.