This section describes modules and how they are used by modular applications.
Subtopics
About modules
Benefits of modules
Module API details
Creating modular applications
About modules
Modules are SWF files that can be loaded and unloaded by an
application. They cannot be run independently of an application, but
any number of applications can share the modules.
Modules let you split your application into several pieces, or
modules. The main application, or shell, can dynamically load other
modules that it requires, when it needs them. It does not have to load
all modules when it starts, nor does it have to load any modules if the
user does not interact with them. When the application no longer needs
a module, it can unload the module to free up memory and resources.
Modular applications have the following benefits:
- Smaller initial download size of the SWF file.
- Shorter load time due to smaller SWF file size.
- Better
encapsulation of related aspects of an application. For example, a
"reporting" feature can be separated into a module that you can then
work on independently.
Benefits of modules
Modules are similar to Runtime Shared Libraries (RSLs) in that they
separate code from an application into separately loaded SWF files.
Modules are much more flexible than RSLs because modules can be loaded
and unloaded at run time and compiled without the application.
Two common scenarios in which using modules is beneficial are a
large application with different user paths and a portal application.
An example of the first common scenario is an enormous insurance
application that includes thousands of screens, for life insurance, car
insurance, health insurance, dental insurance, travel insurance, and
veterinary pet insurance.
Using a traditional approach to rich Internet application (RIA)
design, you might build a monolithic application with a hierarchical
tree of MXML classes. Memory use and start-up time for the application
would be significant, and the SWF file size would grow with each new
set of functionality.
When using this application, however, any user accesses only a
subset of the screens. By refactoring the screens into small groups of
modules that are loaded on demand, you can improve the perceived
performance of the main application and reduce the memory use. Also,
when the application is separated into modules, developers'
productivity may increase due to better encapsulation of design. When
rebuilding the application, the developers also have to recompile only
the single module instead of the entire application.
An example of the second common scenario is a system with a main
portal application, written in ActionScript 3, that provides services
for numerous portlets. Portlets are configured based on data that is
downloaded on a per-user basis. Using the traditional approach, you
might build an application that compiles in all known portlets. This is
inefficient, both for deployment and development.
By using modules, you can establish an interface that contains
portal services, and a generic portlet interface. You can use XML data
to determine which modules to load for a given session. When the module
is loaded, you obtain a handle to a class factory inside the module,
and from that you create an instance of a class that implements the
portlet interface. In this scenario, full recompilation is necessary
only if the interfaces change.
Module API details
Modules implement a class factory with a standard interface. The
product of that class factory implements an interface known to the
shell, or the shell implements an interface known to the modules.
By using shared interface definitions, these shared interfaces
reduce hard dependencies between the shell and the module. This
provides type-safe communication and enforces an abstraction layer
without adding significantly to the SWF file size.
The following image shows the relationship between the shell and the module's interfaces:
Creating modular applications
To create a modular application, you create separate classes for each module, plus an application that loads the modules.
To create a modular application:
- Create any number of modules. An MXML-based module file's root tag is
<mx:Module>
. ActionScript-based modules extend the ModuleBase class.
- Compile
each module as if it were an application. You can do this by using the
mxmlc command-line compiler or the compiler built into Adobe Flex
Builder.
- Create an Application class. This is typically an MXML file whose root tag is
<mx:Application>
, but it can also be an ActionScript-only application.
- In the Application file, use an
<mx:ModuleLoader>
tag to load each of the modules. You can also do this by using the load()
method of the mx.modules.ModuleLoader class. For classes that extend
ModuleBase, you should use the methods of the ModuleManager class to
load them.
The following sections describes these steps in detail.
Creating modules
Modules are classes just like application files. To create a module
in ActionScript, you create a file that extends the
mx.modules.ModuleBase class. To create a module in MXML, you extend the
mx.modules.Module class by creating a file whose root tag is <mx:Module>
.
In that tag, ensure that you add any namespaces that are used in that
module. You should also include a type declaration tag at the beginning
of the file.
The following example is a module that includes a Chart control:
<?xml version="1.0"?>
<!-- modules/ColumnChartModule.mxml -->
<mx:Module xmlns:mx="http://www.adobe.com/2006/mxml" width="100%" height="100%" >
<mx:Script><![CDATA[
import mx.collections.ArrayCollection;
[Bindable]
public var expenses:ArrayCollection = new ArrayCollection([
{Month:"Jan", Profit:2000, Expenses:1500},
{Month:"Feb", Profit:1000, Expenses:200},
{Month:"Mar", Profit:1500, Expenses:500}
]);
]]></mx:Script>
<mx:ColumnChart id="myChart" dataProvider="{expenses}">
<mx:horizontalAxis>
<mx:CategoryAxis
dataProvider="{expenses}"
categoryField="Month"
/>
</mx:horizontalAxis>
<mx:series>
<mx:ColumnSeries
xField="Month"
yField="Profit"
displayName="Profit"
/>
<mx:ColumnSeries
xField="Month"
yField="Expenses"
displayName="Expenses"
/>
</mx:series>
</mx:ColumnChart>
<mx:Legend dataProvider="{myChart}"/>
</mx:Module>
Compiling modules
You compile the module as you would compile any Flex application
using the mxmlc command-line compiler or the Flex Builder compiler. The
following command is the simplest mxmlc command:
The result is a SWF file that you load into your application
as a module. You cannot run the module-based SWF file as a stand-alone
Flash application or load it into a browser window. It must be loaded
by an application as a module.
Subtopics
Controlling module size
Recompiling modules
Controlling module size
Module size varies based on the components and classes that are used
in the module. By default, a module includes all framework code that
its components depend on, which can cause modules to be large by
linking classes that overlap with the application's classes.
To reduce the size of the modules, you can instruct the module to
externalize classes that are included by the application. This includes
custom classes and framework classes. The result is that the module
only includes the classes it requires, while the framework code and
other dependencies are included in the application.
To externalize framework classes, you generate a linker report from
the application that loads the modules, by using mxmlc commands. You
then use this report as input to the module's load-externs
compiler option.
To create a linker report:
- Generate the linker report:
mxmlc -link-report=report.xml MyApplication.mxml
- Compile the application SWF file:
- Compile the module:
mxmlc -load-externs=report.xml MyModule.mxml
Recompiling modules
You must recompile the modules if you make changes. Recompiling the
main application does not trigger a recompilation of the modules.
Similarly, if you change the application file, you do not have to
recompile the modules, unless you make changes that might affect the
linker report or common code.
MXML-based modules can load other modules. Those modules can load other modules, and so on.
Loading and unloading modules
To load and unload modules you use the load()
and unload()
methods of the ModuleLoader class. These methods take no parameters;
the ModuleLoader 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 -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
import mx.modules.*;
public function createModule(m:ModuleLoader, s:String):void {
if (!m.url) {
m.url = s;
return;
}
m.loadModule();
}
public function removeModule(m:ModuleLoader):void {
m.unloadModule();
}
]]>
</mx:Script>
<mx:Panel title="Module Example"
height="90%"
width="90%"
paddingTop="10"
paddingLeft="10"
paddingRight="10"
paddingBottom="10"
>
<mx:TabNavigator id="tn"
width="100%"
height="100%"
creationPolicy="auto"
>
<mx:VBox id="vb1" label="Column Chart Module">
<mx:Button
label="Load"
click="createModule(chartModuleLoader, l1.text)"
/>
<mx:Button
label="Unload"
click="removeModule(chartModuleLoader)"
/>
<mx:Label id="l1" text="ColumnChartModule.swf"/>
<mx:ModuleLoader id="chartModuleLoader"/>
</mx:VBox>
<mx:VBox id="vb2" label="Form Module">
<mx:Button
label="Load"
click="createModule(formModuleLoader, l2.text)"
/>
<mx:Button
label="Unload"
click="removeModule(formModuleLoader)"
/>
<mx:Label id="l2" text="FormModule.swf"/>
<mx:ModuleLoader id="formModuleLoader"/>
</mx:VBox>
</mx:TabNavigator>
</mx:Panel>
</mx:Application>
Setting the location of a ModuleLoader triggers a call to the loadModule()
method, too. This occurs when you first create a ModuleLoader with the url
property set. It also occurs if you change the value of that property.
The following example loads the modules without calling the loadModule()
method because the url
property is set on the <mx:ModuleLoader>
tags:
<?xml version="1.0"?>
<!-- modules/URLModuleLoaderApp.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Panel
title="Module Example"
height="90%"
width="90%"
paddingTop="10"
paddingLeft="10"
paddingRight="10"
paddingBottom="10"
>
<mx:Label width="100%" color="blue"
text="Select the tabs to change the panel."/>
<mx:TabNavigator id="tn"
width="100%"
height="100%"
creationPolicy="auto"
>
<mx:VBox id="vb1" label="Column Chart Module">
<mx:Label id="l1" text="ColumnChartModule.swf"/>
<mx:ModuleLoader url="ColumnChartModule.swf"/>
</mx:VBox>
<mx:VBox id="vb2" label="Form Module">
<mx:Label id="l2" text="FormModule.swf"/>
<mx:ModuleLoader url="FormModule.swf"/>
</mx:VBox>
</mx:TabNavigator>
</mx:Panel>
</mx: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.
Modules are loaded into the child of the current application domain.
You can specify a different application domain by using the applicationDomain
property of the ModuleLoader class.
When two classes of the same name but different implementations are loaded, the first one loaded is the one that is used.
Subtopics
Loading modules from different servers
Loading modules from different servers
To load a module from one server into an application running on a
different server, you must establish a trust between the module and the
application that loads it.
To allow access across domains:
- 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.
- 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.
- Load the cross-domain file on the remote server in the preinitialize event handler of your loading application.
- 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>
<allow-access-from domain="loaderservername" to-ports="*"/>
</cross-domain-policy>
For more information about using the cross-domain policy file, see Applying Flex Security in Building and Deploying Flex 2 Applications.
Using ModuleLoader events
The ModuleLoader class triggers several events, including setup
, ready
, loading
, unload
, progress
, error
, and urlChanged
.
You can use these events to track the progress of the loading process,
and find out when a module has been unloaded or when the ModuleLoader's
target URL has changed.
Subtopics
Using the error event
Using the progress event
Using the error event
The error
event gives you an opportunity to gracefully
fail when a module does not load for some reason. In the following
example, you can load and unload a module by using the Button controls.
To trigger an error
event, change the URL in the
TextInput control to a module that does not exist. The error handler
displays a message to the user and writes the error message to the
trace log.
Revision 1/10/2007: Minor change to createModule() method.
<?xml version="1.0"?>
<!-- modules/ErrorEventHandler.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
import mx.events.ModuleEvent;
import mx.modules.*;
import mx.controls.Alert;
private function errorHandler(e:ModuleEvent):void {
Alert.show("There was an error loading the module." +
" Please contact the Help Desk.");
trace(e.errorText);
}
public function createModule():void {
if (chartModuleLoader.url == ti1.text) {
// If they are the same, call loadModule.
chartModuleLoader.loadModule();
} else {
// If they are not the same, then change the url,
// which triggers a call to the loadModule() method.
chartModuleLoader.url = ti1.text;
}
}
public function removeModule():void {
chartModuleLoader.unloadModule();
}
]]>
</mx:Script>
<mx:Panel title="Module Example"
height="90%"
width="90%"
paddingTop="10"
paddingLeft="10"
paddingRight="10"
paddingBottom="10"
>
<mx:HBox>
<mx:Label text="URL:"/>
<mx:TextInput width="200" id="ti1" text="ColumnChartModule.swf"/>
<mx:Button label="Load" click="createModule()"/>
<mx:Button label="Unload" click="removeModule()"/>
</mx:HBox>
<mx:ModuleLoader id="chartModuleLoader" error="errorHandler(event)"/>
</mx:Panel>
</mx:Application>
Using the progress event
You can use the progress
event to track the progress of a module as it loads. When you add a listener for the progress
event, Flex calls that listener at regular intervals during the
module's loading process. Each time the listener is called, you can
look at the bytesLoaded
property of the event. You can compare this to the bytesTotal
property to get a percentage of completion.
The following example reports the level of completion during the
module's loading process. It also produces a simple progress bar that
shows users how close the loading is to being complete.
<?xml version="1.0"?>
<!-- modules/SimpleProgressEventHandler.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
import mx.events.ModuleEvent;
import flash.events.ProgressEvent;
import mx.modules.*;
[Bindable]
public var progBar:String = "";
[Bindable]
public var progMessage:String = "";
private function progressEventHandler(e:ProgressEvent):void {
progBar += ".";
progMessage =
"Module " +
Math.round((e.bytesLoaded/e.bytesTotal) * 100) +
"% loaded";
}
public function createModule():void {
chartModuleLoader.loadModule();
}
public function removeModule():void {
chartModuleLoader.unloadModule();
progBar = "";
progMessage = "";
}
]]>
</mx:Script>
<mx:Panel title="Module Example"
height="90%"
width="90%"
paddingTop="10"
paddingLeft="10"
paddingRight="10"
paddingBottom="10"
>
<mx:HBox>
<mx:Label id="l2" text="{progMessage}"/>
<mx:Label id="l1" text="{progBar}"/>
</mx:HBox>
<mx:Button label="Load" click="createModule()"/>
<mx:Button label="Unload" click="removeModule()"/>
<mx:ModuleLoader
id="chartModuleLoader"
url="ColumnChartModule.swf"
progress="progressEventHandler(event)"
/>
</mx:Panel>
</mx:Application>
You can also connect a module loader to a ProgressBar control.
The following example creates a custom component for the ModuleLoader
that includes a ProgressBar control. The ProgressBar control displays
the progress of the module loading.
<?xml version="1.0"?>
<!-- modules/MySimpleModuleLoader.mxml -->
<mx:ModuleLoader xmlns:mx="http://www.adobe.com/2006/mxml">
<mx:Script>
<![CDATA[
private function clickHandler():void {
if (!url) {
url="ColumnChartModule.swf";
}
loadModule();
}
]]>
</mx:Script>
<mx:ProgressBar
id="progress"
width="100%"
source="{this}"
/>
<mx:HBox width="100%">
<mx:Button
id="load"
label="Load"
click="clickHandler()"
/>
<mx:Button
id="unload"
label="Unload"
click="unloadModule()"
/>
<mx:Button
id="reload"
label="Reload"
click="unloadModule();loadModule();"
/>
</mx:HBox>
</mx:ModuleLoader>
You can use this module in a simple application, as the following example shows:
<?xml version="1.0"?>
<!-- modules/ComplexProgressEventHandler.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns:local="*">
<mx:Panel title="Module Example"
height="90%"
width="90%"
paddingTop="10"
paddingLeft="10"
paddingRight="10"
paddingBottom="10"
>
<mx:Label text="Use the buttons below to load and unload
the module."/>
<local:MySimpleModuleLoader id="customLoader"/>
</mx:Panel>
</mx:Application>
This example does not change the ProgressBar's label
property for all events. For example, if you load and then unload the module, the label
property remains at "LOADING 100%". To adjust the label properly, you
must define other event handlers for the ModuleLoader events, such as unload
and error
.