关于flex的module开发

本文摘自http://ieltsyangliu.spaces.live.com/blog/cns!5C02FE7927131D49!166.entry


前台的开发,目前RIA技术越来越具有优势,而其中的flex目前也出到了版本3.
之前采用flex开发展现层的时候,经常为程序越做越大而发愁,今天了解到了flex的module这块的功能,打算通过一两天时间学习和了解。之后进行我们之前项目的前台重构工作。
flex的Modules技术是可以被flex程序使用的一个swf文件,它不能脱离程序独立运行,但是多个程序之间可以共享它。flex的Modules技术将应用程序分割成小块、模块,主程序动态的加载所需要的模块。主程序在启动时并不全部加载所有的模块。当用户和模块没有交互的时候它不需要加载模块,同时它在模块不需要的时候可以卸载模块并释放内存和资源。

flex的Modules技术主要有如下的优点:
让swf文件初始下载尺寸更小
让加载时间更短
对应用程序更好的封装性。
一个Module是一个特殊的可以动态加载的包含IFlexModuleFactory类工厂的swf文件。它允许应用程序在运行时加载代码并在不需要主程序相关连类实现的情况下创建类的实例。

Module和RSLs(运行时共享库)很相似,他们独立分离代码将应用程序分成独立的加载的swf文件。但它比RSLs更加灵活,因为它可以在运行时动态加载/卸载并且不依赖于应用程序编译。

使用Modules的两个典型的应用场景是不同用户路径的大型应用程序和门户应用程序。 (分析略)

Modules实现了标准的类工厂接口。

通过使用共享的接口定义,减少了模块和shell之间的硬依赖(耦合)。提供了一种类型安全的沟通机制并在没有增加swf文件大小的情况下增加了一个抽象层。

下面图片展示了modules和shell之间接口的关联关系



ModuleManager 负责管理加载了的modules集合,将之对待为按照module URL为索引的单例map。加载module会触发一系列的事件来让客户端监控module的状态。module只加载一次,但是会重载并分发事件,因此客户端代码可以简单的依赖 READY 事件来获取module的类工厂来使用它。

ModuleLoader类在ModuleManager API之上最高级的API,它提供了一个基于module架构的简单实现,但是ModuleManager提供了module之上的更好的控制。

默认下,一个module会被加载进当前应用程序domain的子domain。你可以通过使用ModuleLoader的applicationDomain的属性来指定不同的应用程序domain。

因为module被加载到子domain中,因此它拥有的类将不属于主应用程序的domain中。举例:当一个module加载了PopUpManager类,那么在整个应用程序中它就变成了PopUpManager类的Owner,因为它注册到SingletonManager中。加入另外一个module晚些试图使用PopUpManager,Adobe ® Flash® Player 将会抛错。

解决方法是确保如PopUpManager、DragManager这些managers和一些共享服务都定义在主应用程序中(或者晚加载入应用程序domain中)。这样这些类可以被所有的module使用。典型的,都是通过将他们添加到脚本块中:

import mx.managers.PopUpManager;
import mx.managers.DragManager;
private var popUpManager:PopUpManager;
private var dragManager:DragManager;这项技术同时也应用到components,当module第一次使用component时,将在它自己的domain中拥有这些component的类定义。作为结果,如果别的module试图使用这些已经被另一个module使用的component,它的定义将会不能匹配到现存的定义中。因此,为了避免component的定义不匹配,在主应用程序中创建component的实例,让所有的module去引用。

因为module必须和应用程序再同一个security domain中,当你在AIR应用程序中使用任何的module SWF文件时,必须和主应用在相同的目录或者在其子目录中,确保他们在相同的security沙盒中。一个确保的方法是使用URL引用module位置不要包含“../”。

创建模块化的应用程序:

1 创建任意数量的modules。基于MXML文件的根节点是<mx:Module>。基于AS的扩展Module或者ModuleBase类。

2 编译module,方式如同应用程序的编译。可以使用基于命令行的mxmlc或者集成于flexbuilder中的编译器。

3 创建一个应用程序类。

4 在应用程序文件中使用<mx:ModuleLoader>来加载modules。或者你也可以使用mx.modules.ModuleLoader和mx.modules.ModuleManager类的方法来加载。

编译module
使用命令行举例:mxmlc MyModule.mxml
编译后生成一个swf文件,它不能独立运行也不能被浏览器执行,它只能被应用程序加载为module。它不能被Adobe® Flash® Player、Adobe® AIR,&8482; 、浏览器所直接运行。

当编译module后,你应当试图删除掉module和应用程序之间的那些冗余文件。你可以通过创建应用程序的link report来达到这个目的。当然了,flexbuilder可以自动的为你做这些。

减小module的大小

module的尺寸主要由module所使用的component和类来决定。默认情况下,module包含它所依赖的所有component的框架代码,这可能会导致其尺寸非常的大。

要想减小其尺寸,你可以通过指令让它导出应用程序class来优化它。只让module包含它所需要的类。

