Welcome!

Adobe Flex Authors: Liz McMillan, RealWire News Distribution, Maureen O'Gara, Yakov Fain, Keith Swenson

Related Topics: Adobe Flex

Adobe Flex: Article

Optimize Flex Applications By Breaking Them Into Modules

Load modules asynchronously for better user experience

Adobe Flex has gotten the Web2.0 development by storm and we see lot of Web2.0 Flex-based implementations these days. There are a lot of complex enterprise applications being developed in Flex. Many of these applications are getting huge in size and often take a lot of time to download in the client browser to run.

With time when these applications get more complex due to additional feature incorporation or user tries to run these applications over relatively slower connection the initial wait time when the application is downloaded will be getting worse. So, if proper attention is not given to the application size and loading time, it might be quite an issue to handle afterward and adversely impact the user experience.

In this article I will explain a method to gain runtime size optimization by breaking a Flex application in a dependant set of modules that may be loaded asynchronously in the virtual machine for optimum application size and better user experience.

Moreover, in any enterprise flex application it’s very important to consider the ease of development looking at the difficulty of implementing new functionalities. But breaking the application down in several small modules for optimum loading may pose difficulties in debugging and developing in the development mode. So, I will also explain how to separate development from deployment and maintain both development and deployment efficiency. In this approach I will explain how to develop a Flex application first with all required features and then how to break the application in modules for optimization purposes.

A simple use-case to explain the concept


Let’s consider a simple use-case of a user log-in. Generally in most of Flex web applications the full application is downloaded and then it shows the log-in screen to enter username and password to proceed. On successful submission of log-in credentials the application takes the user to next state where the user can do more with the application, but a wrong combination notifies the user about it and just stays in the log-in screen until the user type in the correct ones.

So, if the application is big in size, it might take a reasonably long time to download the full application and then to show the log-in screen. But, with some little modification, there might be marked improvement in this application if the log-in screen is just downloaded first, so that the user can see the log-in screen without delay. As soon as the log-in screen is loaded, asynchronous loading of next module or modules will start.

In this approach while the user is taking time to type in the username and password the system is intelligently making use of that time to download next dependant modules. If a user is too fast in entering the correct log-in credentials the system will wait till the next dependent modules are loaded to go to the logged-in state. So, for very quick users this approach might seem to put a response delay but still will reduce overall time taken to execute the application by judiciously using the time for user input to asynchronously download application as modules to increase overall user experience.


In the above figure, the initial download time is shown in red and the other activities are shown in different colors for both classical and asynchronous downloading cases. In asynchronous downloading case there is a marked improvement in the startup time of application and also shows the judicious usage of time taken by users to download stuff asynchronously. So, ideally if a user takes some time to sequentially perform some activities in the new approach those times can be consumed to load application is the background without any visible user experience degradation. Moreover, some application parts might be marked as optional so that if not used those parts might not even be loaded until needed.

This approach will also be beneficial to run very large applications by unloading not-dependent modules at any time to free-up memory in virtual machine, so that very large applications too can run with reasonably good efficiency which in the classical one swf system will crash due to memory constrains.

Concept of Dependent modules

Flex modules allow to break an application into smaller parts. Flex also comes with module loaders to make loading and unloading modules very easy and quite transparent to the application developer. Any application rather than having one executable swf file containing the full application, is separated in one main application swf and several modules being loaded by the main application.

The modules are also made dependent on each other so that one module being downloaded automatically starts downloading the subsequent ones and this process continues till all the relevant modules are downloaded. There might be also a set of optional modules which are being downloaded only when there is a requirement for those modules. These modules are downloaded on request and also being off-loaded from the VM after the application is done using them. This approach thus maintains optimum load time and also reduces memory footprint for a flex application.

As elaborated earlier this concept makes more sense for production build and hence can be made to work on development builds to optimize them for production usage.

Only the framework to load dependent modules contains the main application (swf), so it’s pretty light weight and instantaneously being loaded after flex is loaded diminishing initial load time.

The application then works sequentially to load the first module or modules to show the basic application UI to get user started. When the UI screen gets initialized for user to work on; at once the next dependent module starts downloading asynchronously in the background. So, as the user takes time to complete the required input in one screen, several other screens might be downloaded and available for usage, but if the user is too fast then the UI displays notification of when the next screen will be available.

