动态加载SWF会导致先前加载的SWF出现问题

4

我遇到了一个与Flash和Flex有关的非常奇怪的问题。在特定情况下,如果在此期间加载了另一个SWF,则无法实例化使用Loader运行时加载的SWF中的影片剪辑。以下是可以重现错误的程序的完整代码。它是通过Ensemble Tofino编译的mxmlc:

package 
{
    import flash.display.*;
    import flash.events.*;
    import flash.net.*;
    import flash.system.*;

    public class DynamicLoading extends Sprite
    {
        private var testAppDomain:ApplicationDomain;

        public function DynamicLoading()
        {
            var request:URLRequest = new URLRequest("http://localhost/content/test.swf");
            var loader:Loader = new Loader();
            loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onTestLoadComplete);
            loader.load(request);
        }

        private function onTestLoadComplete(e:Event):void
        {
            var loaderInfo:LoaderInfo = LoaderInfo(e.target);
            testAppDomain = loaderInfo.applicationDomain;

            // To get the error, uncomment these lines...
            //var request:URLRequest = new URLRequest("http://localhost/content/tiny.swf");
            //var loader:Loader = new Loader();
            //loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onTinyLoadComplete);
            //loader.load(request);

            // ...and comment this one:
            onTinyLoadComplete();
        }

        private function onTinyLoadComplete(e:Event = null):void
        {
            var spriteClass:Class = Class(testAppDomain.getDefinition("TopSymbol"));
            var sprite:Sprite = Sprite(new spriteClass());

            sprite.x = sprite.y = 200;

            addChild(sprite);
        }
    }
}

在上面注释掉第二个加载操作的情况下,代码可以正常运行。但是,如果取消注释第二个加载操作,并且在第二个SWF加载后运行onTinyLoadComplete,则包含“new spriteClass()”的行将失败,并出现以下异常:
TypeError: Error #1034: 类型转换失败: 无法将flash.display::MovieClip@2dc8ba1转换为SubSymbol。 at flash.display::Sprite/constructChildren() at flash.display::Sprite() at flash.display::MovieClip() at TopSymbol() at DynamicLoading/onTinyLoadComplete()[C:\Users\...\TestFlash\DynamicLoading.as:38]
test.swf和tiny.swf是在Flash CS4中创建的。test.swf包含两个符号,都导出为ActionScript,一个称为TopSymbol,另一个称为SubSymbol。SubSymbol包含一个简单的图形(涂鸦),而TopSymbol包含SubSymbol的单个实例。tiny.swf什么也没有;它是发布一个新的、空的ActionScript 3项目的结果。
如果我修改test.swf,使得SubSymbol不导出为ActionScript,错误就会消失,但在我们的真实项目中,我们需要能够动态加载包含其他导出精灵类作为子项的精灵类的能力。
有什么想法是什么原因造成这种情况,或如何解决?
编辑: 有几个人建议tiny.swf可能包含与test.swf或父级(DynamicLoading.swf)相同名称的类。它不是这样的。正如我上面所说,我自己创建了tiny.swf,只需发布一个全新的、空的Flash CS4项目即可。当在tiny.swf上运行swfdump -D时,以下是完整的输出:

你可能想要在 test.swf 和 tiny.swf 上运行 swfdump,以查看它们内部的内容。 - James Ward
我在这两个文件上运行了swfdump。 test.swf 包含了预期的三个类:test、TopSymbol 和 SubSymbol。tiny.swf 根本没有包含任何类;请参见我编辑部分中的 swfdump 输出。感谢您指向 swfdump;我可以看到它是一个有用的工具。但是,这并没有解决我的原始问题。 - Aaron
4个回答

1

根据Adobe的文档,如果您想直接访问加载的SWF中的符号类(例如TopSymbol),则应在加载上下文中指定当前应用程序域。所以对我来说,更重要的问题是为什么您第一个示例中注释掉第二个加载的代码能够正常工作。我唯一的猜测是complete事件可能在Loader的当前应用程序域的上下文中执行。

尝试将test.swf显式加载到当前应用程序域中,像这样:

var context:LoaderContext = new LoaderContext();
context.securityDomain = SecurityDomain.currentDomain;
context.applicationDomain = ApplicationDomain.currentDomain;