用命令行编译器来做的话,将在包含module的应用程序中产生一个linker report。在编译时采用load-externs选项。这个过程在flexbuilder中也需要。

用命令行编译器创建和使用linker report

1 产生linker report 和编译应用程序

mxmlc -link-report=report.xml MyApplication.mxml

默认的linker report输出位置和编译器输出问题相同,一般为bin目录。

2 编译module并传递linker report到load-externs选项mxmlc -load-externs=report.xml MyModule.mxml重新编译module如果module和应用程序在相同的工程下,那么当module改变时并不需要重新编译应用程序。这是因为应用程序是运行时加载module,而在编译期并不强制检查。同样当你改变应用程序时也不需要重新编译module。如果module和应用程序在不同的工程下,必须重新编译module。但是如果当改变有可能影响linker report或代码,你必须重新编译相应的应用程序和module。
  调试module 调试使用了module的应用程序,你必须设置调试编译器选项为true。否则你将不能在module中设置中断点或在他们中间收集调试信息。在flexbuilder中调试选项默认是开启的。在命令行,调试是默认关闭的。 一个很普遍的问题是如果一个module包含了其他module所要实用的类定义这种情况。在这种情况,别的module使用时会抛错,原因见前面。解决方法是把此类定义到应用程序domain中。
在不同的服务器中加载module在不同的服务器中加载module,必须在其彼此间建立信任。跨域访问应用程序加载应用程序时必须调用allowDomain()方法并且指定你需要加载的module所在的目标domain。因此在应用程序预初始话事件处理时指定目标域来确保程序在module加载前启动。在module所在的远程服务的cross-domain文件,增加一个entry来指定加载应用程序的服务位置。在你自己加载的应用程序中的preinitialize事件处理器中加载远程cross-domain文件。在被加载的module调用allowDomain方法来和调用者通讯。  下面的例子展示了如何在加载应用程序中的init方法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);}下面展示被加载模块的init()方法public function initMod():void {    Security.allowDomain("loaderservername");} 下面是cross-domain的写法:<!-- crossdomain.xml file located at the root of the server --><cross-domain-policy>    <allow-access-from domain="loaderservername" to-ports="*"/></cross-domain-policy> 预加载module 当你第一次开启应用程序来使用module,此应用程序将会比不使用module的应用程序尺寸更小。同时它也会减小等待时间。但同时,它将会在你访问以前非module的地方增加延迟。这是因为它不是预加载的。他们在第一次请求的时候被加载。 当module第一次被加载时,module的swf文件通过网络传输并储存到浏览器的缓存中。当它被卸载后又被加载时,等待时间会稍短,这是因为Flash Player将在客户端浏览器缓存中加载它,而非网络。 Module的swf文件和其他所有的swf文件一样,存在于客户端缓存一直到用户清空他们。因此,module可以被主应用程序跨不同的session访问,减少加载时间,但着也依赖于浏览器的缓存清空频率。你可以预加载module到内存即使当它目前并没有被用到的时候。使用IModuleInfo类的load方法在应用程序启动时可以预加载module。它将module加载到内存,但是并没有创建其实例。参考下面的例子:<?xml version="1.0"?>
<!-- modules/PreloadModulesApp.mxml -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" creationComplete="preloadModules()">
    <mx: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"
        }
        ]]>
    </mx:Script>

    <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="Bar Chart Module">
                <mx:Label id="l2" text="BarChartModule.swf"/>
                <mx:ModuleLoader url="BarChartModule.swf"/>
            </mx:VBox>
        </mx:TabNavigator>
    </mx:Panel>
