Macromedia Flex is a powerful platform that offers the ability to create Rich Internet Applications (RIAs). Misusing this power can result in areas of poor performance. Flex is like any other programming model because certain coding practices can be detrimental to the overall performance of your client application. This article describes some of the MXML coding practices that affect application performance on the client side and demonstrates simple techniques that will enhance the performance of your Flex application. More specifically, this article explores how to do the following tasks:
Note that the tips suggested in this article do not apply to all Flex applications. It is important to analyze the structure of your own application and modify the suggestions to tailor them to your needs. For ongoing coding and conceptual help, you can use the Flex support forums and Flex Developer Center.
Note also that this is the first part in a two-part article. The second part of this article, Flex Application Performance: Tips and Techniques for Improving Server-Side Performance, discusses the server-side enhancements and deployment options that you can use to improve the performance of your Flex application.
To make the most of this article, you need the following:
Learn more about Macromedia Flex.
We have made every effort to ensure the accuracy of this article and all code included. Feedback for this article and all Flex performance issues is always appreciated. To submit feedback, please email us.
Ideally, performance is a topic that is in the back of your mind during every step of the development process—from application design to implementation and deployment. When creating a Flex application, think through the choice of containers and components that you use to ensure that the code is maintainable, organizationally clear, and performs well.
You can use the Flex navigator containers (Accordion, TabNavigator, and ViewStack) across all application types to organize content. The navigator containers organize content in a way that:
More specifically, Flex navigator containers help you organize content easily into different child views and control the creation of these views with deferred instantiation. Organizing content into these child views spreads out creation time for each child view because Flex creates a specific child view the first time a user requests that view. The "Navigator Containers Have Built-in Deferred Instantiation" section later in this article explains why Flex navigator containers perform better with deferred instantiation and how you can leverage deferred instantiation to make your application more robust.
Dashboard-style applications have also resulted in successful deployments. This type of application organizes content into modular, self-contained views that offer a more intuitive approach to application organization. Like the navigator containers, this approach performs well because it organizes complex views, with Flex creating them when the user drills down. Flex does not have to size, measure, and draw the views in the background, so it creates the selected view more quickly.
Before going into techniques for preventing common performance issues, ensure that you have installed Macromedia Flash Player 7 (7.0.14 or 7.0.19 or later). Flash Player is a multiple-platform client that lets users interact with Flash content. There are two types of players, the Flash Player release version and the Flash Debug Player version. The Flash Debug Player version is best used during the development phase, because it enables the Flex debugging and profiling features. Since most users use the release version of Flash Player, use this version to do performance tuning; running Flex applications with the Flash Debug Player version does not accurately represent the performance of your application. When running a SWF file, the Flash Debug Player version reports trace statements and warnings. This task requires ActionScript processing cycles that would otherwise resource a running application, which impacts the perceived application performance.
When you are ready to test application performance, verify you are running the application with Flash Player 7, the release version. Many Flex application developers make this simple mistake! To verify the version of Flash Player, run a Flex application in the browser you use during development, and then right-click inside the browser window. If you see a debug option in the context menu, you are running the Flash Debug Player version. If you fine-tune your application to perform well with the Flash Debug Player version, you can ensure that your application will perform the same, if not significantly better, with Flash Player 7.
For more on using the Flash Debug Player to debug client-side code, read Debugging Client-Side Code in Flex Applications.
The biggest Flex performance danger is yielding to the temptation to use containers randomly. Using too many containers dramatically reduces the performance of your application. This is the number one performance danger that Flex developers succumb to—and luckily it is 100 percent avoidable. The performance penalty occurs because Flex layout containers and their children follow sizing and measuring algorithms that determine x,y positions, preferred sizes, and styles. These calculations are resource-intensive; it is these calculations, coupled with Flash Player drawing complex objects, that cause a noticeable delay when starting a Flex application or when instantiating a new view in a navigator container. One principle dramatically speeds up application startup and interactivity time: Avoid unnecessary container nesting.
A good rule of thumb is to avoid excessive container nesting. At first, you might find it difficult to pinpoint superfluous container nesting. The following describes some of the more common cases of nesting containers and offers useful tips for choosing and using containers.
Below are a few examples of deeply nested code:
<mx:VBox>
<mx:HBox>
<mx:Form>
<mx:FormItem>
.....
...
</mx:FormItem>
</mx:Form>
</mx:HBox>
</mx:VBox>
and
<mx:Grid>
<mx:GridRow>
<mx:GridItem>
<mx:VBox>
<mx:Button />
</mx:VBox>
</mx:GridItem>
</mx:GridRow>
</mx:Grid>
When you nest containers, each container instance runs measuring and sizing algorithms on its children (some of which are containers themselves, so this measuring procedure can be recursive). When the layout algorithms have processed, and the relative layout values have been calculated, Flash Player draws the complex collection of objects comprising the view. By eliminating unnecessary work at object creation time, you give your application a boost and the performance benefits are readily apparent.
Typically, fewer containers provides good results with respect to creation time. If you find yourself nesting many levels deep, re-evaluate your choice of containers. Perhaps you can achieve the same layout with a different layout container in conjunction with style attributes, such as horizontal and vertical alignment, margins, spacers, and gaps. You can use margins and gaps to manipulate the space around controls and between the edge of controls and the edge of their parent containers. You can use spacer objects to fill unwanted space or to push controls around the screen. You can also align controls horizontally or vertically within their container. For example, take a look at the layout in Figure 1.
Figure 1. You can achieve this layout without using a Grid container.
It is tempting to use a Grid container to achieve this layout:
<mx:Grid>
<mx:GridRow>
<mx:GridItem>
<mx:Button label="Visa"/>
</mx:GridItem>
<mx:GridItem>
<mx:Button label="MasterCard"/>
</mx:GridItem>
<mx:GridItem>
<mx:Button label="Diner's Club"/>
</mx:GridItem>
<mx:GridItem>
<mx:Button label="AmEx"/>
</mx:GridItem>
</mx:GridRow>
</mx:Grid>
However, this code is unnecessarily bloated. In fact, you can easily revise the code so that it looks exactly like Figure 1. The following snippet uses less code and results in a faster creation time and a slightly smaller SWF output.
<mx:HBox> <mx:Button label="Visa"/> <mx:Button label="MasterCard"/> <mx:Button label="Diner's Club"/> <mx:Button label="AmEx"/> </mx:HBox>
Steven Webster, an active Flex community member, has an excellent entry in his blog on the dangers of nested containers, as well as tips on how to avoid nesting.
The Flex container classes are relative layout containers that arrange contents on the screen for you. However, the calculations to decipher how big each container and its children are, as well as where to place them, can potentially be resource-intensive. Here are two tips that can help reduce these calculations:
Think of a Grid container as a layout choice already pushing the deep
nesting rule. Grid, GridItem and GridRow are all containers in their own
right, although GridItem and GridRow are only used in conjunction with
the Grid container. You should only use a Grid container when your controls
must line up both horizontally and vertically. Developers often gravitate
to the Grid container, because they see the similarity to the HTML <table> tag.
However, as a Flex developer, you can choose from multiple container
choices to position objects (and some are less resource-intensive to use
then others). This is not true of the HTML world, where <table> is
really the only choice. The following code provides an example of when
to use a Grid container—the controls in the different columns and rows
must all line up (see Figure 2):
<mx:Grid> <mx:GridRow> <mx:GridItem><mx:TextInput text="TextInput"/></mx:GridItem> <mx:GridItem><mx:NumericStepper/></mx:GridItem> <mx:GridItem><mx:TextInput text="TextInput"/></mx:GridItem> <mx:GridItem><mx:NumericStepper/></mx:GridItem> </mx:GridRow> <mx:GridRow> <mx:GridItem><mx:Button label="button"/></mx:GridItem> <mx:GridItem><mx:DateField /></mx:GridItem> <mx:GridItem><mx:Button label="button"/></mx:GridItem> <mx:GridItem><mx:DateField /></mx:GridItem> </mx:GridRow> <mx:GridRow> <mx:GridItem><mx:TextInput text="TextInput"/></mx:GridItem> <mx:GridItem><mx:NumericStepper/></mx:GridItem> <mx:GridItem><mx:TextInput text="TextInput"/></mx:GridItem> <mx:GridItem><mx:NumericStepper/></mx:GridItem> </mx:GridRow> </mx:Grid>
Figure 2. This UI is the perfect candidate for a Grid container.
Some common misuses of Grid containers include the following:
Using the Grid container when you want to left-justify or right-justify controls in the same container (see Figure 3). Developers often try to do this using the following code:
<mx:Grid borderStyle="solid" width="400"> <mx:GridRow> <mx:GridItem horizontalAlign="left"> <mx:Button label="left" /> </mx:GridItem> <mx:GridItem horizontalAlign="right"> <mx:Button label="right" /> </mx:GridItem> </mx:GridRow> </mx:Grid>
However, using an HBox container with a Spacer object to fill unwanted space works the exact same way, as shown in the following snippet:
<mx:HBox borderStyle="solid" width="400"> <mx:Button label="left" /> <mx:Spacer width="100%" /> <mx:Button label="right" /> </mx:HBox>
![]()
Figure 3. You can achieve this left-justify/right-justify layout without a Grid.
Using a Grid container as a child of a Repeater object when alternate mechanisms would work better. Take a look at the Repeater object in Figure 4.
Figure 4. Using a Grid container in a Repeater object to achieve this layout is resource-intensive
This Repeater object repeats a heavily populated Grid container with labels retrieved from a web-service. Creating just one of these Grid containers would take a noticeable creation time, but repeating this Grid many times is worse.
As your Flex repertoire expands, you will see that there are alternate containers and controls that you can easily customize to meet your needs. For example, you can achieve the layout shown in Figure 4 with a List control and a custom cell renderer. You can use a cell renderer with any list-based component to create custom-formatted cells, and the cells can be different heights. The Flex documentation will have more information and examples on how to create a custom cell renderer. The performance of a List control with a custom cell renderer is spectacularly better than with a repeated Grid container. Or you can use the HorizontalList and TileList controls to create custom controls in an HBox or Tile-like layout. These controls perform very well because they only create elements visible in the initial view and then the user can scroll to see subsequent elements—instantiation time is quicker.
The following list provides examples of common container redundancies to avoid:
The VBox container inside an <mx:Panel> tag—A Panel container is a VBox container with support for a title bar, rounded borders, and other Panel styles. If you want Panel children to lay out as they would in a VBox container, populate the <mx:Panel> tag directly with controls; do not wrap a VBox container around the controls. The VBox container would be a redundant container wrapper, and removing this eliminates one more level of unnecessary container nesting.
For example, instead of writing this:
<mx:Panel title="Grocery List" width="150" height="150"> <mx:VBox> <mx:Label text="Fruits" /> <mx:Label text="Veggies" /> <mx:Label text="Cookies" /> <mx:Label text="Crackers" /> </mx:VBox> </mx:Panel>
Use this code instead:
<mx:Panel title="Grocery List" width="150" height="150"> <mx:Label text="Fruits" /> <mx:Label text="Veggies" /> <mx:Label text="Cookies" /> <mx:Label text="Crackers" /> </mx:Panel>
Both achieve an identical layout.
VBox container inside an <mx:Application> tag—An Application object is inherently a VBox container layout. It is unnecessary to wrap your <mx:Application> tag with a VBox container; removing this wrapper will eliminate one more level of container nesting.
For example, instead of writing this:
<mx:Application xmlns:mx="http://www.macromedia.com/2003/mxml">
<mx:VBox horizontalAlign="center" backgroundColor="#EFEFEF">
<mx:Label label="Shopping Cart" />
.
.
.
</mx:VBox>
</mx:Application>
Use this code instead:
<mx:Application xmlns:mx=http://www.macromedia.com/2003/mxml
horizontalAlign="center" backgroundColor="#EFEFEF">
<mx:Label label="Shopping Cart" />
.
.
.
</mx:Application>
Containers as top-level tags for MXML components—The beauty of MXML components is that they provide a way to modularize repeated code. However, these components are vulnerable to the same performance pitfalls as the larger application. You do not need to use a container tag as the top-level tag of your MXML component definition. It is perfectly valid for an MXML component to be a simple control, like:
<mx:Image xmlns:mx=http://www.macromedia.com/2003/mxml source="@embed('foo.jpg')" width="200" height="200" />
By stepping through MXML component definitions and purging unneeded container wrappers, you can reduce container nesting (and many unnecessary objects), and, subsequently, ease the load of your application.
Container wrappers for an MXML component instance—
Normally, there is no need to wrap a container around an MXML component
tag. You can set different styles, labels, and ids within the instance
of the MXML component—you don't need to set them within a container
that wraps the MXML component. For example, instead of wrapping an
unnecessary VBox container around your MXML component to set some
styles like this:
<mx:VBox backgroundColor=" #FFCCCC" borderStyle=" solid"> <myComponent xmlns=" *" /> </mx:VBox>
You can set those styles within the MXML component tag itself, like this:
<myComponent xmlns=" *" backgroundColor=" #FFCCCC" borderStyle=" solid" />
Developers often couple this bad practice with using unnecessary containers as top-level tags for MXML components, which can create at least two superfluous containers for each MXML component used! This creates many unused objects that, when eliminated, yield a dramatic improvement in the responsiveness of your application.
If the number one cause of performance problems is unnecessary measurement
and layout from superfluous container nesting, the number two cause is
creating objects before they are needed. To avoid this problem, you can
use deferred instantiation. Flex uses deferred instantiation to determine
which components to create at application startup. When using deferred
instantiation, you can decide at which stages the user incurs the costs
of component creation. Containers have a creationPolicy property
that you set to specify when Flex should create the container (at startup,
incrementally, when a user navigates to that container, or based on other
user action).
The Flex navigator containers (ViewStack, Accordion, TabNavigator) have built-in deferred instantiation behavior. The default deferred instantiation behavior means that Flex does not create all the child views at startup, but only when a user triggers it by navigating to the container. The following code shows two navigator containers, TabNavigator and ViewStack, in use:
<mx:TabNavigator> <mx:VBox id="tabNavView1"> <mx:LinkBar dataProvider="myViewStack" /> <mx:ViewStack id="myViewStack"> <mx:VBox id="view1" > . . . </mx:VBox> <mx:VBox id="view2" > . . . </mx:VBox> <mx:VBox id="view3" > . . . </mx:VBox> </mx:ViewStack> </mx:VBox> <mx:VBox id="tabNavView2"> . . . </mx:VBox> </mx:TabNavigator>
The TabNavigator container creates tabNavView1 because it is the first view displayed when Flex instantiates the TabNavigator container. Instantiating tabNavView1 will cause the LinkBar and the first view of the ViewStack, view1, to be instantiated. When the user interacts with the LinkBar to select another view in the ViewStack, Flex will create that view. Flex continues in this way, creating the navigator container descendants as it calls them.
The creationPolicy property on container tags control the creation of child views. The following list explains what each creationPolicy property does when set on Flex navigator containers:
creationPolicy="auto"—When Flex creates the navigator containers, it does not immediately create all of their descendants, only those that are initially visible. The result of this deferred instantiation is that an MXML application with a navigator container loads quickly, but users experience a brief pause the first time they navigate from one view to another. Usability studies have shown this is a better user experience then having to wait a noticeable amount of time at application startup to create the navigator containers' child views. Also, there is always the possibility that the user may never visit some of the child views, so creating them at startup is potentially wasteful.
Note that if you set creationPolicy="auto" on a non-navigator container, you must add extra code to specify when to create the container's children. This extra code is built-in in the navigator containers, which is why you can set creationPolicy="auto" on a navigator container without doing any extra work.
creationPolicy="all"—When Flex creates the navigator containers, it creates all of the controls in all their child views. This setting causes a delay in application startup time, but results in a quicker response time when navigating from view to view.creationPolicy="none"—Flex does not
instantiate any component within the navigator container or any of
the navigator component's child views until you explicitly call the
instantiation methods. You explicitly instantiate views with the createComponents() method.
The Flex documentation has more information on setting up a custom
component creation plan.creationPolicy="queued"—Flex creates
all containers and then creates the children of the queued containers
in the order in which they appear in the application unless you specify
a creationIndex property. This setting causes components
in your application to become visible in successive fashion, reducing
the amount of time it takes for the user to start viewing the application.
In the following "Progressive Layout" section I
give an example and more information on this feature. By playing with the creationPolicy properties, you can manually
handle the creation of child views and decide where in your application
architecture you want to incur the cost of creating the child views of
your navigator containers. Usability studies show that a better user experience
is proffered when using the auto setting. A common mistake
that can inadvertently slow your application startup time is to mistakenly
set creationPolicy="all" on one of your navigator
containers. Use creationPolicy="all" only if you
are completely sure that the component creation plan you have in place
is efficient
and timely.
In the process of fine-tuning performance, you may reach a point where you have whittled your application's startup time to as fast as possible. However, this does not mean there are no more performance improvements you can make; you could choose to use progressive layout. Progressive layout is a UI concept that involves wiring your application to lay out components in a piece-by-piece fashion so there is a shorter initial delay before components begin appearing on the screen. Progressive layout is similar to the way HTML applications load content in succession in a client.
Progressive layout does not quantifiably reduce application startup time, but it significantly improves the perceived startup time. You implement progressive layout by using the queued creationPolicy in the deferred instantiation architecture of Flex. See the example below, which loads three panels in successive fashion.
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.macromedia.com/2003/mxml" >
<mx:HBox>
<mx:Panel id="panel1" title="Panel 1" width="210" height="240" horizontalAlign="center" verticalAlign="middle" creationPolicy="queued" creationIndex="1">
<mx:DataGrid width="190" height="95">
<mx:dataProvider>
<mx:Array>
<mx:Object Artist="Death Cab for Cutie" />
<mx:Object Artist="The Postal Service" />
</mx:Array>
</mx:dataProvider>
</mx:DataGrid>
<mx:DataGrid width="190" height="95">
<mx:dataProvider>
<mx:Array>
<mx:Object Album="Such Great Heights" />
<mx:Object Album="We Know the Facts" />
</mx:Array>
</mx:dataProvider>
</mx:DataGrid>
</mx:Panel>
<mx:Panel id="panel2" title="Panel 2" width="210" height="240" horizontalAlign="center" verticalAlign="middle" creationPolicy="queued" creationIndex="2">
<mx:List width="190" height="95">
<mx:dataProvider>
<mx:Array>
<mx:String>one</mx:String>
<mx:String>two</mx:String>
<mx:String>three</mx:String>
<mx:String>four</mx:String>
</mx:Array>
</mx:dataProvider>
</mx:List>
<mx:List width="190" height="95">
<mx:dataProvider>
<mx:Array>
<mx:String>red</mx:String>
<mx:String>green</mx:String>
<mx:String>yellow</mx:String>
<mx:String>blue</mx:String>
</mx:Array>
</mx:dataProvider>
</mx:List>
</mx:Panel>
<mx:Panel id="panel3" title="Panel 3" width="210" height="240" horizontalAlign="center" verticalAlign="middle" creationPolicy="queued" creationIndex="3">
<mx:Tree width="190" height="95">
<mx:dataProvider>
<mx:XML>
<node label="File">
<node label="Open"/>
</node>
<node label="Close">
<node label="Help"/>
</node>
</mx:XML>
</mx:dataProvider>
</mx:Tree>
<mx:TextArea width="190" height="95">
<mx:text>The Macromedia Flex presentation server offers a familiar, standards-based programming framework and powerful set of components for creating a rich, responsive presentation tier for enterprise Rich Internet Applications (RIAs).</mx:text>
</mx:TextArea>
</mx:Panel>
</mx:HBox>
</mx:Application>Jason Szeto, a developer on the Flex team, has written an in-depth article on implementing progressive layout in Flex applications, including using progressive layout in data-driven applications:"Building Flex Applications with Progressive Layout."
Sometimes your application may appear slow because it manages a large amount of data at once. Matt Chotin, a software engineer on the Flex team, has some excellent entries on data management that address this issue. He discusses how to incorporate paging, sorting, and improving perceived performance on his blog.
You may notice that transition effects seem choppy, especially when your effect has a short duration applied to a large view. What defines choppy? For example, a Fade effect that fades in a few distinct alpha stages, instead of a smooth and seamless fade. Or a Zoom effect that zooms in a few distinct sizes, instead of a gradual and smooth Zoom. There are a few ways to tweak your transition effects to play in a smoother fashion. Try the following suggestions and see which work best to improve effects in your application:
The less there is for Flash Player to redraw during an animation,
the smoother the effect plays. To do this, make parts
of the target view invisible when the effect starts, play the effect,
and then make those parts visible when the effect has completed.
The populating of controls happens so quickly that the human eye
does not notice any sort of delay or sudden appearance of the controls.
Coding this is simple: You hook into the effectStart and effectEnd events
to control what is visible before and after the effect.
See the following Flex SWF:
To view this content, you need the latest version of the Macromedia Flash Player.
Download the free Macromedia Flash Player now.
The panel has a populated DataGrid control with a fast Fade effect
applied to it, and a duration of 250 milliseconds. As you toggle
the panel's visibility, see how the Fade plays in an abrupt fashion?
Adding the following code to the <mx:Panel> tag
lessens the number of objects Flash Player must redraw during an
animation:
effectStart="myDataGrid.visible=false" effectEnd="myDataGrid.visible=true"
Now Flash Player does not draw the DataGrid control, but concentrates on redrawing the empty Panel container.
See the following Flex SWF, which uses this technique:
To view this content, you need the latest version of the Macromedia Flash Player.
Download the free Macromedia Flash Player now.
See the difference? The effect is smoother and better-looking. Because
the duration is so fast, there is no noticeable disappearing and appearing
of the DataGrid. Only use this technique if the effect duration is
relatively short (500 ms or less), and only on a showEffect or hideEffect.
In other situations, hiding pieces of the object will not appear seamless.
The Resize effect, a native effect in Flex, has some of this functionality already
built in. You can use the hideChildren property to specify an array
of panels whose children should be hidden while the effect plays. This property
only works with Panels and help your Resize effects to play smoother. Before
the Resize animation plays, Flex iterates through the hideChildren array
and hides the children of each of the specified panels. Note: You cannot use
the
hideChildren property with an effect declared in MXML (such as in
the <mx:Resize> tag).
The effect must be triggered in ActionScript to use of the hideChildren functionality.
Sometimes animations play in a choppy fashion because background processing occurs and interferes with the animation. You may notice this when you have an effect attached to a handler that populates controls from a web-service result, or when you have an effect accompanying the creation of a large view. The set of tags are a subclass of the Effect class (Fade, Move, Resize, WipeLeft, and so on) have a public property, suspendBackgroundProcessing. When it is true, it blocks all background processing like measurement and layout while the effect plays. The default is false. Macromedia suggests that you set this property to true for a smooth playing of effects. However, you must realize that when you switch on suspendBackgroundProcessing, your effect cannot be interrupted while playing. Because of this, there are a few cases where you should avoid using suspendBackgroundProcessing="true". One common use of effects is to play an effect while the application waits for a web-service result to return. After the web-service result returns, the result handler tries to interrupt and stop the effect. If suspendBackgroundProcessing is set to true, the result handler cannot interrupt the effect, and the effect plays continuously, hanging the application. Avoid using suspendBackgroundProcessing in these cases.
Runtime cascading styles are very powerful, but you should use them sparingly
and in the correct context. Dynamically setting styles on an instance
of an object means calling UIObject's setStyle() method.
The setStyle() method
is one of the most expensive calls in the Flex application model
framework, because the call requires notifying all the children of the
newly styled object to do another style lookup. The resulting tree of
children that must be notified can be quite large.
A common mistake that impacts performance is overusing or unnecessarily
using the setStyle() method. In general, you only need
the setStyle() method when you want to change styles on existing
objects. Do not use it when setting up styles for an object for the first
time. Instead, set styles in an <mx:Style> block, as
explicit style properties on the MXML tag, through an external CSS style
sheet, or as global styles. It is important to initialize your objects
with the correct style information, if you do not expect these styles
to change while your program executes (whether it is your application,
a new view in a navigator container, or a dynamically created component).
Some applications need to call the setStyle() method during the application or object instantiation. If this is the case, call the setStyle() method early in the instantiation phase to avoid unnecessary lookups. Early in the instantiation phase means setting styles from the component or application's initialize event, instead of the creationComplete or other event. By setting the styles as early as possible during initialization, you avoid unnecessary style notification and lookup.
New in Flex 1.5 is the addition of the HorizontalList and TileList controls. You can use these controls for layouts that require dynamically repeating elements. They perform much better than layouts that used Repeaters. In fact, layouts created during the Flex 1.0 timeframe that used a Repeater may often be replaced by a combination of the HorizontalList or TileList controls and cell renderers for better performance.
The HorizontalList and TileList controls are List controls that display data elements horizontally or in a tile layout, much like the HBox or Tile containers. Unlike the Repeater object, performance with these controls is determined by what is visible in the HorizontalList and TileList at that time. This behavior reduces instantiation time of the view significantly; a Repeater’s instantiation time will always be equal to or worse than the HorizontalList and TileList controls.
The HorizontalList and TileList controls usually contain a horizontal or vertical scroll bar, used to access all the items in the list. When a user scrolls, it triggers the creation of subsequent elements in the List. Thus, the user avoids creating all the possible elements at startup; they are created only when requested.
The HorizontalList and TileList controls perform much better than a Repeater object. In most cases it is a better choice to use them instead of a Repeater. However the Repeater control is still available in Flex; it is better to use the Repeater control to repeat simple elements. For example, it would make more sense to repeat a collection of RadioButton controls using a Repeater then a Horizontal or TileList control.
There are a few things to think about if you need to improve your Repeater object's performance. First, if you are using containers as the child of the Repeater object, check whether using a HorizontalList or TileList control would be better. If that is not the case, ensure that the containers used as the children of the Repeater do not have unnecessary container nesting and are as trim as possible. If a single instance of the repeated view takes a noticeable time to instantiate, repeating makes it worse. As mentioned previously in this article, multiple Grid containers in a Repeater object do not perform well because Grid containers themselves are resource-intensive containers to instantiate. An alternative solution is to try a List control with a custom cell renderer or the HorizontalList or TileList controls.
You should also set the recycleChildren property to true to improve a Repeater object's performance. The recycleChildren property is a boolean value that, when set to true, binds new data items into existing Repeater children, incrementally creates new children if there are more data items, and destroys extra children that are no longer required.
When you set this property to false, the Repeater object recreates all the repeated objects when you swap dataProvider properties, sort, and so on, which causes a performance lag. The FlexStore sample application has an example of recycleChildren at play. When you load the application, notice the Sort by option under the product thumbnail view. This enables users to sort the product thumbnails based on name or price. The ordering of the Repeater object's dataProvider property is what changes, the thumbnail views do not. By setting the recycleChildren property to true, the Repeater object does not recreate each thumbnail view; it simply reshuffles the dataProvider property based on name or price.
The default value of the recycleChildren property is false to
ensure that you do not leave stale state information in a repeated instance.
For example, suppose you use a Repeater object to display photo images
and each Image control has an associated NumericStepper control for how
many prints you want to order. Some of the state information, such as
the image, comes from the dataProvider property, while other state information,
such as the print count, is set by user interaction. If you set the recycleChildren property
to true and page through the photos by incrementing the Repeater
object's startingIndex value, the Image controls bind to
the new images, but the NumericStepper control maintains
the old information! You should use recycleChildren="false" only
if it is too cumbersome to reset the state information manually, or if
you are confident that modifying your dataProvider property should not
trigger a recreation of the Repeater object's children.
This may go without saying, but the recycleChildren property has no effect on a Repeater object's speed when the Repeater object loads the first time. The recycleChildren property only improves performance for subsequent Repeater occurrences. If you know that your Repeater object will only create children once, there is no need to use the recycleChildren property or worry about the stale state situation.
Flex provides a very robust library of charting components that give you a two-dimensional visual representation of your data. These charting components follow the same guidelines as other Flex components, including the same performance drawbacks.
The Flex charts have been designed to perform well. All charts cache intermediary values in the transformation from data to screen, so that only the minimum amount of recalculation occurs in response to any change to the data or chart. The most expensive actions to perform in Flex charts is forcing a chart to redraw an axis, or forcing a chart to recalculate its labels. In fact, it is faster to resize a chart than to change its dataProvider (a change that requires a chart to potentially redraw an axis or recalculate labels). Below are further tips that you can use to improve the performance of your Flex charting components.
filterData property
to false. In
the transformation from data to screen coordinates, the various Series types
filter the incoming data to remove any missing values or values outside the
range of the chart that would render incorrectly if drawn to the screen.
For example, a chart representing vacation time for each week in 2003 might
not have a value for the July fourth weekend since the company was closed.
If you know your data model will not have any missing values at runtime,
or values that fall outside the chart’s data range, you can instruct a series
to explicitly skip the filtering step by setting its filterData property
to false, earning you a small performance boost. LinearAxis autocalculate
its range.
A LinearAxis calculating its numeric range can be a costly
calculation. If you know reasonable minimum/maximum values for the range
of your LinearAxis, specifying them help your charts render quicker. Also,
specifying an interval (the numeric distance between label values along
the axis) value improves performance. CategoryAxis dataProvider and Series dataProvider refer
to different objects. Modifying a CategoryAxis object’s
dataProvider is more expensive than modifying a Series object's
dataProvider. If the data bound to your chart is going to change,
but the categories in your chart will stay static, have the CategoryAxis'
dataProvider and Series' dataProvider refer to different objects.
This prevents the CategoryAxis from reevaluating its dataProvider, which
is a resource-intensive computation. AxisRenderers by setting particular styles. The AxisRenderers perform many calculations to make sure they render correctly in all situations.
The more help you can give them in restricting their options, the faster
they will render. Explicitly setting the following styles on the AxisRenderer will improve performance: labelRotation and canStagger.
You can set these styles within the tag or in CSS. gutterLeft, gutterRight,
gutterTop, and gutterBottom values, your charts draw quicker
and more efficiently. BarSeries, ColumnSeries, and LineSeries use ShadowRenderers.
ShadowRenderers draw drop shadows beneath the data elements.
If DropShadows are not necessary, switch to the Simple renderers
(SimpleBoxRenderer for ColumnSeries and BarSeries,
SimpleLineRenderer for LineSeries); this improves
the rendering speed of your charts. Test the performance of your application early and often. It is always
best to identify problem areas early and resolve them in an iterative
manner, rather then trying to shove performance enhancements into existing,
poorly performing code at the end of your application development cycle.
The following subsections
investigate using Runtime Shared Libraries (RSLs) to improve performance,
as well as
describe two approaches to performance testing your Flex client: using the ActionScript
profiler and using a code snippet that times application initialization.
We also provide a handy solution to time component and data gestures using
the ActionScript getTimer() method. (See also the "Load
Testing Your Flex Application" section in the "Flex
Application Performance: Tips and Techniques to Improving Flex Server Performance" article.)
You can shrink the size of your application's resulting SWF file by externalizing shared assets into standalone files that you can separately download and cache on the client. Multiple Flex applications can load these shared assets at runtime, but each client need only to download them once. These shared files are called Runtime Shared Libraries.
Flex projects that have multiple Flex applications downloaded to the client can leverage RSLs for better performance. More specifically, the time it takes to download a Flex application once the initial RSL has been downloaded is significantly reduced.
However, not all applications benefit from RSLs. The following are examples of Flex applications that might use RSLs for better performance:
Depending on the type of Flex project you are developing, RSLs may or may not offer a performance benefit. Roger Gonzalez, a developer on the Flex team, has a more in-depth article focused on RSLs: "Using Runtime Shared Libraries."
The ActionScript profiler records the time Flash Player takes to perform tasks in ActionScript. Most commonly, you use the profiler to determine how long an ActionScript function or method takes to execute, how often it is called, and how much time is spent executing in its descendant. This helps identify which objects might be taking too long to initialize, or whether there are bottlenecks due to heavy graphics use or poor coding. However, running the profiler adds overhead to the application you are analyzing. This is because the profiler runs with the Flash Debug Player version, which is slower than the release version. Analyze the results returned with the profiler relative to each other, but do not take them as correct absolute times. Running an application in the release version of Flash Player will yield different results when compared to running the same application with the Flash Debug Player version. The Flex documentation has more information on how to install and run the ActionScript profiler. Macromedia Flex Evangelist Christophe Coenraets also has an excellent blog entry on optimizing application performance with the ActionScript profiler.
A more simple approach to performance profiling is to use code to gauge startup time. The following snippet times application initialization time (the time it takes the Application object to create, measure, lay out, and draw all of its children); it does not include the time to download the client SWF, or any of the server-side processing such as checking the Flash Player version, checking the SWF cache, and so on. The following example shows a sample Flex application that, when invoked in a browser, shows a simple form populated with controls, with the time it took to initialize and print a label.
<?xml version="1.0" encoding="iso-8859-1"?>
<mx:Application xmlns:mx="http://www.macromedia.com/2003/mxml" creationComplete="doLater(this,'doneNow')">
<mx:Script><![CDATA[
var dp = [{food:"apple", type:"fruit", color:"red"}, {food:"potato", type:"vegetable", color:"brown"}, {food:"pear", type:"fruit", color:"green"},
{food:"orange", type:"fruit", color:"orange"},{food:"spinach", type:"vegetable", color:"green"},{food:"beet", type:"vegetable", color:"red"}];
function doneNow()
{
doLater(this, "reallyDoneNow");
}
function reallyDoneNow()
{
timerLabel.text += getTimer() + " ms"
}
]]></mx:Script>
<mx:Form>
<mx:FormHeading label="Sample Form" />
<mx:FormItem label="List Control">
<mx:List dataProvider="{dp}" labelField="food"/>
</mx:FormItem>
<mx:FormItem label="DataGrid control">
<mx:DataGrid width="200" dataProvider="{dp}"/>
</mx:FormItem>
<mx:FormItem label="Date controls">
<mx:DateChooser />
<mx:DateField />
</mx:FormItem>
<mx:FormItem label="Load Time">
<mx:Label id="timerLabel" fontSize="12" fontWeight="bold" text="The application initialized in "/>
</mx:FormItem>
</mx:Form>
</mx:Application>
The first thing is to get a baseline reading of your application startup time. Ensure that you exit all other running applications, verify that you are using the release version of Flash Player, and run the application three times to get three initialization times. The average of these three times is your baseline reading. As you implement performance tuning, confirm that the startup time is indeed getting faster.
This code will also help you see which portion of your application is the slowest and identify whether you can speed it up. To try this, selectively remove parts of your application to see whether the single part's startup time is relative to the entire application's startup time. This iterative process highlights the problem areas of your application. For example, this methodology might show you that View B of your application loads in six seconds out of an overall startup time of twelve seconds. View B is a problem area for which you can drill down into and investigate performance alternatives. This process is simpler than setting up and using the ActionScript profiler, although it yields much less detailed information.
The getTimer() function is a very useful ActionScript function
that returns the number of milliseconds since a Flex client application
has been running in the browser. Attaching getTimer() calls
to events enables you to time component and data gestures. Brandon Purcell,
coauthor of this article and a support engineer at Macromedia, has a blog
entry explaining how to time Flex data service calls using getTimer().
We have made every effort to ensure the accuracy of this article and all code included. Feedback regarding this article and all Flex performance issues is always appreciated.
Deepa Subramaniam is a software engineer for the Flex SDK. Straight out of UC Berkeley (Go Bears!), with an undergraduate degree in computer science, she has been at Adobe (formerly Macromedia) since 2004 and is very excited to be working on all things Flex related.