天天看點

flex moudle詳解

本文摘自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事件。

上一篇: 名句300
下一篇: amoy url

繼續閱讀