var request:URLRequest = new URLRequest("http://localhost/content/test.swf");
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onTestLoadComplete);
loader.load(request, context);

编辑:

由于您想确保动态加载的 SWF 在单独的应用程序域中加载,因此您可能希望为每个加载显式创建您的 ApplicationDomain 对象。我不知道 Loader 默认使用什么应用程序域。尝试像这样做:

public class DynamicLoading extends Sprite
{
    private var testAppDomain:ApplicationDomain;
    private var tinyAppDomain:ApplicationDomain;

    public function DynamicLoading()
    {
        testAppDomain = new ApplicationDomain();
        var context:LoaderContext = new LoaderContext(false, testAppDomain);
        var request:URLRequest = new URLRequest("http://localhost/content/test.swf");
        var loader:Loader = new Loader();
        loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onTestLoadComplete);
        loader.load(request, context);
    }

    private function onTestLoadComplete(e:Event):void
    {
        tinyAppDomain = new ApplicationDomain();
        var context:LoaderContext = new LoaderContext(false, tinyAppDomain);
        var request:URLRequest = new URLRequest("http://localhost/content/tiny.swf");
        var loader:Loader = new Loader();
        loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onTinyLoadComplete);
        loader.load(request, context);
    }

    private function onTinyLoadComplete(e:Event = null):void
    {
        var spriteClass:Class = Class(testAppDomain.getDefinition("TopSymbol"));
        var sprite:Sprite = Sprite(new spriteClass());

        sprite.x = sprite.y = 200;

        addChild(sprite);
    }
}    

你说得没错,将SWF加载到当前应用程序域中可以消除错误,但我想将SWF加载到一个单独的域中,以便稍后卸载它。(在我的实际项目中,主SWF将加载可能有数十个子SWF,但一次只加载一个,因此我希望能够通过在使用完毕后卸载这些子SWF来节省资源。)根据Adobe的文档,当未指定LoaderContext时,默认值是... - Aaron
将内容加载到加载器应用程序域的子级中(如上述代码中)是一种常见的做法。在这种情况下,“如果父级希望使用子级的类,则必须调用ApplicationDomain.getDefinition()来检索它们。”这正是我的代码所做的,也是为什么它可以正常工作,而第二个加载则被注释掉了。我想知道的是,为什么取消注释第二个加载会有任何不同?更重要的是,如果我想要能够加载和卸载多个SWF,该怎么做呢? - Aaron
我不确定为什么第二次加载不同,除非“complete”事件在不同的上下文中运行。如果是这样,那么如果您在加载test.swf后以回调方式运行构造函数,与在加载tiny.swf后以回调方式运行构造函数会有所不同。如果您仍然加载tiny.swf,但在稍后的代码中进行精灵实例化(响应按钮按下等),会发生什么? - Jacob
此外,testAppDomain.getDefinition("SubSymbol") 是否有效?也许 getDefinition("TopSymbol") 不会级联加载依赖项。 - Jacob
你们两人的评论听起来都很有前途,但不幸的是都没有成功。我尝试将 getDefinition 调用移动到按钮单击处理程序中,但仍然收到相同的消息。testAppDomain.getDefinition("SubSymbol") 确实有效,但仅因为 SubSymbol 不包含任何进一步具有自己的类类型的子级(记住,错误是类型强制错误)。使用 getDefinition(“SubSymbol”) 然后紧接着使用 getDefinition(“TopSymbol”) 也无济于事。 - Aaron
显示剩余2条评论

1

当加载两个 swf 文件时,我曾经看到过一些奇怪的行为。问题出现在当这两个 swf 文件拥有不同版本的相同类时。请检查确保 TopSymbol 中的 SubSymbol 实例与你直接加载的 SubSymbol 是相同的。


“the SubSymbol you've loaded directly”是什么意思?唯一的SubSymbol就在TopSymbol内部。 - Aaron

1

这似乎是由于有多个同名类而引起的问题。由于只要未加载tiny.swf代码就可以正常工作,我倾向于认为这是罪魁祸首。请确保库确实为空。


我使用swfdump检查了tiny.swf;请参见我的编辑以查看原始问题的结果。 tiny.swf确实为空,这就是为什么这个问题如此令人困惑的原因。 - Aaron

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接