Loading and unloading modules

There are several techniques you can use to load and unload modules in your applications. These techniques include:

When you’re using modules in an AIR application, the module SWF file must be located in the same directory as the main application SWF file or one of its subdirectories.

Using the ModuleLoader class to load modules

You can use the ModuleLoaderclass to load a module in an application or other module. The easiest way to do this in an MXML application is to use the <s:ModuleLoader> tag. You set the value of the url property to point to the location of the module’s SWF file. The following example loads the module when the application first starts:

<?xml version="1.0"?>
<!-- modules/MySimplestModuleLoader.mxml -->
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    xmlns:mx="library://ns.adobe.com/flex/mx">
    
    <s:ModuleLoader url="ColumnChartModule.swf"/>

</s:Application>

You can change the timing of when the module loads by setting the value of the url property at some other time, such as in response to an event. Setting the target URL of a ModuleLoader object triggers a call to the loadModule() method. This occurs when you first create a ModuleLoader object with the url property set. It also occurs if you change the value of that property.

If you set the value of the url property to an empty string ("") or null, the ModuleLoader object unloads the current module by calling the release() method.

You can have multiple instances of the ModuleLoader class in a single application. The following example loads the modules when the user navigates to the appropriate tabs in the TabNavigator container:

<?xml version="1.0"?>
<!-- modules/URLModuleLoaderApp.mxml -->
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    xmlns:mx="library://ns.adobe.com/flex/mx">

    <s:Panel title="Module Example" width="100%" height="100%">
        <s:layout> 
            <s:VerticalLayout/> 
        </s:layout>
    
        <mx:TabNavigator id="tn"          
            paddingTop="10" 
            paddingLeft="10" 
            paddingRight="10" 
            paddingBottom="10"
            width="100%" height="100%"
            creationPolicy="auto">
            <s:ModuleLoader id="ml1" 
                label="ColumnChart Module" 
                url="ColumnChartModule.swf"/>
            <s:ModuleLoader id="ml2" 
                label="BarChart Module" 
                url="BarChartModule.swf"/>
        </mx:TabNavigator>
    </s:Panel>
</s:Application>

You can also use the ModuleLoader API to load and unload modules with the loadModule() and unloadModule() methods. These methods take no parameters; the ModuleLoader class loads or unloads the module that matches the value of the current url property.

The following example loads and unloads the module when you click the button:

<?xml version="1.0"?>
<!-- modules/ASModuleLoaderApp.mxml -->
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    xmlns:mx="library://ns.adobe.com/flex/mx">

    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout>

    <fx:Script>
        <![CDATA[
        import spark.modules.ModuleLoader;

        public function createModule(m:ModuleLoader, s:String):void {
            if (!m.url) {
                m.url = s;
            }
            m.loadModule();
        }
        
        public function removeModule(m:ModuleLoader):void {
            m.unloadModule();
        }
        ]]>
    </fx:Script>

    <s:Panel title="Module Example" width="100%" height="100%">
        <mx:TabNavigator id="tn"          
            paddingTop="10" 
            paddingLeft="10" 
            paddingRight="10" 
            paddingBottom="10"
            width="100%" height="100%"
            creationPolicy="auto">
            <s:NavigatorContent label="ColumnChartModule">
                <s:layout>
                    <s:VerticalLayout paddingTop="10" paddingLeft="5"/>
                </s:layout>
                <s:Button label="Load" 
                    click="createModule(chartModuleLoader, l1.text)"/>
                <s:Button label="Unload" 
                    click="removeModule(chartModuleLoader)"/>
                <s:Label id="l1" text="ColumnChartModule.swf"/>
                <s:ModuleLoader id="chartModuleLoader"/>                                
            </s:NavigatorContent>

            <s:NavigatorContent label="FormModule">
                <s:layout>
                    <s:VerticalLayout paddingTop="10" paddingLeft="5"/>
                </s:layout>
                <s:Button label="Load" 
                    click="createModule(formModuleLoader, l2.text)"/>
                <s:Button label="Unload" 
                    click="removeModule(formModuleLoader)"/>
                <s:Label id="l2" text="FormModule.swf"/>
                <s:ModuleLoader id="formModuleLoader"/>
            </s:NavigatorContent>
        </mx:TabNavigator>
    </s:Panel>
