安全沙箱(SandBox)和应用程序域(ApplicationDomain)一直是令Flash开发者谈虎色变的两个话题,相信每个人在成长路上都遇到这样或那样的问题,吐槽点太多。

此文并不是要对这两个技术点作详细介绍。可以说,每次看一遍这个系列,都有新的收获,对Flash Player的底层运作(很神秘的东东)也会有更深的了解。我这里仅仅是分享自己对ApplicationDomain的一个疑问。

ApplicationDomain的作用是管理SWF文件的类,即加载SWF文件时,协调SWF文件中类的关系。

综合Adobe的官方文档和网上的一些文章,ApplicationDomain常见的有三种关系:

1. 继承

这也是Loader对象加载SWF文件时的默认行为,被加载的SWF将置于当前应用程序域的子域,类似类的继承。对应代码为: 

 
  
  1. var loaderContext:LoaderContext = new LoaderContext(); 
  2. //这句代码可以省略,是默认行为。 ApplicationDomain的构造函数接受的参数为父域
  3. loaderContext.applicationDomain = new ApplicationDomain(ApplicationDomain.currentDomain); 

child.swf(被加载的SWF)将继承parent.swf(主程序)中的类定义,如果有同名的类,则被忽略,使用父SWF中的类代替之。child.SWF可以访问父SWF中的类,但父SWF不能直接访问子SWF中的类。

在Adobe的文档中提到:The advantage of this choice is that, if the child defines a class with the same name as a class already defined by the parent, no error results; the child simply inherits the parent’s definition of that class, and the child’s conflicting definition goes unused unless either child or parent calls the ApplicationDomain.getDefinition() method to retrieve it.

2.组合

将SWF加载到当前的应用程序域中,child.swf和parent.swf完全互通,这也是自定义RSL的实现方式,可用在模块化开发中,Flex框架自带的Module机制用的也是这一套。对应代码为: 

 
  
  1. var loaderContext:LoaderContext = new LoaderContext(); 
  2. //使用当前域
  3. loaderContext.applicationDomain = ApplicationDomain.currentDomain; 

如果有同名的类,child.swf中的类被忽略,且无法通过任何方式访问。

3.共存

child.swf比加载到系统域的子域,与parent.swf处于平行关系。对应代码为:

 
  
  1. var loaderContext:LoaderContext = new LoaderContext(); 
  2. //默认系统域为父异
  3. loaderContext.applicationDomain = new ApplicationDomain(); 

即便child.swf和parent.swf中有完全相同的类,也被视为不同类型,一个经典的例子是,在AIR程序中从 ApplicationStorageDirectory目录加载SWF文件,被加载的文件不会被放置在当前安全域中,而应用程序域总是依附域某个安全 域,因此就会出现这种问题。诡异的是,利用URLLoader可以从某种程度上避开这个问题,具体见这里。Adobe工程师的心理我们真的不懂!(也许你觉得你懂了,但总有一天你会发现,其实你不懂…)

不管是哪种情况,都秉承一个原则:已经存在的类定义无法被更改或覆盖。这应该是由ActionScript 3.0的语言特性决定的,作为一门强类型语言,如果连类定义都可以在运行期间修改,那整个语言体系恐怕都要变动。

还有第4种关系,比较复杂,这里就不说了,事实上我觉得根本就不应该存在第4种,有人用过吗?

————————我是分割线——————————————-

说了这么多,我遇到的问题是:

主程序:

  • Main.as
  • World.as

SubA.swf: 

  • SubA.as
  • World.as 

SubB.swf: 

  • SubB.as
  • World.as  

三个SWF中的World类有细微的区别,主程序依次去加载SubA.swf和SubB.swf,使用默认方式(即继承方式)去加载。测试结果是:

 在SubA.swf、SubB.swf和主程序中, 使用new World()初始化对象时,使用的都是主程序中的World类,通过 loader.contentLoaderInfo.applicationDomain.getDefinition("World")获取的类定义, 也是主程序中的World类,换言之,Adobe的文档有误,我猜想这会不会是最新的Flash Player在安全机制上作了改动导致的。

 对另外两种情况的测试结果与文档的叙述完全吻合,需要注意的是,在AIR和Flash Player中,在加载SWF文件时会有细微的差异