</mx:Application> 使用ModuleLoader事件ModuleLoader类会触发很多事件,包括setup, ready, loading, unload, progress, error, 和urlChanged。你可以通过这些事件来记录程序加载过程,找寻合适module已经被卸载或者何时ModuleLoader目标url已经改变。 下面的例子使用定制的ModuleLoader组件,这个组件在主应用程序加载module时对每一个事件都做了一次报告:定制后的客户端程序<?xml version="1.0" encoding="iso-8859-1"?>
<!-- modules/CustomModuleLoader.mxml -->
<mx:ModuleLoader xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="*" creationComplete="init()">
  <mx:Script>
    <![CDATA[
    public function init():void {
        addEventListener("urlChanged", onUrlChanged);
        addEventListener("loading", onLoading);
        addEventListener("progress", onProgress);
        addEventListener("setup", onSetup);
        addEventListener("ready", onReady);
        addEventListener("error", onError);
        addEventListener("unload", onUnload);

        standin = panel;
        removeChild(standin);      
    }
  
    public function onUrlChanged(event:Event):void {
        if (url == null) {
            if (contains(standin))
                removeChild(standin);
        } else {
            if (!contains(standin))
                addChild(standin);
        }
        progress.indeterminate=true;
        unload.enabled=false;
        reload.enabled=false;
    }

    public function onLoading(event:Event):void {
        progress.label="Loading module " + url;
        if (!contains(standin))
            addChild(standin);

        progress.indeterminate=true;
        unload.enabled=false;
        reload.enabled=false;
    }
  
    public function onProgress(event:Event):void {
        progress.label="Loaded %1 of %2 bytes...";
        progress.indeterminate=false;
        unload.enabled=true;
        reload.enabled=false;
    }
  
    public function onSetup(event:Event):void {
        progress.label="Module " + url + " initialized!";
        progress.indeterminate=false;
        unload.enabled=true;
        reload.enabled=true;
    }
  
    public function onReady(event:Event):void {
        progress.label="Module " + url + " successfully loaded!";
        unload.enabled=true;
        reload.enabled=true;

        if (contains(standin))
            removeChild(standin);
    }
  
    public function onError(event:Event):void {
        progress.label="Error loading module " + url;
        unload.enabled=false;
        reload.enabled=true;
    }
  
    public function onUnload(event:Event):void {
        if (url == null) {
            if (contains(standin))
                removeChild(standin);
        } else {
            if (!contains(standin))
                addChild(standin);
        }
        progress.indeterminate=true;
        progress.label="Module " + url + " was unloaded!";
        unload.enabled=false;
        reload.enabled=true;
    }
  
    public var standin:DisplayObject;
    ]]>
  </mx:Script>

  <mx:Panel id="panel" width="100%">
    <mx:ProgressBar width="100%" id="progress" source="{this}"/>
    <mx:HBox width="100%">
      <mx:Button id="unload"
        label="Unload Module"
        click="unloadModule()"
      />
      <mx:Button id="reload"
        label="Reload Module"
        click="unloadModule();loadModule()"
      />
    </mx:HBox>
  </mx:Panel>
</mx:ModuleLoader>
主应用程序:<?xml version="1.0"?>
<!-- modules/EventApp.mxml -->
<mx:Application xmlns="*" xmlns:mx="http://www.adobe.com/2006/mxml">
    <mx:Script>
        <![CDATA[
            [Bindable]
            public var selectedItem:Object;
        ]]>
    </mx:Script>
    <mx:ComboBox
        width="215"
        labelField="label"
        close="selectedItem=ComboBox(event.target).selectedItem"
    >
        <mx:dataProvider>
            <mx:Object label="Select Coverage"/>      
            <mx:Object
                label="Life Insurance"
                module="insurancemodules/LifeInsurance.swf"
            />
            <mx:Object
                label="Auto Insurance"
                module="insurancemodules/AutoInsurance.swf"
            />        
            <mx:Object
                label="Home Insurance"
                module="insurancemodules/HomeInsurance.swf"
            />
        </mx:dataProvider>
    </mx:ComboBox>

    <mx:Panel width="100%" height="100%">
        <CustomModuleLoader id="mod"
            width="100%"
            url="{selectedItem.module}"
        />
    </mx:Panel>
    <mx:HBox>
        <mx:Button label="Unload" click="mod.unloadModule()"/>
        <mx:Button label="Nullify" click="mod.url = null"/>
    </mx:HBox>
</mx:Application>

另一个采用form的例子:<?xml version="1.0" encoding="utf-8"?>
<!-- modules/insurancemodules/AutoInsurance.mxml -->
<mx:Module xmlns:mx="http://www.adobe.com/2006/mxml"
    layout="absolute"
    backgroundColor="#ffffff"
    width="100%"
    height="100%"
>
    <mx:Label
        x="147"
        y="50"
        text="Auto Insurance"
        fontSize="28"
        fontFamily="Myriad Pro"
    />
    <mx:Form left="47" top="136">
        <mx:FormHeading label="Coverage"/>
        <mx:FormItem label="Latte Spillage">
            <mx:TextInput id="latte" width="200" />
        </mx:FormItem>
        <mx:FormItem label="Shopping Cart to the Door">
            <mx:TextInput id="cart" width="200" />
        </mx:FormItem>
        <mx:FormItem label="Irate Moose">
            <mx:TextInput id="moose" width="200" />
        </mx:FormItem>
        <mx:FormItem label="Color Fade">
            <mx:ColorPicker />
        </mx:FormItem>
    </mx:Form>
</mx:Module>


使用error事件
error事件用来处理异常。下面的例子:<?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>
使用progress事件
通过使用progress事件来追踪module加载状态。增加一个progress事件的监听器后,在module的加载时,flex会调用这个监听器。每次它被调用时,你可以查看这个事件的bytesLoaded属性来判断它的进度百分比属性。
下面的例子:<?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>

你也可以将module连接到一个ProgressBar控件。看下面的例子:<?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>


<?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>
这个例子并不会对所有的事件改变ProgressBar标签的属性,加入你加载了module按后再卸载它,label属性还是保持“LOADING 100%”。为了调整它的属性,你必须定义其他的ModuleLoader事件处理器,比如unload和error事件。

你可能感兴趣的:(应用服务器,浏览器,网络应用,Flex,AIR)