</s:Application>

When you load a module, Flex ensures that there is only one copy of a module loaded, no matter how many times you call the load() method for that module.

Using the ModuleManager class to load modules

You can use the ModuleManagerclass to load the module. This technique is less abstract than using the <s:ModuleLoader> tag, but it does provide you with greater control over how and when the module is loaded.

To use the ModuleManager to load a module in ActionScript:
  1. Get a reference to the module’s IModuleInfo interface by using the ModuleManager getModule() method.

  2. Call the interface’s load() method.

    The application that loads the module should pass in its moduleFactory property. This lets the module know who its parent style manager is. When using the load() method, you can specify the application’s moduleFactory with the fourth parameter, as the following example shows:
    info.load(null, null, null, moduleFactory);
  3. Use the factory property of the interface to call the create() method and cast the return value as the module’s class. If you are adding the module to a container, you can cast the return value as an IVisualElement (for Spark containers) or a DisplayObject (for MX containers) so that they can be added to the display list.

The following example shell application loads the ColumnChartModule.swf file. The example then adds the modules to the display list so that it appears when the application starts:

<?xml version="1.0"?>
<!-- modules/ModuleLoaderApp.mxml -->
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    xmlns:mx="library://ns.adobe.com/flex/mx"
    creationComplete="initApp()">

    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout>

    <fx:Script>
        <![CDATA[
        import mx.events.ModuleEvent;
        import mx.modules.ModuleManager;
        import mx.modules.IModuleInfo;        
        import mx.core.IVisualElement;

        public var info:IModuleInfo;
        
        private function initApp():void {
            info = ModuleManager.getModule("ColumnChartModule.swf");
            info.addEventListener(ModuleEvent.READY, modEventHandler);           

            /* Load the module into memory. Calling load() makes the
               IFlexModuleFactory available. You can then get an
               instance of the class using the factory's create()
               method. */
            info.load(null, null, null, moduleFactory);
        }

        /* Add an instance of the module's class to the display list. */        
        private function modEventHandler(e:ModuleEvent):void {
            /* For MX containers, cast to a DisplayObject. */
            vb1.addChild(info.factory.create() as DisplayObject);

            /* For Spark containers, cast to a UIComponent. */
            vg1.addElement(info.factory.create() as IVisualElement);
        }
        ]]>
    </fx:Script>

    <!-- MX container -->
    <mx:VBox id="vb1">
        <s:Label text="Module loaded in MX VBox container:"/>
    </mx:VBox>

    <!-- Spark container -->
    <s:VGroup id="vg1">
        <s:Label text="Module loaded in Spark VGroup container:"/>    
    </s:VGroup>

</s:Application>

The IModuleInfo class’s load() method also optionally takes an ApplicationDomain and a SecurityDomain as arguments. If you do not specify either of these (or set them to null), then the module is loaded into a new child domain.

MXML-based modules can load other modules. Those modules can load other modules, and so on.

Be sure to define the module instance outside of a function, so that it is not in the function’s local scope. Otherwise, the object might be garbage collected and the associated event listeners might never be invoked.

If you remove all references to the module, it will be garbage collected. You do not need to call the unload() method when adding and removing modules using the IModuleInfo class. You just need to set the IModuleInfo instance to null.

Loading modules from different servers

To load a module from one server into an application running on a different server, you must establish trust between the module and the application that loads it.

Access applications across domains

  1. In your loading application, you must call the allowDomain() method and specify the target domain from which you load a module. So, specify the target domain in the preinitialize event handler of your application to ensure that the application is set up before the module is loaded.

  2. In the cross-domain file of the remote server where your module is, add an entry that specifies the server on which the loading application is running.

  3. Load the cross-domain file on the remote server in the preinitialize event handler of your loading application.

  4. In the loaded module, call the allowDomain() method so that it can communicate with the loader.

