當程式越來越大,我們需要把它拆分成多個swf,在需要的時候動态加載。拆分時應該盡量把不同的類編譯進唯一的swf,避免因swf檔案增多而使整個程式的檔案尺寸增大。按此原則可以拆分出以下兩種swf,借助 ApplicationDomain 共享其代碼和資源。
-
子產品(Module)
按照程式邏輯,可以拆分出多個“功能子產品”,如“注冊”、“管理”等等;按照遊戲或社群類程式的關卡或場景,可以拆分出不同的“場景子產品”。這些子產品不是主程式運作必須的,隻在需要的時候加載。
-
運作時共享庫(RSL)
主場景或者多個子產品通用的資源,比如位圖、聲音、設計好的頁面元素等,可作為“庫”在主程式運作前加載。可以整套更換的皮膚(skin)隻需先加載一套。
ApplicationDomain 是存放AS3定義(包括類、方法、接口等)的容器。使用Loader類加載swf時可以通過指定 ApplicationDomain 參數将swf加載到不同的域(Domain):
var loader : Loader = new Loader () ;
var context : LoaderContext = new LoaderContext () ;
context . applicationDomain = new ApplicationDomain ( ApplicationDomain . currentDomain ) ;
context . applicationDomain = ApplicationDomain . currentDomain ;
context . applicationDomain = new ApplicationDomain () ;
loader . load ( new URLRequest ( " loaded.swf " ) , context ) ;
ApplicationDomain使用類似于顯示清單(DisplayList)的樹形結構。 相對于舞台(Stage) ,可以認為 ApplicationDomain 最根部的是系統域(system domain),包含 Flash Player 核心類定義。主程式所在的域(以下簡稱主域)就是它唯一的子域,類似于Stage下的文檔類(Document Class)。
一個fla文檔類裡代碼:
this . stage . addChild ( mySprite ) ;
this . addChild ( myMC ) ;
this . addChild ( myShape ) ;
運作後的顯示清單:

ApplicationDomain 的類似結構:
-
加載到子域(子產品)
類似于“繼承”,子域可以直接獲得父域所有的類定義,反之父域得不到子域的。和繼承關系不同的是,如果子域中有和父域同名的類,子域定義會被忽略而使用父域的定義。
-
加載到同域(運作時共享庫)
類似集合裡的合并關系。被加載swf裡的所有類定義被合并到目前域中可以直接使用。和加載到子域相同,和目前域同名的定義也會被忽略。
-
加載到新域(獨立運作的程式或子產品)
swf載入指定域之前,先要檢查該域及其父域中是否存在同名類,重複定義一概忽略。如果加載别人寫的程式,或者使用舊版本的主程式加載新版本的子產品,為避免類名沖突就要加載到新域獨立運作以使用自己的類。
子產品加載到同域不是一樣可以嗎?為何要加載到子域呢?好處就在于,解除安裝一個加載到子域的子產品時,隻要確定清除所有到該子產品的引用,子產品的所有類定義将被垃圾回收(Garbage Collection)。
有兩種方式可以通路 ApplicationDomain :
-
ApplicationDomain.currentDomain
currentDomain是ApplicationDomain的靜态變量,表示目前代碼 所 在的域。該變量很奇特,在主程式裡指向主域,在加載到子域的子產品裡則指向該子產品所在的子域。雖然 ApplicationDomain 有個 parentDomain 屬性,但子域已經自動獲得了父域的類定義,是以通過 ApplicationDomain.currentDomain 就可以擷取父域定義了——包括主程式和加載到主域的共享庫。(注:系統域不可直接通路,主域和所有新域即系統域子域的parentDomain屬性為 null)
-
LoaderInfo類的applicationDomain屬性
此方式可以通路任何方式加載的swf的 ApplicationDomain。對于主程式來說,加載到同域的庫定義已經存在于 ApplicationDomain.currentDomain ,而子產品的類主程式一般用不到。是以這種方式個人不推薦使用。
ApplicationDomain 的 hasDefinition() 方法判斷某定義是否存在,getDefinition() 方法擷取指定的定義。下面以一個 例子 來介紹 ApplicationDomain 的具體用法和應用程式的拆分。
本例 有四個swf,shell.swf是主程式,lib.swf是共享庫,login.swf和result.swf分别是“登入”和“結果”子產品,所有的視 圖元件都在共享庫中。實際開發時可能有很多庫,比如“位圖庫”、“音效庫”、“模型通用庫”等。“通用庫”裡存放多個子產品共用的資源,比如此例中的背景元 素。而各個子產品獨有的資源還是放在各自的swf中。
主程式首先将共享庫加載到同域,完成後将“登入子產品”加載到子域。主程式可以像操作普通的視覺對象(DisplayObject)一樣操作加載的模 塊:監聽事件、調用方法。因為編譯器不會識别未定義的類,為使用強類型,建議為主類和模型定義相應的接口,使用少量的重複代碼協助程式設計。
private function showModule ( p_module : IModule ) : void
{
if ( this . m_moduleList [ 0 ] == " login.swf " )
{
p_module . show ( this ) ;
p_module . addEventListener ( " login " , this . onLogin ) ;
} else
{
p_module . show ( this , this . m_userName ) ;
}
}
子產品“繼承”了主程式和共享庫的所有類和資源,可以通過 ApplicationDomain.currentDomain.getDefinition() 來擷取相應的類。注意擷取不存在的類會抛出一個 ReferenceError。
protected function getClass ( p_name : String ) : Class
{
try
{
return ApplicationDomain . currentDomain . getDefinition ( p_name ) as Class ;
} catch ( p_e : ReferenceError )
{
trace ( " 定義 " + p_name + " 不存在 " ) ;
return null ;
}
return null ;
}
登入子產品擷取庫中的界面元素,并在點選按鈕後抛出事件。Event類不允許帶參數,必須使用繼承Event的自定義事件抛出參數。主程式可以把 子產品的自定義事件也編譯進去(這樣就增大了整個程式的檔案尺寸),或者讓監聽子產品事件的函數接受一個Objcet參數,以擷取其動态屬性。
private function onLogin ( p_e : Object ) : void
{
this . m_userName = p_e . userName ;
var login : IModule = p_e . currentTarget ;
login . removeEventListener ( " login " , this . onLogin ) ;
login . dispose () ;
this . loadSwf () ;
}
主程式收到事件之後解除安裝注冊子產品,加載“結果子產品”到子域,并将登入子產品傳出的”userName”參數傳給結果子產品。
public function show ( p_parent : DisplayObjectContainer , ... rest ) : void
{
var libClass : Class = this . getClass ( " net.eidiot.appDomainDemo.Libaray " ) ;
if ( libClass != null ) this . initUi ( libClass , rest ) ;
}
override protected function initUi ( p_libClass : Class , p_rest : Array = null ) : void
{
this . addUi ( this . getClass ( p_libClass . BG_NAME ) , " 結果 " ) ;
var resultFunc : Function = p_libClass . getResult ;
var userName : String = p_rest [ 0 ] ;
this . addChild ( resultFunc ( userName )) ;
}
注意initUi()方法分别使用了共享庫中Libaray類的靜态屬性BG_NAME和靜态方法getResult()。但是直接調用此靜态方法會報錯,可以先用 resultFunc 變量取出此方法。詳細内容請參考 源代碼 。
執行個體效果示範
下載下傳源檔案 (rar 278K)