In dependent modules, there is always a set of modules to be loaded on initialization complete of one and there are exit conditions which need to be met after user input submission and before loading the next view.

Figure-2 shows a loading process of a sample application. It consists of a main application (swf) and views like log-in screen and others located in individual modules. The application swf is downloaded first and as soon as it’s initialized it starts downloading the Module-1. Once the Module-1 is downloaded and initialized it starts downloading Module-2 asynchronously. After downloading and initialization, Module-2 starts downloading two modules Module-3 and Module-4 asynchronously. The downloading matrix is also shown in Figure-2.



Separating the application into modules and loading them asynchronously also poses additional exit conditions to check while loading screens belonging to another module. So, in the above example if we consider an action in Module-2 which needs a screen to load in Module-3, the exit condition will be dependent on successful downloading of Module-3 and any other application-specific conditions. But if any action in Module-2 needs components in both Module-3 and Module-4, the exit condition will consist of both modules to be successfully downloaded with additional application dependant conditions.


More on Module Loading and exit conditions

If an application is broken into different modules and loaded synchronously during the system initialization or on demand from application code then additional exit conditions to check if the module is already downloaded is not required. But in asynchronous module loading each and every action which asks to load a view in a different module must add exit conditions to check for successful download of relevant modules.

Let’s consider the case of log-in module. In the application in Figure-3 the log-in screen loads the Module-1 and Module-2 asynchronously on initialization. So, once the user enters the correct username and password and clicks the “Submit” button, in a classical single swf case, the system used to check the credentials with the backend server, and if correct, used to load the next view.

But breaking the next screen in Module-1 and Module-2 now adds two new exit conditions that the Module-1 and Module-2 has been successfully downloaded before the next view is loaded even after successful credential check from the server side. The button click and all the actions which take the system to load stuff from any module being asynchronously loaded should put an additional condition to check for successful download of the corresponding module inside which the screen resides.

With more and more modules are asynchronously downloaded and any action depending on them there will be more and more exit conditions to satisfy before going to the next step. These exit conditions checks might also clutter the code and make stuff really unclear. To make checking exit conditions easy, a helper action script class AsyncProcManager.as may be used as shown in the code section at the end of this article.


Ideal Separation between Development and Deployment

Today, Flex Builder is the IDE of choice to develop flex based applications. It provides the rich support to develop and debug flex based applications. In the development mode the full application can be developed thinking it to be just a classical single swf one. Once the application is fully functional and ready for deployment in production mode, the applications may be divided in several dependant modules depending on the application logic.

To do the breakup of application into modules first the individual mxml components are taken into account and decision should be made which ones can be compiled as modules. To separate any component from the main application as modules depends upon the ease of referencing them from the main one as a flex common type or an interface so that the actual implementation can be bundled in the module.

For synchronous downloading of modules ModuleLoader is the best choice, but for asynchronous downloading of modules there are few more things to consider. The component downloaded as module for asynchronous loading can not be initialized instantly on download but should wait for the application to get initialized. So, the modules for asynchronous loading wrap up the component inside them referenced through an interface and will be explained more in the next section.

Once these modules are identified, then asynchronous loading of modules can be figured out with respect to use case flow and program logic. Depending on user input and process flow in the application, you need to create a map of dependent modules. All the actions like button click, combo box selection, which can trigger loading of anything from the asynchronously loaded dependent modules should be noted down, and the glue code to check for module downloading should be attached there. Once these steps are done, the modules can be compiled and the application will act the same way it used to perform in single swf application case.

More about implementation of Dependent Modules

Because the modules need to be asynchronously loaded, the actual UI component is wrapped in the module. This helps to download the module without going through the initialization of the UI component wrapped inside until the application finds it necessary to go through the initialization. The sample code for Module_Component1 is given below:

<?xml version="1.0" encoding="utf-8"?>
<mx:Module xmlns:mx="http://www.adobe.com/2006/mxml" implements="com.idr.utils.IApplicationModule">
<mx:Script>
<![CDATA[
import com.idr.Component1;
import mx.core.IUIComponent;
public function getComponent():IUIComponent{
return main;
}

public function getInitFunction():Function{
return main.onInit;
}

public var main:com.idr.Component1 = new com.idr.Component1();
]]>
</mx:Script>
</mx:Module>



