This blog isn't maintained anymore. Check out my current project, an agile/scrum management tool.

Sunday, November 04, 2007

Slow Flex Builder compile and refresh solution - Modules

For the past month I've been plagued with up to three minute compile times on Flex Builder 3 Beta 2. I've also been plagued with long several minute waits of "Refresing bin/".

Last week I solved the issue for one of my projects and prevented it from happening to another.

Apparently embedding graphics causes a performance hit in compiling, refreshing directories, and even just before launching. So if you have a hundred or so of these:

<mx:Button icon="@Embed(...)" />

You'll be in a world of pain. The easiest solution is to move those embed calls into your style sheet and then compile your style sheet as a module.

Here's how to make a style sheet module:

1) Create a new MXML module (file->new->flex->MXML module)
2) Add your stylesheet reference to that.


<?xml version="1.0" encoding="utf-8"?>
<mx:Module xmlns:mx="http://www.adobe.com/2006/mxml"
layout="absolute" width="0" height="0"
creationComplete="trace('iconsloaded')">
<mx:Style source="style.css" />
</mx:Module>

3) Next, remove the reference to your style sheet from your main application.
4) Now, in your main MXML or AS file add some code to load your module.

protected var styleLoader:ModuleLoader;
protected function init() : void
{
styleLoader = new ModuleLoader();
styleLoader.addEventListener(ModuleEvent.READY, onModuleLoad );
styleLoader.addEventListener(ModuleEvent.ERROR, onModuleError );
styleLoader.url = "IconsModule.swf";
styleLoader.loadModule();
}
protected function onModuleLoad( e:ModuleEvent ) : void
{
maximize();
var mainApp:MainApplication = new MainApplication();
mainApp.percentHeight = 100;
mainApp.percentWidth = 100;
addChild(mainApp);
}

protected function onModuleError(e : ModuleEvent ) : void
{
Alert.show("Could not load module IconsModule.swf","Error",Alert.OK );
}

You can see there that I intialize my main application component after the module has loaded. I didn't have to do this in a Flex app, but in my AIR app that component wouldn't get all of the new styles if created before the module was loaded.

Option 2

Sometimes, it wasn't practical for me to put the embedded graphic in a stylesheet. In those cases I:

1) Made an interface listing all the embedded graphics

public interface MyGraphicsModule
{
// Bindable tags are there purely to suppress warnings,
// these properties never change after startup.
[Bindable(event="moduleChanged")] function get addIcon() : Class;
...
}

2) Make a class that implements that interface and extends ModuleBase, also embed the graphics in there.

public class MyGraphics extends ModuleBase implements TimeLinerGraphicsModule
{
[Embed(source="/art/toolbars/add.png")]
protected var _addIcon:Class;
public function get addIcon() : Class { return _addIcon; }

...
}
NEVER import or otherwise use this class into your application. The goal is to keep it completely separate.

3) Create a module manager class

public class MyModules
{
[Bindable] public static var graphics:MyGraphicsModule;
}


4) Set MyGraphics to be a module that gets built in your Project->Properties->Flex Modules settings.

5) Load your module somewhere at startup

public function loadModules() : void
{
graphicsModuleLoader = new ModuleLoader();
graphicsModuleLoader.addEventListener(ModuleEvent.READY, onModuleLoaded );
graphicsModuleLoader.addEventListener(ModuleEvent.ERROR, onModuleLoadFailure );
graphicsModuleLoader.url = "MyGraphics.swf";
graphicsModuleLoader.loadModule();
}

protected function onModuleLoaded( event:ModuleEvent ) : void
{
if( event.target == graphicsModuleLoader )
{
var o:Object = event.module.factory.create();
MyModules.graphics = event.module.factory.create() as MyGraphicsModule;
}
}

6) Find the spot in your code that used that icon and replace it

So this:

[Embed(source="/art/icons/addIcon.png")]
public var addIcon:Class;


Would become this:

public var addIcon:Class = MyModules.graphics.addIcon;


This all seems like a pretty tedius job to do, but I ended up writing a perl script to scour the source tree, find all the references, and replace them for me. Unfortunately I did that script as part of my day-job and can't share it with everyone right now. I'll ask for permission on Monday.

The end result was we went from a 3.5 minute wait from saving a code change to executing the application to a 20 second wait. Not too shabby considering the size of the codebase we have.

Labels: ,

6 Comments:

  • Great solution, well done Marc!

    By Blogger wenzi, At 11/07/2007 5:30 AM  

  • Would creating a runtime CSS SWF accomplish the same thing as option 1? Why not use that method over creating a Module SWF?

    By Blogger Unknown, At 11/07/2007 6:13 AM  

  • Hey, neat, look at that ... runtime css swfs. Learn something new everyday. :)

    It certainly does look like it would cover option #1. We combined both options into a single module so we'll probably just leave ours but that does seem like an easier solution.

    By Blogger Marc, At 11/07/2007 6:25 AM  

  • Also you may consider right clicking your flex project in Flex Builder 3 Beta 2 and choosing Properties > Flex Compiler. Then UNCHECK the box that says - "Copy non-embedded files to output directory". from my experience in FlexBuilder 3 beta 2 this drastically increased my compile time. It's just way too much to add every sub folder etc... when all you NEED is your embedded assets. I realize the module issue works but may also become tedious at times. Also Symbolic links to bin Directory works pretty good too for runtime assets. Anyways my 2 cents yo.

    By Blogger Frankie Loscavio, At 11/16/2007 12:19 PM  

  • Try to put this arguments on Project->Properties->Flex Compiler options panel.

    Where is: -locale en_US

    Write: -locale en_US incremental=true -keep=true

    It increases a little the building/compilling speed.

    By Anonymous Anonymous, At 1/09/2008 5:29 AM  

  • Embedded PNG:s slow down a lot, not other media to my experience. Flex apparently rewrites PNG:s somehow. Avoiding large png:s (using swf instead of them) is quite often a solution.

    By Blogger Juise, At 4/16/2008 5:15 AM  

Post a Comment

Subscribe to Post Comments [Atom]



<< Home