The following example shows the init() method of the loading application:

public function setup():void { 
    Security.allowDomain("remoteservername"); 
    Security.loadPolicyFile("http://remoteservername/crossdomain.xml"); 
    var request:URLRequest = new URLRequest("http://remoteservername/crossdomain.xml"); 
    var loader:URLLoader = new URLLoader(); 
    loader.load(request); 
}

The following example shows the loaded module’s init() method:

public function initMod():void { 
    Security.allowDomain("loaderservername"); 
}

The following example shows the cross-domain file that resides on the remote server:

<!-- crossdomain.xml file located at the root of the server --> 
<cross-domain-policy> 
    <site-control permitted-cross-domain-policies="all"/> 
    <allow-access-from domain="loaderservername" to-ports="*"/> 
</cross-domain-policy>

For more information about using the cross-domain policy file, see Security.

Preloading modules

When you first start an application that uses modules, the application’s file size should be smaller than a similar application that does not use modules. As a result, there should be a reduction in wait time because the application can be loaded into memory and run before the modules’ SWF files are even transferred across the network. However, there will be a delay when the user navigates to a part in the application that uses the module. This is because the modules are not by default preloaded, but rather loaded when they are first requested.

When a module is loaded by the application for the first time, the module’s SWF file is transferred across the network and stored in the browser’s cache. If the application unloads that module, but then later reloads it, there should be less wait time because Flash Player loads the module from the cache rather than across the network.

Module SWF files, like all SWF files, reside in the browser’s cache unless and until a user clears them. As a result, modules can be loaded by the main application across several sessions, reducing load time; but this depends on how frequently the browser’s cache is flushed.

You can preload modules at any time so that you can have the modules’ SWF files in memory even if the module is not currently being used.

To preload modules on application startup, use the IModuleInfo class load() method. This loads the module into memory but does not create an instance of the module.

The following example loads the BarChartModule.swf module when the application starts up, even though it will not be displayed until the user navigates to the second pane of the TabNavigator container. Without preloading, the user would wait for the SWF file to be transferred across the network when they navigated to the second pane of the TabNavigator.

<?xml version="1.0"?>
<!-- modules/PreloadModulesApp.mxml -->
<s:Application 
    xmlns:fx="http://ns.adobe.com/mxml/2009" 
    xmlns:s="library://ns.adobe.com/flex/spark" 
    xmlns:mx="library://ns.adobe.com/flex/mx"
    creationComplete="preloadModules()">

    <s:layout> 
        <s:VerticalLayout/> 
    </s:layout>
    
    <fx:Script>
        <![CDATA[
        import mx.events.ModuleEvent;
        import mx.modules.ModuleManager;
        import mx.modules.IModuleInfo;
        
        private function preloadModules():void {
            /* Get a reference to the module's interface. */
            var info:IModuleInfo = 
                ModuleManager.getModule("BarChartModule.swf");
            info.addEventListener(ModuleEvent.READY, modEventHandler);
            
            /* Load the module into memory. The module will be
               displayed when the user navigates to the second
               tab of the TabNavigator. */
            info.load();
        }
        
        private function modEventHandler(e:ModuleEvent):void {
            trace("module event: " + e.type); // "ready"
        }
        ]]>
    </fx:Script>

    <s:Panel title="Module Example" width="100%" height="100%">
        <mx:TabNavigator id="tn"          
            paddingTop="10" 
            paddingLeft="10" 
            paddingRight="10" 
            paddingBottom="10"
            width="100%" height="100%"
            creationPolicy="auto">
            <s:ModuleLoader label="ColumnChartModule" 
                url="ColumnChartModule.swf"/>
            <s:ModuleLoader label="BarChartModule" 
                url="BarChartModule.swf"/>
        </mx:TabNavigator>
    </s:Panel>
</s:Application>