Best Practices for JavaFX Mobile Applications (Part 2)

Today I am writing about the second tip to increase performance of JavaFX Mobile applications. I think this and the previous tip are the most important ones.


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 2: Keep the scenegraph as small as possible

Behind the scenes of the runtime a lot of communication takes place to update the variables of the nodes in a scenegraph. The more elements a scenegraph has, the more communication is required. Therefore it is critical to keep the scenegraph as small as possible. Especially animations tend to suffer from a large scenegraph.

It is bad practice to keep a node in the scenegraph at all times and control its visibility via the visible-flag or its opacity. Invisible nodes in the scenegraph are still part of the communication-circus in the background. Instead one should remove nodes from the scenegraph and add them only when required.

This approach has one drawback though. Adding or removing nodes takes longer than setting the visibility. Therefore it might not be appropriate in situations were immediate responses are critical.

Example 1

Often one has a set of nodes of which only one is visible. These can be for example different pages, or nodes to visualize different states of an element. One might be tempted to add all nodes to the scenegraph and set only the current as visible.

Code Sample 1 shows a simplified version of this approach. Three colored circles are created to visualize some kind of state (red, yellow, green). Only one node is visible at any time. (Let's ignore for a second that this could simply be achieved by changing the fill-color of a single circle. In real life applications one would probably have images or more complex shapes for visualizations and simply changing the color would not work.)

 1 def colors = [Color.GREEN, Color.YELLOW, Color.RED];
 2 
 3 var state: Integer;
 4 
 5 Stage {
 6     scene: Scene {
 7         content: for (i in [0..2])
 8             Circle {
 9                 centerX: 10
10                 centerY: 10
11                 radius: 10
12                 fill: colors[i]
13                 visible: bind state == i
14             }
15     }
16 }

Code Sample 1: Using visibility to switch between nodes

This results in three nodes in the scenegraph although only one is shown. This should be refactored to ensure that only the visible node is in the scenegraph. Code Sample 2 shows one possible implementation.

 1 def colors = [Color.GREEN, Color.YELLOW, Color.RED];
 2 
 3 var state: Integer on replace oldValue {
 4     insert nodes[state] into stage.scene.content;
 5     delete nodes[oldValue] from stage.scene.content;
 6 }
 7 
 8 
 9 def nodes = for (i in [0..2])
10     Circle {
11         centerX: 10
12         centerY: 10
13         radius: 10
14         fill: colors[i]
15     }
16 
17 def stage = Stage {scene: Scene{}}

Code Sample 2: Adding and removing nodes when required

The code in Code Sample 1 is more compact, but Code Sample 2 reduced the number of nodes in the scenegraph from three to one. While tuning some of the demos for the JavaFX Mobile release, we were able to reduce the number of nodes in the scenegraph by 50% and more, simply by ensuring that only visible nodes are part of it.

Example 2

If nodes are shown and hidden with some kind of animation, adding and removing the node to the scenegraph becomes extremely simple. One only needs to implement an action at the beginning of the fadeIn-animation and at the end of the fadeOut-animation to add respectively remove the node. Code Sample 3 shows such a usage where a simple message-box is shown and hidden by changing the opacity.

 1 def msgBox = Group {
 2     opacity: 0.0
 3     content: [
 4         Rectangle {width: 150, height: 40, fill: Color.GREY},
 5         Text {x: 20, y: 20, content: "Hello World!"}
 6     ]
 7 }
 8 
 9 def fadeIn = Timeline {
10     keyFrames: [
11         KeyFrame {
12             action: function() {insert msgBox into stage.scene.content}
13         },
14         at (1s) {msgBox.opacity => 1.0 tween Interpolator.LINEAR}
15     ]
16 }
17 
18 def fadeOut = Timeline {
19     keyFrames: KeyFrame {
20         time: 1s
21         values: msgBox.opacity => 0.0 tween Interpolator.LINEAR
22         action: function() {delete msgBox from stage.scene.content}
23     }
24 }
25 
26 def stage = Stage {scene: Scene{}}

Code Sample 3: Using fadeIn- and fadeOut-animations to add and remove nodes.

Comments:

I don't see how removing objects could not work against you. What if I bound a slider to a view of 100 circle nodes and when I slide the slider, I zoom in and out on those 100 nodes. When I am zoomed I see 1 node up close. When I zoom away, I see them all. Should I destroy then recreate the circle nodes as a slide in and out?

As I understand it, this is about the ability to locate and update the positions of all the nodes very quickly and also to find that set of nodes which is out of sight and we shouldn't waste time rendering.

Do you know what data structures are being used to track the positions of the nodes? For instance, when a mouseClick happens on a circle node, somehow that x-y point of that mouse event has to be used to find the distinguished circle-node that lies at that point.

Do you know what data structures are used to locate what object received the mouseClick event?

The scene graph is a developer-facing abstraction created to ease developer effort, but it's not how finding that node I was just talking about is found. So also with culling views.

Can we (civilians) know what's going on under the covers somehow? Is there a developer blog or mailing list we can act as viewers on?

many thanks.

Posted by softwarevisualization on February 26, 2009 at 02:17 PM CET #

Did you measure how much reducing the number of shapes in a scene improves performance?

I have been busy developing a fairly complex GUI with JavaFX, and find that it very easy to clog javafx up. Keeping things a simple as possible does help. And indeed, removing invisible and off-the-screen nodes from the graph is a good idea when presenting large lists of nodes in a scrollpane. That is why I have been thinking to build an intelligent scroll view that applies that lesson.

Adobe Flex also applies this wisdom in its DataGrid component. Only the visible data cells have a UIComponent attached, as soon as a cell scrolls off the view, than the cell is simply recycled, populated with other UIComponents and moved to the top of the view.

Posted by Mark Nankman on March 02, 2009 at 03:24 PM CET #

Good point, I've been using OpenLaszlo and Flex a lot. Especially with older versions of the Flash Player instantiation of objects was very resource intensive. Using a pool of objects that are re-used for a dynamic list is the best approach. You could even define an attribute pooling: true when binding data to a list.

Posted by Raju Bitter on July 09, 2009 at 08:45 PM CEST #

Post a Comment:
Comments are closed for this entry.
About

Michael Heinrichs

Search

Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today