Optimizing your build

Several things can be optimized that reduce size, memory consumption in the client and execution performance. 

There are some conecpts you have to understand in order to know how some of the optimization works.

Reducing the Size of a Flex Application

In a SWC all classes/resources of a module are included. This is because in a library you cannot tell which parts will be used by an application using the SWC.

In a SWF only those classes directly or indireclty referenced by the Applications main Class (or MXML) are included.

In a multi-module Flex application some classes might be included in several modules, hence the class binaries will be loaded multiple times. This multiple loading of classes will unnecessarily (this is the first time I wrote "unnecessarily" correct in the first attempt ;-) ) increase the size of your application. The classes that will propably blow up your modules most will be the classes of the Flex Framework itself.

To address this problem there are several options: 

  1. Excluding classes that are allready loaded by a parent module
  2. Using Runtime Shared Libraries
  3. Using Signed RSLs (only applicable for the Flex framework RSLs)
  4. Using the Apparat Framework to optimize

Excluding classes that are allready loaded by a parent module

You could manually exclude classes you know that are allready loaded by a parent module by hand using an "excludeClasses" section in the modules flexmojo config:

<plugin>
   <groupId>org.sonatype.flexmojos</groupId>
   <artifactId>flexmojos-maven-plugin</artifactId>
   <extensions>true</extensions>
   <configuration>
   ...

      <excludeClasses>
         <excludeClass>de.cware.cweb.module.Login</excludeClass>
         <excludeClass>de.cware.cweb.module.User</excludeClass>
         ...
      </excludeClasses>
      ...
    </configuration>
</plugin>

But this can be a very booring and error prone task.

Here Flexmojos has an automatism that will handle this for you.

By setting the "linkReport" option to true, Flexmojos will generate a file "target/[module-name]-[module-version]-link-report.xml. This contains all information about which classes in which libraries are required by the current module.

In a Module that will load other modules:

<plugin>
    <groupId>org.sonatype.flexmojos</groupId>
    <artifactId>flexmojos-maven-plugin</artifactId>
    <extensions>true</extensions>
    <configuration>
       ...
       <linkReport>true</linkReport>
       ...
    </configuration>
</plugin>

You can now reference a link report of a modules parent module and Flexmojos makes sure these classes are not added to the current module, as it can assume these have allready been loaded.

In a Module that is loaded by another module:

