深入理解Flash Player的应用程序域(Application Domains)(6)
Same Domain: Runtime Shared Libraries 相同的域:运行时共享库
把新增的定义增加到现有的应用程序域下可能是应用程序域最大的用处。因为继承只能把父域内的定义对子域共享,而合并定义到相同的应用程序域内则可以对所有使用这个域的SWF共享,包括父级和子级。
父应用程序域包括了子SWF的定义
运行时共享库(RSLs)正是运用了这种机制。RSLs是可以在运行时被加载的独立的代码库。通过RSLs,其他SWF可以共用其中的代码而不需要编译到自身,从而排除了冗余,减小了文件量,也让代码更容易维护。我们在主应用程序域中加载RSL,从而可以在整个程序中共享定义。
使用RSLs之前需要做些准备工作。首先,ActionScript编译器需要在发布SWF文件的时候知道哪些定义不需要被编译。
原生的Flash Player API定义就不需要编译。虽然每个SWF都需要用到原生的定义(Array,XML,Sprite等),但是这些定义只存在于Flash Player的可执行文件中,不需要也不会被编译到SWF文件中。编译器使用一个叫做playerglobal.swc的特殊SWC(预先编译的SWF类库)来识别原生定义。它包含了原生定义的接口,包括定义的名字和数据类型等。编译器通过它来编译SWF,而且不会把这些定义编译到最终的SWF中。
编译器还可以引用其他类似playerglobal.swc一样的SWC库。这些库作为“外部”类库,其中包含的定义只是用于编译,不会包含到SWF内部。
这里不详细讨论在编辑工具中如何进行库链接的设置。不同版本的编辑器的设置有些不同,具体方法请参考Flash文档。
虽然我们用SWCs来编译SWF,但实际上他们本身就是SWF文件,和其他被加载的SWF内容类似。在进行库编译的时候,同时生成了SWF和SWC文件。SWF用于运行时加载,而SWC在编译时用做外部库。
编译器使用SWCs共享库,SWF共享库在运行时加载
另一个准备工作需要编写代码。使用外部库的时候,发布的SWF中不包含库中的定义。如果Flash Player尝试运行其中代码,就会产生核查错误,整个SWF基本上就瘫痪了。
Flash Player会在类第一次使用的时候校验其定义。如果应用程序域中不包括该定义,那么校验错误就会产生。
实际上缺少定义产生的错误有两种。校验错误是两种之中最糟的,表示类无法正常工作的灾难性失败。另一种是引用错误,当某种数据类型被引用但是却不可用的情况下发生。虽然缺失定义也会造成引用错误,但这种错误只会在已经经过核查的类内部打断代码执行的正常过程。
var instance:DoesNotExist; // VerifyError: Error #1014: Class DoesNotExist could not be found. // 当Flash Player校验包含该定义的类时发生校验错误var instance:Object = new DoesNotExist(); // ReferenceError: Error #1065: Variable DoesNotExist is not defined. // 当代码执行到这一行的时候发生引用错误
主要的区别在于校验错误与类定义有关,而引用错误与代码执行相关。在类内部的代码要执行之前,必须要先通过校验。上面的例子中instance对象声明为Object类型,校验可以正常通过(只是在执行的时候就会遇到引用错误)。
Note: Strict Mode 注意:严格模式
外部库是引用定义而不需将其编译到SWF中的一种方法。另一种方法是关闭严格模式,这将大大放宽了对变量使用的检查。对于类的使用来说,你可以引用一个不存在的类而不会引起编译器报错。你不能直接把不存在的类用作变量类型(这样做会在运行时产生校验错误),但是你可以像上面的“引用错误”例子中那样去引用。在非严格模式下,编译器也许会检测不到一些可能发生的错误,所以通常不建议用这种模式。
使用了RSLs的SWF文件必须保证先加载好RSLs,才能使用这些外部定义。我们应该在主应用程序开始执行之前用一个预加载器来加载RSLs。
下面演示了一个SWF加载包含Doughnut类的外部RSL的例子。虽然在SWF中直接引用了这个类,但是它却是编译在外部库中,并通过SWC的方式来引用的。RSL在Doughnut类第一次使用之前就被加载进来,所以不会造成校验错误。
Doughnut.as (编译为 doughnutLibrary.swc 和 doughnutLibrary.swf):
package { import flash.display.Sprite; public class Doughnut extends Sprite { public function Doughnut(){ // draw a doughnut shape graphics.beginFill(0xFF99AA); graphics.drawCircle(0, 0, 50); graphics.drawCircle(0, 0, 25); } } }
ShapesMain.as (Shapes.swf的主类):
package { import flash.display.Sprite; public class ShapesMain extends Sprite { public function ShapesMain(){ // 虽然并没有编译到Shapes.swf中, // 但是我们通过doughnutLibrary.swc外部库 // 可以获得对Doughnut类的引用 var donut:Doughnut = new Doughnut(); donut.x = 100; donut.y = 100; addChild(donut); } } }
Shapes.swf (RSL loader):
var rslLoader:Loader = new Loader(); rslLoader.contentLoaderInfo.addEventListener(Event.INIT, rslInit); // 把RSL中的定义加载到当前应用程序域中 var context:LoaderContext = new LoaderContext(); context.applicationDomain = ApplicationDomain.currentDomain; var url:String = "doughnutLibrary.swf"; rslLoader.load(new URLRequest(url), context); function rslInit(event:Event):void { // 只有当RSL中的定义导入到当前应用程序域以后 // 我们才能用其中的Doughnut定义通过ShapesMain类的校验 addChild(new ShapesMain()); }
在这个例子中,Shapes.swf是主程序,当RSL加载完毕后实例化主类ShapesMain。如果没有导入RSL中的定义,创建ShapesMain实例的时候就会因为在应用程序域中找不到对应的类而发生校验错误。
注意:Flex中的RSL
这里讨论的方法是最底层的方法,不应该用于Flex开发。Flex框架中有自己的一套RSLs处理机制,更多关于RSL在Flex中的应用,请参考Flex Runtime Shared Libraries (Flex 4)。
热门文章推荐
- [HLS]做自己的m3u8点播系统使用HTTP Live Streaming(HLS技术)
- [FMS]FMS流媒体服务器配置与使用相关的介绍
- [AS3]什么是M3U8,与HTML5的区别是什么
- AS2.0 让flash自适应全屏,并且不自动缩放
- [AS3]as3.0的sound类常用技巧整理
- [AS3]as3与ByteArray详解、ByteArray介绍、ByteArray用法
- 关于RTMP,RTMPT,RTMPS,RTMPE,RTMPTE协议的介绍
- [JS]分享浏览器弹出窗口不被拦截JS示例