【轉載】關於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事件。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章