當程序愈來愈大,咱們須要把它拆分紅多個swf,在須要的時候動態加載。拆分時應該儘可能把不一樣的類編譯進惟一的swf,避免因swf文件增多而使整個程序的文件尺寸增大。按此原則能夠拆分出如下兩種swf,藉助 ApplicationDomain 共享其代碼和資源。shell
- 模塊(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文檔類裏代碼:app
this
.
stage
.
addChild
(
mySprite
)
;
this
.
addChild
(
myMC
)
;
this
.
addChild
(
myShape
)
;
運行後的顯示列表:
ApplicationDomain 的相似結構:
less
- 加載到子域(模塊)
相似於「繼承」,子域能夠直接得到父域全部的類定義,反之父域得不到子域的。和繼承關係不一樣的是,若是子域中有和父域同名的類,子域定義會被忽略而使用父域的定義。
- 加載到同域(運行時共享庫)
相似集合裏的合併關係。被加載swf裏的全部類定義被合併到當前域中能夠直接使用。和加載到子域相同,和當前域同名的定義也會被忽略。
- 加載到新域(獨立運行的程序或模塊)
swf載入指定域以前,先要檢查該域及其父域中是否存在同名類,重複定義一律忽略。若是加載別人寫的程序,或者使用舊版本的主程序加載新版本的模塊,爲避免類名衝突就要加載到新域獨立運行以使用本身的類。
模塊加載到同域不是同樣能夠嗎?爲什麼要加載到子域呢?好處就在於,卸載一個加載到子域的模塊時,只要確保清除全部到該模塊的引用,模塊的全部類定義將被垃圾回收(Garbage Collection)。
有兩種方式能夠訪問 ApplicationDomain :dom
- 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)同樣操做加載的模塊:監聽事件、調用方法。由於編譯器不會識別未定義的類,爲使用強類型,建議爲主類和模型定義相應的接口,使用少許的重複代碼協助編程。ide
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參數,以獲取其動態屬性。this
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」參數傳給結果模塊。spa
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
))
;
}