In the above code snippet, it shows how to wrap a UI component com.idr.Component1 in a module. As the module is wrapping up an UI component and there will be hooks from the application code to download and initialize the component at a later time, the module is implementing an interface com.idr.utils.IApplicationModule, which provides the common functions for the wrapper.

package com.idr.utils
{
import mx.core.IUIComponent;

public interface IApplicationModule
{
function getComponent():IUIComponent;
function getInitFunction():Function;
}
}



This interface is being used by application and the custom module downloader to unwrap the UI component. For clarity let’s have a look into two main functions of the module downloader below:

public function ModuleDownLoader(srcURL:String,onDownLoad:Function):void{
sourceURL = srcURL;
onDownloadCompele = onDownLoad;
moduleInfo = ModuleManager.getModule(sourceURL);
downLoadModule();
}

public var onReady:Function = function( e:ModuleEvent):void{
var uiC:IApplicationModule = e.module.factory.create() as IApplicationModule;
var nP:IUIComponent = uiC.getComponent();
onDownloadCompele(nP);
}

The first function is the constructor where the module downloader is being initialized. In the onReady function the downloaded module is got as an IApplicationModule interface and the component reference is being passed to the application provided onDownloadComplete function as an argument.

Until now we have seen how to generate a module for the dependant module approach. Now we will take a look in the internals of what need to be changed in the caller to integrate the downloaded module. The caller may be the application or another module. In the caller component there few steps to hook in the dependant modules as shown below with example code from MainApplication.


1. Add asynchronous process managers for every separate action which can initiate to loa d UI from the dependant modules to the caller component as class scope variables


public var loginExitMgr:AsyncProcManager = new
AsyncProcManager(onExitForSucessfulLogin);
public var newUserExitMgr:AsyncProcManager = new AsyncProcManager(onExitForNewUser);

2. In the initialization or pre-initialization of caller (in this case MainApplication), hook up the calls to download the dependant modules after registering them in the asynchronous process manager. Also register functions dependent for each action to the respective process managers.


public function onInit():void{
modDownloaderC1=new ModuleDownLoader("Module_Component1.swf",
loginExitMgr.registerFunc(onComponent1_DownLoad));
loginExitMgr.registerFunc(onSucessFulLogin);

modDownloaderC2=new ModuleDownLoader("Module_Component2.swf",
newUserExitMgr.registerFunc(onComponent2_DownLoad));
newUserExitMgr.registerFunc(registerNewUser);
}


3. Write onModuleDownload functions for each and every dependant module download and declare the components inside the modules as DisplayObject as class scope variables as shown below:

[Bindable]public var viewCredComp:DisplayObject;
[Bindable]public var editCredComp:DisplayObject;

public function onComponent1_DownLoad(o:Object):void{
viewCredComp = o as DisplayObject;
}

public function onComponent2_DownLoad(o:Object):void{
editCredComp = o as DisplayObject;
}


4. All the action which loads the dependent module UI needs to register the function in the asynchronous process manager.

public function submitCredentials():void{
userName = uName.text;
password = uPass.text;
// now ideally it will call a authentication routine in server
// for this example we short circuit
runLogin( loginExitMgr.registerFunc(onSucessFulLogin));
}
...
..

<mx:Button label="New User Register Here..." id="newUserRegister" click="newUserExitMgr.registerFunc(registerNewUser)(null)"/>

When all the above steps are done the caller and the dependent modules will be all integrated and work the same way as if there is just one application swf.

The full source code for the example and the flex builder project is given at the end of this article.

Flex Builder Enhancement Suggestions


In the present scenario, the developer makes a decision on how to implement dependent modules, and all the code to break an application into modules should be programmed manually. But, ideally Flex Builder should be able to do the same transparently to the application developer.

For optimization it can run a wizard where the developer will suggest which potential components need to be turned into dependent modules to the caller component. Flex Builder of the future should automatically generate the code to break the system into a set of dependant modules. This exact task can be performed with ant scripts and there is an implementation using ant scripts now available, which I will explain in my next article.

Conclusion

Size optimization of Flex applications are required. The dependent modules approach just fills up the basic gap of maintaining the same code base for both development and deployment but provides a way to optimize both for better efficiency. It also provides the same flexibility as in a Web1.0 page flip system with all the goodies needed for a Web2.0 project development. With some help being provided in the Flex API and Flex compiler this approach can really take Flex in a “Flexi Way” to its next milestone in Web2.0 development.


