»
S
I
D
E
B
A
R
«
Setting ASDoc description for packages
Jun 26th, 2009 by Gaurav

So you have a lot of packages and you want to add description for all your package for ASDoc? You can use the -packages.package parameter but it can be tedious to repeat the ASDoc command line parameter (-packages.package) for each individual package.

Now you don’t have to repeat the parameter because you can now use the new parameter (-package-description-file) instead.

Using the new parameter (-package-description-file) you can now specify a xml file that contains the description for all your packages.

The format of the file is as follows:

<overviews>
	<packages>
		<package name="package.name1">
			<shortDescription></shortDescription>
			<description></description>
		</package>
	</packages>
</overviews>

Here is a sample file (pkgDescription.xml)

<overviews>
	<packages>
		<package name="mx.core">
			<shortDescription>
			<![CDATA[
				The mx.core package contains the
				base classes and interfaces,
				such as UIComponent, used by Flex.
			]]>
			</shortDescription>
			<description>
			<![CDATA[
				The mx.core package contains the
				base classes and interfaces,
				such as UIComponent, used by Flex.
			]]>
			</description>
		</package>
		<package name="mx.controls">
			<shortDescription>
			<![CDATA[
				The mx.controls package contains
				the Flex user-interface controls.
			]]>
			</shortDescription>
			<description>
			<![CDATA[
				The mx.controls package contains
				the Flex user-interface controls.
			]]>
			</description>
		</package>
	</packages>
</overviews>

Here is how you can use it:


./asdoc -package-description-file=pkgDescription.xml
-doc-classes=mx.controls.Button
-source-path=<sdk root>/frameworks/projects/framework/src

This parameter is available in Flex SDK 4 only. And the nightly build of Flex SDK 4 can be found at http://opensource.adobe.com/wiki/display/flexsdk/Download+Flex+4 (choose a build after 4.0.0.8245 to use this parameter)

ASDoc integration with build scripts
Jun 25th, 2009 by Gaurav

If you work in a team and use Flex, then you definitely use code written by other developers on your team. Wouldn’t it be nice to see the asdoc for their code the same way you see asdoc for the Flex SDK? Well now you no longer have to manually run asdoc, you can now leverage the <asdoc> ant task which is part of the Flex SDK.

The <asdoc> is available in the Flex 4 SDK only (not available in Flex 3). Nightly builds for the Flex 4 sdk can be downloaded from http://opensource.adobe.com/wiki/display/flexsdk/Download+Flex+4

Here is an sample asdoc target that can be used in ant build file.

