Flex applications size optimization

After quite some time of development we realised that our application flex has grown quite large, both in functionality and size. I should have chosed to develop it in GWT since I think we could not have done even 30% of the functionality and it would have been simpler :). Joke aside the size of all the modules has grown over 50M. These are some steps I have found in order to reduce the size. Note that I am using ant files to compile everything so adapt this to your method.

Remove debug information

This can be done by setting the “debug” option:

<mxmlc file="${src.dir}/myApp.mxml" keep-generated-actionscript="false"
incremental="true" debug="false" output="${build.dir}/myApp.swf"
allow-source-path-overlap="true" locale="${locale}" services="services-config.xml">
...

This quickly decreased the size almost in half to about 25M. But since this is a normal step some more optimization was expected.

Move reusable code to a RSL

There is a lot of documentation about RSL’s but the idea is simple. You move the reused code in a .swc file using the compc compiler and the add some special compilation options to load this code at runtime. There are however some problems. The first is related probably to the fact that the guys at adobe did not envisaged so much development of their flex language and that you might want to have more than a few components in a RSL. As such the list of components must be given as a space separated string. In my case there where > 20 components and I had no wish to add them on a very, very, very long and unmaintainable line so I found this trick on the internet which saved the day by converting path elements to a long line. Many thanks.

<fileset id="sources" dir="${src.dir}">
 <include name="myPackage/*.as" />
 <exclude name="**/utils.as" />
 </fileset>
 <pathconvert property="classes" pathsep=" " refid="sources">
 <chainedmapper>
 <globmapper from="${basedir}/${src.dir}/*" to="*" handledirsep="true"/>
 <mapper type="package" from="*.as" to="*" />
 </chainedmapper>
 </pathconvert>
 <echo message="classes is set to = ${classes}" />
 <compc output="${build.dir}/myApp-lib.swc" include-classes="${classes}" debug="${debug}">
 <source-path path-element="${src.dir}" />
...
 <include-resource-bundles bundle="MyBundle" />
...
 <sp path-element="${locale.dir}/{locale}" />
 </compc>

Ok so now the .swc file is created. In a somehow stupid way the .swc must now be unpacked since only the library.swf file inside is needed.

<unzip src="${build.dir}/myApp-lib.swc" dest="${build.dir}">
 <patternset>
 <include name="library.swf"/>
 </patternset>
 </unzip>
 <move tofile="${build.dir}/myApp-library.swf" file="${build.dir}/library.swf"/>

Now the library can be used by modifying the mxmlc task to use it. Note that you will need to know the url where the myApp-library.swf will reside.

<mxmlc file="${src.dir}/myApp.mxml" keep-generated-actionscript="false"
incremental="true" debug="${debug}" output="${build.dir}/diapason.swf"
allow-source-path-overlap="true" locale="${locale}">
 <load-config filename="${FLEX_HOME}/frameworks/flex-config.xml" />
 <source-path path-element="${FLEX_HOME}/frameworks" />
 <source-path path-element="${locale.dir}/{locale}" />

 <compiler.library-path dir="${FLEX_HOME}/frameworks" append="true">
 <include name="libs" />
 <include name="../bundles/{locale}" />
 </compiler.library-path>
 <compiler.library-path dir="libs" append="true">
 <include name="*.swc" />
 </compiler.library-path>
 <runtime-shared-library-path path-element="${build.dir}/myApp-lib.swc">
 <url rsl-url="/myApp/myApp-library.swf" />
 </runtime-shared-library-path>
 </mxmlc>

Now everything should work. Or not! In fact the mechanism works but there are some bugs.

What happened to the application.url ?

Actually the problem I had was related to the application.url as described in this bug as the string returned ended in myApp.swf/[[DYNAMIC]]/

var swfURL:String = Application.application.url;
//fix for https://bugs.adobe.com/jira/browse/SDK-18318 when using RSL-s
var bugPattern:RegExp = /\/diapason.swf.*/;
swfURL = swfURL.replace(bugPattern,'/diapason.swf');

This fixes the bug by eliminating the undesired part. A related problem happened in the modules since the this.loaderInfo.url was also bad. And bad in 2 ways. First is contained the path to the library.swf and second it ended in [[DYNAMIC]]. The solution in this case was a bit more complicated and consisted of passing the url from the ModuleLoader to the module in the onReady event.

Module optimization

The last step I did was to emulate the flex builder “optimize module for application” functionality. This consists in 2 steps. First when the application swf is compiled with the link-report option to generate a xml file with the classes used:

<mxmlc file="${src.dir}/myApp.mxml" output="${build.dir}/myApp.swf"
 keep-generated-actionscript="false" incremental="true" debug="${debug}" optimize="${optimize}"
 allow-source-path-overlap="true" locale="${locale}"
 link-report="${build.dir}/report.xml">
 <load-config filename="${FLEX_HOME}/frameworks/flex-config.xml" />
 <source-path path-element="${FLEX_HOME}/frameworks" />
 <source-path path-element="${locale.dir}/{locale}" />

 <compiler.library-path dir="${FLEX_HOME}/frameworks" append="true">
 <include name="libs" />
 <include name="../bundles/{locale}" />
 </compiler.library-path>
 <compiler.library-path dir="libs" append="true">
 <include name="*.swc" />
 </compiler.library-path>
 <runtime-shared-library-path path-element="${build.dir}/myApp-lib.swc">
 <url rsl-url="/myApp/myApp-library.swf" />
 </runtime-shared-library-path>
 </mxmlc>

Then the modules are compiled using the load-externs option:

<mxmlc file="${src.dir}/myModule.mxml" output="${build.dir}/myModule.swf"
 keep-generated-actionscript="false" incremental="true" debug="${debug}" optimize="${optimize}"  
 allow-source-path-overlap="true" locale="${locale}"
 load-externs="${build.dir}/report.xml">
 <source-path path-element="${src.dir}" />
 <source-path path-element="${locale.dir}/{locale}" />
 <load-config filename="${FLEX_HOME}/frameworks/flex-config.xml" />
 <compiler.library-path dir="${FLEX_HOME}/frameworks" append="true">
 <include name="libs" />
 <include name="../bundles/{locale}" />
 </compiler.library-path>
 <compiler.library-path dir="libs" append="true">
 <include name="*.swc" />
 </compiler.library-path>
 <runtime-shared-library-path path-element="${build.dir}/myApp-lib.swc">
 <url rsl-url="/myApp/myApp-library.swf" />
 </runtime-shared-library-path>
 </mxmlc>

This way the modules will be even smaller.

Final results after all these steps are that the application is now almost 10 times smaller.

After problems

One of the problems generated with this approach is that during debuging I sometime get the following error:

Flex Error #1001: Digest mismatch with RSL /myApp/myApp-library.swf. 
Redeploy the matching RSL or relink your application with the matching library.

There is an option for the mxmlc compiler to disable the digest verification with -verify-digests=false but unfortunatelly this option is not included in the ant task so for now I have to clear the cache often.

3 Responses

  1. […] See the original post here:  Flex applications size optimization […]

  2. […] This post was mentioned on Twitter by Edgard Davidson, Dennis Plucinik. Dennis Plucinik said: Reading: mxmlc config for optimized modules: http://bit.ly/a2yuWp […]

Leave a Reply

*