Relevant Source Code

AsyncExitFuncWrapper.as
package com.idr.utils
{
public class AsyncExitFuncWrapper
{
public var _manager:AsyncProcManager;
public var _exitFunc:Function;
private var _executionCompleted:Boolean=false;

public function get executionCompleted():Boolean{
return _executionCompleted;
}

public function set executionCompleted(val:Boolean):void{
_executionCompleted = val;
}


public function AsyncExitFuncWrapper(manager:AsyncProcManager,exitFunc:Function)
{
_manager = manager;
_exitFunc = exitFunc;
}

public function executeOnExit(obj:*):void{
_exitFunc(obj);
executionCompleted = true;
_manager.executeEndFunction();
}

}
}

AsyncProcManager.as
package com.idr.utils
{
import mx.collections.ArrayCollection;

public class AsyncProcManager
{
public var _finalExitFunction:Function;
public var _exitFuncWrappers:ArrayCollection = new ArrayCollection();

public function AsyncProcManager(finalExitFunction:Function):void
{
_finalExitFunction = finalExitFunction;
}

public function registerFunc(func:Function):Function{
var exitFuncWrapper:AsyncExitFuncWrapper= getAddedFunc(func);
if( exitFuncWrapper == null){
exitFuncWrapper = new AsyncExitFuncWrapper(this,func);
addExitFunctionWrapper(exitFuncWrapper);
}
return exitFuncWrapper.executeOnExit;
}

private function addExitFunctionWrapper(val:AsyncExitFuncWrapper):void{
if( getAddedFunc(val._exitFunc) == null){
_exitFuncWrappers.addItem(val);
}
}

public function getAddedFunc(func:Function):AsyncExitFuncWrapper{
for(var i:int=0;i<_exitFuncWrappers.length;i++){
if( (_exitFuncWrappers[i] as AsyncExitFuncWrapper)._exitFunc == func ){
return _exitFuncWrappers[i] as AsyncExitFuncWrapper;
}
}
return null;
}

public function executeEndFunction():void{
var canExec:Boolean = true;
for( var i:int=0;i<_exitFuncWrappers.length;i++){
if((_exitFuncWrappers[i] as AsyncExitFuncWrapper).executionCompleted== false){
canExec = false;
break;
}
}
if( canExec ){
_finalExitFunction();
}
}

}
}

MainApplication.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" initialize="onInit()">
<mx:Script>
<![CDATA[
import com.idr.utils.ModuleDownLoader;
import mx.modules.Module;
import com.idr.utils.AsyncProcManager;

[Bindable]public var viewCredComp:DisplayObject;
[Bindable]public var editCredComp:DisplayObject;

[Bindable]public var userName:String="";
[Bindable]public var password:String="";

public var loginExitMgr:AsyncProcManager = new AsyncProcManager(onExitForSucessfulLogin);
public var newUserExitMgr:AsyncProcManager = new AsyncProcManager(onExitForNewUser);

public var modDownloaderC1:ModuleDownLoader;
public var modDownloaderC2:ModuleDownLoader;

public function onInit():void{
modDownloaderC1=new ModuleDownLoader("Module_Component1.swf",
loginExitMgr.registerFunc(onComponent1_DownLoad));
loginExitMgr.registerFunc(onSucessFulLogin);

modDownloaderC2=new ModuleDownLoader("Module_Component2.swf",
newUserExitMgr.registerFunc(onComponent2_DownLoad));
newUserExitMgr.registerFunc(registerNewUser);
}

public function onComponent1_DownLoad(o:Object):void{
viewCredComp = o as DisplayObject;
}

public function onComponent2_DownLoad(o:Object):void{
editCredComp = o as DisplayObject;
}

public function submitCredentials():void{
userName = uName.text;
password = uPass.text;
// now ideally it will call a authentication routine in server
// for this example we short circuit
runLogin( loginExitMgr.registerFunc(onSucessFulLogin));
}

public function runLogin(onSuccess:Function):void{
if( userName.indexOf(password)!= -1){
onSuccess(null);
}
}

public function onSucessFulLogin(o:Object):void{
// after sucessfull creds check
}

public function onExitForSucessfulLogin():void{
// change the UI to component in module
setCurrentState("showCredentialState");
}

public function registerNewUser(o:Object):void{
// do some check
trace("register called");
}

public function onExitForNewUser():void{
// change the UI to component in module
setCurrentState("editCredentialState");
}
]]>
</mx:Script>
<mx:states>
<mx:State name="showCredentialState">
<mx:RemoveChild target="{label1}"/>
<mx:RemoveChild target="{uPass}"/>
<mx:RemoveChild target="{label2}"/>
<mx:RemoveChild target="{uLogin}"/>
<mx:RemoveChild target="{newUserRegister}"/>
<mx:RemoveChild target="{hbox1}"/>
<mx:RemoveChild target="{hbox2}"/>
<mx:RemoveChild target="{hbox3}"/>
<mx:AddChild target="{viewCredComp}"/>
</mx:State>
<mx:State name="editCredentialState">
<mx:RemoveChild target="{label1}"/>
<mx:RemoveChild target="{label2}"/>
<mx:RemoveChild target="{uPass}"/>
<mx:RemoveChild target="{uLogin}"/>
<mx:RemoveChild target="{newUserRegister}"/>
<mx:RemoveChild target="{hbox1}"/>
<mx:RemoveChild target="{hbox2}"/>
<mx:RemoveChild target="{hbox3}"/>
<mx:AddChild target="{editCredComp}"/>
</mx:State>
</mx:states>