<plugin>
   <groupId>org.sonatype.flexmojos</groupId>
   <artifactId>flexmojos-maven-plugin</artifactId>
   <extensions>true</extensions>
   <configuration>
    ...
      <loadExterns>
         <loadExtern>
            <groupId>[parent-module-groupId]</groupId>
               <artifactId>[parent-module-artifactId]</artifactId>
               <version>[parent-module-version</version>
         </loadExtern>
      </loadExterns>
      ...
   </configuration>
</plugin>

This will use the linkage-report of the parent module and omit all classes that are referenced there from the current module.

In my Case I usually have one really dumb main Application that loads several other modules in parallel. This means that the main loader doesn't contain too much of the classes that a lot of the child modules share. So in this case the benefit of this approach is minimal.

(warning)  Using this option will result in problems if you are using the Maven Release Plugin, as this doesn't update the versions in the loadExtern references. So if you are using the Maven Release plugin you allways have to manually update your poms. In my case this fact resulted in major problems.

Using Runntime Shared Libraries

Runntime Shared Libraries (RSLs) can be thought of as a Flex equivalent of DLLs or Jar-Files in Java. They are a SWC that is not statically linked into the binary but will be loaded at runntime by the Flash Player.

As mentioned in the last Chapter, a lot of my applications consist of a very simple and dumb application core, that loads the actual logic in multiple modules that exist parallel to each other. Therefore reusing the parents classes will not be very usefull and a lot of classes would still be loaded multiple times.

Using RSLs is a major improvement in this case, as the first module requiring a library will cause the Flash player to load that library and the loaded classes are then used by multiple modules.

Using RSLs is realy easy with Flexmojos, as all you have to do, is to specify a scope of "rsl" to dependencies that should be loaded from an RSL and tell the compiler the URL pattern from where it can actually load the rsls from:

<plugin>
   <groupId>org.sonatype.flexmojos</groupId>
   <artifactId>flexmojos-maven-plugin</artifactId>
   <version>${flexmojos.version}</version>
   <configuration>
      ...
      <rslUrls>
         <url>rsl/{artifactId}-{version}.{extension}</url>
      </rslUrls>
       ...
   </configuration>
      ...
</plugin>
...
<dependencies>
   ...
   <dependency>
      <groupId>[rsl-module-groupId]</groupId>
      <artifactId>[rsl-module-artifactId]</artifactId>
      <version>[rsl-module-version]</version>
      <type>swc</type>
      <scope>rsl</scope>
   </dependency>
   ...
</dependencies>

This will make the linker link the classes of that library as RSL loaded class.

The Flexmojos "copy-flex-resources" goal in the "war" module will now create a directory called "rsl" in the resulting war, that will contain the RSL binaries referenced in the probject.

In one of my projects without any optimization of this kind, all generated SWFs had a size of 25MB and by simply loading the Flex Framework as RSLs reduced the size to 6,5MB ... I would call this a significant optimization.

(warning)  I Strongly recommend to set the rslUrl to that value, as Flexmojos uses a slightly different one per default. The FM default is "/{contextRoot}/rsl/{artifactId}-{version}.{extension}". Usually I don't have "contextRoot" set and this results in invalid urls for rsl loading. By changing the url to the above value you also have a lot less problems if you use the standallone player/debugger to run your application as with this setting it will look relative to the path the swf was loaded from and this works when using http in a browser AND using file in the standallone player/debugger.

Using Signed RSLs

Now when using RSLs there will be some RSLs that will propably be loaded most: The Flex Framework RSLs. With introduction of the caching capabilities of the Flash-Player Adobe allowed to download officially signed Framework RSLs from Adobe and to install them on a client machine. This way the Framework RSLs are only loaded once and are reused the next time they are needed (Even if they were initially loaded from a different application/website). The cool thing is that the signed RSLs are first loaded from an Adobe Server in the web. Only if this fails, the local Webserver is queried. There are several things I find specially interestig in this approach:

  1. It dramatially reduces the size of an application
  2. It dramatically reduces the traffic on your applications webserver as the traffic for loading the Flex Framework is handled by Adobes Servers.

In general the only difference to the RSL approach is that you have to name the scope "caching" instead of "rsl" and that the resulting files in your wars "rsl" directory have a different file-ending.

As I mentioned above the caching mechanism only applies to libraries officially distributed by Adobe so when using custom libraries you will have to stick to the normal RSL approach.

Unfortunately you can't use the relatively easy way to reference all Flex Framework libraries by referencing the "framework" pom-dependency. This would include all Framework libs the default way. The solution I chose for now was to explicitly reference the framework libraries BEFORE the framework-pom.

<dependency>
   <groupId>com.adobe.flex.framework</groupId>
   <artifactId>framework</artifactId>
   <version>${flex.version}</version>
   <type>swc</type>
</dependency>

Was then replaced by (for a Flex 4.5 SDK >= Build 18261)

<dependency>
  <groupId>com.adobe.flex.framework</groupId>
  <artifactId>textLayout</artifactId>
  <version>${flex.version}</version>
  <type>swc</type>
  <scope>caching</scope>
</dependency>
<dependency>
  <groupId>com.adobe.flex.framework</groupId>
  <artifactId>framework</artifactId>
  <version>${flex.version}</version>
  <type>swc</type>
  <scope>caching</scope>
</dependency>
<dependency>
  <groupId>com.adobe.flex.framework</groupId>
  <artifactId>spark</artifactId>
  <version>${flex.version}</version>
  <type>swc</type>
  <scope>caching</scope>
</dependency>
<dependency>
  <groupId>com.adobe.flex.framework</groupId>
  <artifactId>sparkskins</artifactId>
  <version>${flex.version}</version>
  <type>swc</type>
  <scope>caching</scope>
</dependency>
<dependency>
  <groupId>com.adobe.flex.framework</groupId>
  <artifactId>mx</artifactId>
  <version>${flex.version}</version>
  <type>swc</type>
  <scope>caching</scope>
</dependency>
<dependency>
  <groupId>com.adobe.flex.framework</groupId>
  <artifactId>rpc</artifactId>
  <version>${flex.version}</version>
  <type>swc</type>
  <scope>caching</scope>
</dependency>
<dependency>
  <groupId>com.adobe.flex.framework</groupId>
  <artifactId>charts</artifactId>
  <version>${flex.version}</version>
  <type>swc</type>
  <scope>caching</scope>
</dependency>
<dependency>
  <groupId>com.adobe.flex.framework</groupId>
  <artifactId>advancedgrids</artifactId>
  <version>${flex.version}</version>
  <type>swc</type>
  <scope>caching</scope>
</dependency>
<dependency>
  <groupId>com.adobe.flex.framework</groupId>
  <artifactId>flex-framework</artifactId>
  <version>${flex.version}</version>
  <type>pom</type>
</dependency>

Or for an older SDK ("datavisualization" was split up into "charts" and "advancedgrids" in 18261)

<dependency>
    <groupId>com.adobe.flex.framework</groupId>
    <artifactId>textLayout</artifactId>
    <version>${flex.version}</version>
    <type>swc</type>
    <scope>caching</scope>
</dependency>
<dependency>
    <groupId>com.adobe.flex.framework</groupId>
    <artifactId>framework</artifactId>
    <version>${flex.version}</version>
    <type>swc</type>
    <scope>caching</scope>
</dependency>
<dependency>
    <groupId>com.adobe.flex.framework</groupId>
    <artifactId>spark</artifactId>
    <version>${flex.version}</version>
    <type>swc</type>
    <scope>caching</scope>
</dependency>
<dependency>
    <groupId>com.adobe.flex.framework</groupId>
    <artifactId>sparkskins</artifactId>
    <version>${flex.version}</version>
    <type>swc</type>
    <scope>caching</scope>
</dependency>
<dependency>
    <groupId>com.adobe.flex.framework</groupId>
    <artifactId>mx</artifactId>
    <version>${flex.version}</version>
    <type>swc</type>
    <scope>caching</scope>
</dependency>
<dependency>
    <groupId>com.adobe.flex.framework</groupId>
    <artifactId>rpc</artifactId>
    <version>${flex.version}</version>
    <type>swc</type>
 <scope>caching</scope>
</dependency>
<dependency>
    <groupId>com.adobe.flex.framework</groupId>
    <artifactId>datavisualization</artifactId>
    <version>${flex.version}</version>
    <type>swc</type>
    <scope>caching</scope>
</dependency>
<dependency>
    <groupId>com.adobe.flex.framework</groupId>
    <artifactId>flex-framework</artifactId>
    <version>${flex.version}</version>
    <type>pom</type>
</dependency>

(warning)  If you are getting "Error: #2032" messages, you probably setup your rslUrls wrong. Initially I let the urls start with "/" but this caused #2032 Errors in a different project when loading classes-swz. Investigating the error made me find out that the flashplayer allready installed the other swzs from loading another application and therefore I only got errors with charts (which I didn't use in the other application). In my case removing the leading "/" resolved the issue. So if you are encountering #2032 Errors check if the urls to your RSLs are correct and dont let yourself get confused that other libs seem to load correctly.

Versioning Problems with loading signed RSLs from Adobe Servers

There might be situations when you don't want to distribute the Flex rsls/swzs with your application. In my case, I debug my applications in the Standallone Debugger. This would fail to load the RSLs from "file:///rsl/framework-4.5.1.21328.swz". So the solution was to add publically available RSL urls.

The default will load signed rsls from:

"/{contextRoot}/rsl/{artifactId}-{version}.{extension}"

If however you want to make the Flash Player fetch the rsls from an Adobe server, you would use an rslUrl like this:

http://fpdownload.adobe.com/pub/{extension}/flex/${flex.version}/{artifactId}_{version}.{extension}

Unfortunately the "tlf" and "osmf" versions do not follow the usual versioning sheme. 

In the sdk versions deployed by velo all libs of the flex sdk share the version of the sdk. Unfortunately this is not true when it comes to "tlf" and "osmf". Usually this is no problem and your builds will work fine. All untill you want or have to use the official RSLs from the Adobe servers.

On the Adobe Servers there are no osmf_4.5.1.21328.swz or tlf_4.5.1.21328.swz.

In order to make it work, we have to trick the Flash Player by adding some fake RSL urls. But before you can do this you have to find out the correct version of those two libs.

I usually do this by downloading the official Flex SDK from Adobe (http://opensource.adobe.com/wiki/display/flexsdk/Flex+SDK) And then to have a look at the directory "frameworks/rsls". Here you can see allmost all libs share the same version, except those two ... so simply remember those versions. I usually define some Properties.

For Flex SDK 4.5.1.21328 i have the following:

?        <flex.version>4.5.1.21328</flex.version>
        <flex.tlf.version>2.0.0.232</flex.tlf.version>
        <flex.osmf.version>1.0.0.16316</flex.osmf.version>

... Currently working on this ...

Using the Apparat Framework to optimize

Using Apparat can dramatically reduce the size of your SWFs. Unfortunately this is a really, really CPU intense operation that will propably max out all cores you have available for quite some time. So It's a really good idea to do the other optimization first.

Flexmojos comes with a goal that allows to utilize the Appparat framework. This should be a pretty easy task, simply by adding the goal "optimize-swf" in your build, unfortunately I am getting exceptions from Aparat, which are making the Flexmojos Plugin go into aninfinite loop. But on the Flexmojos mailinglist someone posted a snippet utilizing the apparat-maven-plugin instead.

<properties>
        <flex.target.player.version>10.2</flex.target.player.version>
        <flex.version>4.5.0.20967</flex.version>
    </properties>

...

    <profiles>
        <profile>
            <id>release</id>

            <properties>
                <flex.debug>false</flex.debug>
            </properties>

            <build>
                <plugins>
                    <plugin>
                        <groupId>com.googlecode.apparat</groupId>
                        <artifactId>apparat-maven-plugin</artifactId>
                        <version>${apparat.version}</version>
                        <dependencies>
                            <dependency>
                                <groupId>com.adobe.flex</groupId>
                                <artifactId>compiler</artifactId>
                                <version>${flex.version}</version>
                                <type>pom</type>
                                <scope>compile</scope>
                            </dependency>
                            <dependency>
                                <groupId>com.adobe.flex.framework</groupId>
                                <artifactId>playerglobal</artifactId>
                                <version>${flex.version}</version>
                                <classifier>${flex.target.player.version}</classifier>
                                <type>swc</type>
                            </dependency>
                        </dependencies>
                        <executions>
                            <execution>
                                <id>reducer</id>
                                <goals>
                                    <goal>reducer</goal>
                                    <goal>tdsi</goal>
                                </goals>
                            </execution>
                        </executions>
                        <configuration>
                            <arguments>
                                <argument>-I</argument>
                            </arguments>
                            <mergeABC>false</mergeABC>
                            <sortCPool>false</sortCPool>
                            <!-- LZMA does not work yet -->
                            <lzma>false</lzma>
                            <matryoshkaType>preloader</matryoshkaType>
                            <mergeCF>false</mergeCF>
                        </configuration>
                    </plugin>
                </plugins>
            </build>
        </profile>
    </profiles>

Using the above configuration and activating the release profile makes the Apparat framework reduce the size of the generated SWFs by again 40-60%. I guess this is now a size nobody should be complaining again.