<target name="asdocgen">
		<!-- for svn users the flexTasks.jar is
			under <sdk root>/lib -->
		<available property="flexTasksJar"
			value="${sdk.dir}/lib/flexTasks.jar"
			file="${sdk.dir}/lib/flexTasks.jar"/>

		<!-- for sdk package (zip) the flexTasks.jar is
			under <sdk root>/ant/lib -->
		<available property="flexTasksJar"
			value="${sdk.dir}/ant/lib/flexTasks.jar"
			file="${sdk.dir}/ant/lib/flexTasks.jar"/>

	    <property name="FLEX_HOME" value="${sdk.dir}" />	

		<asdoc output="${output.folder}/asdoc-output"
			main-title="My asdoc main title"
			footer="<u>My custom footer.</u>"
			window-title="Custom asdoc documentation"
			left-frameset-width="300"
			failonerror="true" fork="true">

			<packages.package name="com.test"
				description="description for com.test" />

			<!-- top level class to include in asdoc.
				These should be in the source-path (src1, src2) -->
			<doc-classes class="com.test.CustomMain"/>
			<doc-classes class="com.test.Foo"/>

			<!-- generate asdoc for all as/mxml files in the doc-sources -->
			<doc-sources path-element="${basedir}/src3"/>

			<!-- don't try to generate asdoc for
				includeFile.as (its an include file
				and can't be independently compiled -->
			<exclude-sources path-element="${basedir}/src3/includeFile.as"/>

			<!-- source path for asdoc -->
			<compiler.source-path path-element="${basedir}/src1"/>
			<compiler.source-path path-element="${basedir}/src2"/>

			<!-- namespaces to include in asdoc -->
			<doc-namespaces uri="http://www.gauravj.com/blog/"/>

			<!-- namespace declaration for asdoc -->
			<namespace uri="http://www.gauravj.com/blog/"
			manifest="${basedir}/manifest.xml"/>

			<jvmarg line="-Xmx512m"/>
		</asdoc>
	</target>

As you can see it supports all the options of command line asdoc (doc-classes, doc-sources, doc-namespaces, source-path, etc.). So you can now write ant targets for your asdoc generation.

If you face any issues or have any feedback, you can log bugs at http://bugs.adobe.com/flex

Creating smaller swfs (Part II)
Jun 23rd, 2009 by Gaurav

Here is the “Part II” of creating smaller swfs. In case you haven’t checked out the “Part I”, it can be found here: Creating smaller swfs (Part I). In this post, let’s focus on modules and how they can help in reducing the swf size.

What are Modules?

Modules are Flex swf files that can be dynamically loaded by other swf files. So they allow you to break your large application into smaller pieces and gives you the flexibility to load the the required piece when/if required. In case you are wondering “How is Module different than RSL (since RSL can also be a swf and can be loaded by your application)”, the difference is that RSLs can only be loaded in frame 1 (i.e before your application initializes) but Modules can be loaded and unloaded whenever you want (after your application initializes) through out the life cycle of your application. Also note that you can not run modules independently, they always need to be loaded by another application. Benefits of modules are much more than just helping you to make your swf smaller, some of the benefits are:

  • Improve start up time by reducing the swf size.
  • Improves development time because modules allows for code separation and can be independently compiled. So you can break you app into different functional pieces and have different developers/teams work on the different pieces as Modules.
  • Improves build time because you need not recompile your entire application if changes are made to an independent module.
  • Improves integration where you can’t upgrade all your code to the most recent version of Flex.

How to write Modules?

You can write a module either using ActionScript or MXML. If your module is going to use any part of the Flex framework code then you must write a module based on mx.modules.Module. In case your module is not going to use any part of the Flex framework code then you can write a module based on mx.modules.ModuleBase but then you must write it using ActionScript.

Here is an example of a module written using MXML (DataGridModule.mxml):

<?xml version="1.0" encoding="utf-8"?>
<mx:Module xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical"
		   width="400" height="300" creationComplete="module1_creationCompleteHandler(event)">
	<mx:Script>
		<![CDATA[
			import mx.controls.Alert;
			import mx.rpc.events.FaultEvent;
			import mx.events.FlexEvent;

			protected function module1_creationCompleteHandler(event:FlexEvent):void
			{
				srv.send();
			}

			protected function srv_faultHandler(event:FaultEvent):void
			{
				Alert.show("Can not retrieve data");
			}
		]]>
	</mx:Script>

	<mx:HTTPService id="srv" url="catalog.xml" fault="srv_faultHandler(event)" />

	<mx:DataGrid dataProvider="{srv.lastResult.report.account}" width="100%" height="100%">
		<mx:columns>
			<mx:DataGridColumn dataField="month" headerText="Month"/>
			<mx:DataGridColumn dataField="sales" headerText="Sales" textAlign="right"/>
			<mx:DataGridColumn dataField="expenses" headerText="Expenses" textAlign="right"/>
		</mx:columns>
	</mx:DataGrid>
</mx:Module>

As you can see the only thing special about the above code is the wrapping <mx:Module> tag, otherwise it looks pretty similar to a custom component (You can simply replace <mx:Module>, with say <mx:VBox> or <mx:HBox> and use it as a custom component)

The following is an example of a Module using ActionScript (ActionModule.as) which extends mx.modules.ModuleBase:

package
{
	import mx.modules.ModuleBase;

	public class ActionModule extends ModuleBase
	{
		public function ActionModule()
		{
			super();
		}

		public function calculateAverageProfit(arr:Array): String
		{
			var profit:Number = 0;

			for(var counter:int = 0; counter < arr.length; counter++)
			{
				var obj:Object = arr[counter];
				profit += obj.sales - obj.expenses;
			}

			return new Number(profit/arr.length).toFixed(2);
		}
	}
}

Since the above module (ActionModule.as) is for calculation purpose only, it doesn't depend upon any framework code and hence can extend from mx.modules.ModuleBase instead of mx.modules.Module

How to use Modules?

So now you probably have some idea about writing the modules, once you write the Modules, there are two techniques to load them into your application.

  1. Use ModuleLoader
  2. Use ModuleManager and IModuleInfo

Following is an example of an app which loads Module(s) (using both the techniques):

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical"
creationComplete="srv.send();">
	<mx:Script>
		<![CDATA[
			import mx.modules.IModuleInfo;
			import mx.modules.ModuleManager;
			import mx.events.ModuleEvent;
			import mx.collections.ArrayCollection;
			import mx.rpc.events.ResultEvent;
			import mx.controls.Alert;
			import mx.rpc.events.FaultEvent;
			import mx.events.FlexEvent;

			private var result:ArrayCollection;

			private var moduleInfo:IModuleInfo;

			private var obj:Object;

			protected function srv_faultHandler(event:FaultEvent):void
			{
				Alert.show("Can not retrieve data");
			}
			protected function modLoader_errorHandler(event:ModuleEvent):void
			{
				Alert.show("Error in loading the Module.");
			}

			protected function srv_resultHandler(event:ResultEvent):void
			{
				result = event.result.report.account;
				linechart.dataProvider = result;
			}

			protected function showDataGrid_clickHandler(event:MouseEvent):void
			{
				modLoader.url = "DataGridModule.swf";
			}

			protected function showProfit_clickHandler(event:MouseEvent):void
			{
				moduleInfo = ModuleManager.getModule("ActionModule.swf");
				moduleInfo.addEventListener(ModuleEvent.READY, readyHandler);
				moduleInfo.addEventListener(ModuleEvent.ERROR, modLoader_errorHandler);
				moduleInfo.load();
			}

			private function readyHandler(event:ModuleEvent):void
			{
				obj = moduleInfo.factory.create();
				profitLbl.text = "Average Profit: $" + obj.calculateAverageProfit(result.source)
                                                 + " (in millions)";
			}
		]]>
	</mx:Script>
	<mx:HTTPService id="srv" url="catalog.xml" fault="srv_faultHandler(event)"
                         result="srv_resultHandler(event)"/>

	<mx:LineChart id="linechart" color="0x323232" height="50%" showDataTips="true" >
		<mx:horizontalAxis>
			<mx:CategoryAxis categoryField="month"/>
		</mx:horizontalAxis>
		<mx:series>
			<mx:LineSeries yField="sales" form="curve" displayName="Sales"/>
			<mx:LineSeries yField="expenses" form="curve" displayName="Expenses"/>
		</mx:series>
	</mx:LineChart>
	<mx:Legend dataProvider="{linechart}" color="0x323232"/>
	<mx:Label id="profitLbl" />
	<mx:Button id="showProfit" click="showProfit_clickHandler(event)"
                 label="Show Average Profit"/>
	<mx:Button id="showDataGrid" click="showDataGrid_clickHandler(event)"
                 label="Show Details"/>

	<mx:ModuleLoader id="modLoader" error="modLoader_errorHandler(event)" />
</mx:Application>

In the above example the DataGridModule.swf is loaded using <mx:ModuleManager/>, the load is invoked when the url property is set (modLoder.url ="DataGridModule.swf"), which fires the urlChange event. Also you can invoke the load operation using the loadModule() method of ModuleLoader. You can unload a module using the unloadModule() method.

The ActionModule.swf is loaded using ModuleManager and IModuleInfo, ModuleManager is used to get a reference to the IModuleInfo for ActionModule.swf and the actual load is invoked using the load() method on IModuleInfo.

Notice that both techniques allow you to add a error handler (see modLoader_errorHandler() which takes event of type ModuleEvent), a error handler can be helpful to debug issue with module loading. Also there are other event types that you can use to find info related to loading (ModuleEvent.PROGRESS, ModuleEvent.READY, ModuleEvent.SETUP)

Here is the running swf. When you press on the button to "Show Average Profit", it loads the ActionModule.swf (which calculates the average profit). When you press on "Show Details", it loads the data grid module which shows the data in a datagrid.



Now for the size benefits:

Configuration Swf size Comments
Without Modules and RSLs 484 KB  
With Modules 415 KB In this case the benefit is small because we moved only a small portion of code into the module. Reduction in swf size depends upon the amount of code you move into modules. Expect higher benefits for large applications.
With Modules and RSLs 159 KB This app was compiled with Flex 4 beta build. With a more recent Flex 4 build there should be increased benefits for swf size reduction.

Optimizing Modules

So far you have seen how to use modules and how they can help to reduce the swf size. But when you compile a module swf, lot of core classes of the Flex framework (which are dependencies of mx.modules.Module) also get stitched into the module swf file, this creates a situation where you duplicate the same classes into your application (once in the main app and then in each module which is loaded by the app). However there is a compiler option to avoid this situation. You can compile you main app with the -link-report option, it will give you the link-report (a report that has the information regarding the byte code linkage) for your application. Then you can compile your module(s) with the -load-externs option and you can point the load-externs to the generated link report from the compilation of the main app. Its like telling the compiler that you already have taken care of classes which are in the link report, so don't add those to the module swf file.

Here are the example command I used:

# compile main app - this will generate the main app swf and a link report named myLinkReport.xml
./mxmlc -link-report=myLinkReport.xml Dashboard.mxml

# compile module
./mxmlc -load-externs=myLinkReport.xml DataGridModule.mxml

Or you can also use Flex/Flash Builder to set up the module optimization. You can do this via the project properties. Here are the steps:

  1. Right Click on the project and go to properties
  2. Choose Flex Modules

  3. You can see the modules in this dialog and whether they are being optimized or not. You can add new modules using the "Add" button.

  4. Edit setting using the edit button or double click on the module

  5. Using this dialog, you can choose whether to optimize(and for which application) or not

The only situation where you should not optimize your modules is when you want to use your modules in different applications. Because optimized modules can only be used with application for which they were optimized.

Shared Code problem

When modules are loaded, the classes that are contained in the module are added into the child domain of the current application domain. So classes that are part of the module are owned by the module and not be the application. This can create an issue when two modules are using the same classes. When the first module has loaded, its class definitions gets registered, and module loaded after wards finds itself in a situation where the class definition that it contains doesn't matches with what is already registered. This usually creates issue with the manager classes like HistoryManager, PopUpManager etc. The error usually is a "Type Coercion" error, for example:

Type Coercion failed: cannot convert mx.managers::PopUpManagerImpl@ to mx.managers.IPopUpManager

The solution is to make these classes part of the main app (so the main app is the owner of these class definitions and both managers can share them). If you simply add the import and variable declaration to the script block of the main app, you can avoid the issue with the classes that cause shared code problem

import mx.managers.HistoryManager;
private var managerObj: HistoryManager;

Alternatively you can also refactor code into a shared module and load the shared module before loading any other module.

Modules and RSLs

Using both modules are RSLs can significantly reduce swf size and start-up time. But if your app is using RSL (say for framework.swc) then your module swf does not need to use framework.swc as RSL. In such cases the library that will be loaded as RSL by the main app should just be in the external-library-path of the module.

This way since the RSL will already be loaded by the main app and available for consumption, for the module, the module will further save on load time because it won't even try to load the RSL (btw since the RSL is already loaded by the main app, the module will get it quickly get it from the cache so the saving per module is small but can add up if you are using lots of modules)

There is a bug (see FB-15470) related to this in Flex Builder 3. The fix for the bug will be available in Flash Builder 4

So if you are using FB 3, here is how you can do this manually:
You should move the modules into a seperate Flex Builder project and not list the libraries as rsls (instead just as -external-library-path for the libraries). That way the modules won't try to reload the rsls (because they would have already been loaded by the main app (for the project containing the main app you can continue to have the libraries listed as rsls))

»  Substance: WordPress   »  Style: Ahren Ahimsa