<mx:HBox width="100%" id="hbox2">
<mx:Label id="label1" text="User Name:" width="72"/>
<mx:TextInput id="uName" text="{userName}"/>
</mx:HBox>
<mx:HBox width="100%" id="hbox3">
<mx:Label id="label2" text="Password:" width="72"/>
<mx:TextInput id="uPass" displayAsPassword="true" text="{password}"/>
</mx:HBox>
<mx:HBox id="hbox1" width="100%">
<mx:Button label="Login" id="uLogin" click="submitCredentials()" />
</mx:HBox>

<mx:Button label="New User Register Here..." id="newUserRegister" click="newUserExitMgr.registerFunc(registerNewUser)(null)"/>

</mx:Application>

Component1.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml" width="400" height="300" initialize="onInit()">
<mx:Script>
<![CDATA[
import mx.core.Application;
[Bindable]public var app:MainApplication;

public function onInit():void{
app= Application.application as MainApplication;
}

public function editCredentials():void{
app.setCurrentState("editCredentialState");
}
]]>
</mx:Script>
<mx:HBox width="100%">
<mx:Label text="User Name:" width="72"/>
<mx:Label id="uNameDisplay" text="{app.userName}"/>
</mx:HBox>
<mx:HBox width="100%">
<mx:Label text="Password:" width="72"/>
<mx:Label id="uPassDisplay" text="{app.password}"/>
</mx:HBox>

<mx:Button label="Edit" id="uEditButton" click="editCredentials()"/>

</mx:VBox>

More Stories By Indroniel Deb Roy

Indroniel Deb Roy works as an UI Architect for BlueCoat Systems.He has more than 10 years of development experience in the fields of J2EE and Web Application development. In his past he worked in developing web applications for Oracle, Novell, Packeteer, Knova etc. He has a passion for innovation and works with various Web2.0 & J2EE technologies and recently started on Smart Phone & IPhone Development.

Comments (2) View Comments

Share your thoughts on this story.

Add your comment
You must be signed in to add a comment. Sign-in | Register

In accordance with our Comment Policy, we encourage comments that are on topic, relevant and to-the-point. We will remove comments that include profanity, personal attacks, racial slurs, threats of violence, or other inappropriate material that violates our Terms and Conditions, and will block users who make repeated violations. We ask all readers to expect diversity of opinion and to treat one another with dignity and respect.


Most Recent Comments
indronieldebroy 03/24/09 06:36:00 PM EDT

There are various ways to achieve inter-module communication. The easiest ways are
1) Through interfaces and callback functions
2) Through messaging

Lord1984 03/16/09 11:29:48 AM EDT

Hi,

very good article...

i was interisting in your previous article about a new way to look at portal.

can i ask you some question?

When you talk about portlet window you mean that portlet window is a mx panel and it contains a module, is it true?
how to achieve direct module to module communication?

Where download the source?

thanks in advance

Regards
Lord