- 模块(Module)
按照程序逻辑,可以拆分出多个“功能模块”,如“注册”、“管理”等等;按照游戏或社区类程序的关卡或场景,可以拆分出不同的“场景模块”。这些模块不是主程序运行必须的,只在需要的时候加载。 - 运行时共享库(RSL)
主场景或者多个模块通用的资源,比如位图、声音、设计好的页面元素等,可作为“库”在主程序运行前加载。可以整套更换的皮肤(skin)只需先加载一套。
ApplicationDomain是存放AS3定义(包括类、方法、接口等)的容器。使用Loader类加载swf时可以通过指定ApplicationDomain参数将swf加载到不同的域(Domain):
varloader:Loader=newLoader();
varcontext:LoaderContext=newLoaderContext();
/*加载到子域(模块)*/
context.applicationDomain=newApplicationDomain(ApplicationDomain.currentDomain);
/*加载到同域(共享库)*/
context.applicationDomain=ApplicationDomain.currentDomain;
/*加载到新域(独立运行的程序或模块)*/
context.applicationDomain=newApplicationDomain();
loader.load(newURLRequest("loaded.swf"),context);
ApplicationDomain使用类似于显示列表(DisplayList)的树形结构。相对于舞台(Stage),可以认为ApplicationDomain最根部的是系统域(systemdomain),包含FlashPlayer核心类定义。主程序所在的域(以下简称主域)就是它唯一的子域,类似于Stage下的文档类(DocumentClass)。
一个fla文档类里代码:
this.stage.addChild(mySprite);
this.addChild(myMC);
this.addChild(myShape);
运行后的显示列表:
ApplicationDomain的类似结构:
- 加载到子域(模块)
类似于“继承”,子域可以直接获得父域所有的类定义,反之父域得不到子域的。和继承关系不同的是,如果子域中有和父域同名的类,子域定义会被忽略而使用父域的定义。 - 加载到同域(运行时共享库)
类似集合里的合并关系。被加载swf里的所有类定义被合并到当前域中可以直接使用。和加载到子域相同,和当前域同名的定义也会被忽略。 - 加载到新域(独立运行的程序或模块)
swf载入指定域之前,先要检查该域及其父域中是否存在同名类,重复定义一概忽略。如果加载别人写的程序,或者使用旧版本的主程序加载新版本的模块,为避免类名冲突就要加载到新域独立运行以使用自己的类。
模块加载到同域不是一样可以吗?为何要加载到子域呢?好处就在于,卸载一个加载到子域的模块时,只要确保清除所有到该模块的引用,模块的所有类定义将被垃圾回收(GarbageCollection)。
有两种方式可以访问ApplicationDomain:
- ApplicationDomain.currentDomain
currentDomain是ApplicationDomain的静态变量,表示当前代码所在的域。该变量很奇特,在主程序里指向主域,在加载到子域的模块里则指向该模块所在的子域。虽然ApplicationDomain有个parentDomain属性,但子域已经自动获得了父域的类定义,所以通过ApplicationDomain.currentDomain就可以获取父域定义了——包括主程序和加载到主域的共享库。(注:系统域不可直接访问,主域和所有新域即系统域子域的parentDomain属性为null) - LoaderInfo类的applicationDomain属性
此方式可以访问任何方式加载的swf的ApplicationDomain。对于主程序来说,加载到同域的库定义已经存在于ApplicationDomain.currentDomain,而模块的类主程序一般用不到。所以这种方式个人不推荐使用。
ApplicationDomain的hasDefinition()方法判断某定义是否存在,getDefinition()方法获取指定的定义。下面以一个
主程序首先将共享库加载到同域,完成后将“登录模块”加载到子域。主程序可以像操作普通的视觉对象(DisplayObject)一样操作加载的模块:监听事件、调用方法。因为编译器不会识别未定义的类,为使用强类型,建议为主类和模型定义相应的接口,使用少量的重复代码协助编程。
privatefunctionshowModule(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。
protectedfunctiongetClass(p_name:String):Class
{
try
{
returnApplicationDomain.currentDomain.getDefinition(p_name)asClass;
}catch(p_e:ReferenceError)
{
trace("定义" p_name "不存在");
returnnull;
}
returnnull;
}
登录模块获取库中的界面元素,并在点击按钮后抛出事件。Event类不允许带参数,必须使用继承Event的自定义事件抛出参数。主程序可以把模块的自定义事件也编译进去(这样就增大了整个程序的文件尺寸),或者让监听模块事件的函数接受一个Objcet参数,以获取其动态属性。
privatefunctiononLogin(p_e:Object):void
{
this.m_userName=p_e.userName;
varlogin:IModule=p_e.currentTarget;
login.removeEventListener("login",this.onLogin);
login.dispose();
this.loadSwf();
}
主程序收到事件之后卸载注册模块,加载“结果模块”到子域,并将登录模块传出的”userName”参数传给结果模块。
publicfunctionshow(p_parent:DisplayObjectContainer,...rest):void
{
varlibClass:Class=this.getClass("net.eidiot.appDomainDemo.Libaray");
if(libClass!=null)this.initUi(libClass,rest);
}
overrideprotectedfunctioninitUi(p_libClass:Class,p_rest:Array=null):void
{
this.addUi(this.getClass(p_libClass.BG_NAME),"结果");
varresultFunc:Function=p_libClass.getResult;
varuserName:String=p_rest[0];
this.addChild(resultFunc(userName));
}
注意initUi()方法分别使用了共享库中Libaray类的静态属性BG_NAME和静态方法getResult()。但是直接调用此静态方法会报错,可以先用resultFunc变量取出此方法。详细内容请参考


