At 9elements, Mathias and me have been working on a fairly large Project since March 2010 which is developed using Air and Flex 4. Recently we started optimizing a few things and found out a few tricks that we didn’t find on the internet before.
CSS
The CSS implementation in Flex for styling components is awful. If you’re not careful, CSS will eat into you CPU consumption because all but its most primitive features make use of very expensive component navigation to look up rules.
You should never use the descendant selectors as they require expensive parent() lookups. Limit yourself to id or Class-Selectors. If you need complex behaviour, it’s more efficient to introduce custom style properties via the [Style] metadata tag than to use descendant selectors. Example:
Instead of this:
<fx:Style>
custom|MyContainer s|Label {
color: #FF0000;
}
</fx:Style>
...
<custom:MyContainer>
<s:Label text="Hello World in Red" />
</custom:MyContainer>
<s:Label text="Hello World" />
...
Replace descendant selectors by moving them into the components directly as Stylenames. You can also use Class- or ID selectors, as they are all processed equally. This is of course conceptually unelegant and not even always possible, but trust me, it can be much faster:
<fx:Style>
s|Label.in-myContainer {
color: #FF0000;
}
</fx:Style>
...
<custom:MyContainer>
<s:Label text="Hello World in Red" styleName="in-myContainer"/>
</custom:MyContainer>
<s:Label text="Hello World" />
...
Your experience with this may vary depending on how complex your CSS already is but we could reduce CPU-consumption about 60% by optimizing our CSS this way. You might also think that traversing the ancestors, trying to find applicable CSS-Rules is a one-time cost. You’re only half-right. For one, this has to happen every time you add a UIComponent to the display list. Additionally, the CSS framework is far from bug-free. In our app complex rules had the engine run into an endless loop traversing the displaylist up and down, making the component unusable as soon as this happened.
XML
Try not to keep XML-Objects in Memory. Only create them for processing but release them as soon as you’re done with them. Better store the XML data as a string. This is because ActionScript seems to use a horribly inefficient way of storing XML structures. In our app we had about 20MB of XML-Strings, which took about 1.5GB of memory when converted to XML-Objects!
DescribeType vs. DescribeTypeCache
ActionScript uses XML to perform reflection via the flash.utils.describeType
method. This has two disadvantages: First the XML implementation is bloated (see last paragraph), second describeType is terribly slow. Actually Flex has a cached implementation of describeType.
For some reason however, Adobe thought it was a good idea to hide this from developers. Instead of using mx.utils.describeType
, you should use the mx.utils.DescribeTypeCache.describeTypemethod. To make this a little harder, the DescribeTypeCache class is marked with
[ExcludeClass]`, removing it from FlashBuilders autocompletion.
This makes the repeated lookup fasterbut if you are dealing with lots of different classes, the cache starts to become a memory problem. So, an even better idea would be to write your own cache, storing only the properties of each type that you’re interested in, in a plain object or Array that doesn’t use as much memory.
Resizing images
For certain components we were generating previews of images from disk. This should have been easy, with Flash being basically an engine for manipulating images quickly. It wasn’t. At least not in acceptable quality. After much googling the trick was to raise the renderquality of the stage, otherwise the generated thumbnails were jaggy and ugly. Long story short, this is how we did it:
public function scaleBitmap(originalImage:Bitmap, scale:Number):BitmapData
{
var originalWidth:Number = originalImage.width;
var originalHeight:Number = originalImage.height;
var newWidth:Number = originalWidth * scale;
var newHeight:Number = originalHeight * scale;
var matrix:Matrix = new Matrix();
matrix.scale(scale, scale);
stage.quality = StageQuality.BEST;
originalImage.pixelSnapping = PixelSnapping.NEVER;
originalImage.smoothing = true;
var scaledImageData:BitmapData = new BitmapData(newWidth, newHeight);
scaledImageData.draw(originalImage, matrix, null, null, null, true);
stage.quality = StageQuality.HIGH;
return scaledImageData
}
Continue reading