动态加载 SWF 会导致先前加载的 SWF 行为异常

发布于 2024-08-26 08:18:07 字数 3246 浏览 4 评论 0原文

我在使用 Flash 和 Flex 时遇到了一个非常奇怪的问题。在某些情况下,如果同时加载了另一个 SWF,则在运行时(使用 Loader)加载的 SWF 中的影片剪辑似乎无法实例化。这是重现错误的程序的完整代码。它是使用 mxmlc 编译的,通过 Ensemble Tofino

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);
        }
    }
}

第二个加载操作注释为如上所示,代码有效。但是,如果第二个加载操作未注释,并且 onTinyLoadComplete 在加载第二个 SWF 之后运行 onTinyLoadComplete,则包含 new spriteClass() 的行将失败,并出现以下异常:

TypeError: Error #1034: Type Coercion failed: cannot convert flash.display::MovieClip@2dc8ba1 to 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 以便不为 ActionScript 导出 SubSymbol,则错误就会消失,但在我们的实际项目中,我们需要能够动态加载包含其他导出的精灵类作为子级的精灵类。

关于造成这种情况的原因或如何解决它有什么想法吗?

编辑: 有几个人建议tiny.swf 可能包含一个与test.swf 或父级(DynamicLoading.swf) 中的类同名的类。 事实并非如此。正如我上面所说,我自己通过简单地发布一个全新的空 Flash CS4 项目来创建tiny.swf。以下是在 tiny.swf 上运行时 swfdump -D 的完整输出:

[HEADER]        File version: 10
[HEADER]        File is zlib compressed. Ratio: 41%
[HEADER]        File size: 1343
[HEADER]        Frame rate: 30.000000
[HEADER]        Frame count: 1
[HEADER]        Movie width: 550.00
[HEADER]        Movie height: 400.00
[045]         4 FILEATTRIBUTES as3 symbolclass
[04d]      1284 METADATA
[009]         3 SETBACKGROUNDCOLOR (ff/ff/ff)
[056]        11 SCENEDESCRIPTION
[001]         0 SHOWFRAME 1 (00:00:00,000)
[000]         0 END

I have run into a very strange problem with Flash and Flex. It appears that under certain circumstances, movie clips from a SWF loaded at runtime (using Loader) cannot be instantiated if another SWF has been loaded in the mean time. Here is the complete code for a program that reproduces the error. It is compiled using mxmlc, via Ensemble Tofino:

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);
        }
    }
}

With the second loading operation commented out as shown above, the code works. However, if the second loading operation is uncommented and onTinyLoadComplete runs after the second SWF is loaded, the line containing new spriteClass() fails with the following exception:

TypeError: Error #1034: Type Coercion failed: cannot convert flash.display::MovieClip@2dc8ba1 to 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 and tiny.swf were created in Flash CS4. test.swf contains two symbols, both exported for ActionScript, one called TopSymbol and one called SubSymbol. SubSymbol contains a simple graphic (a scribble) and TopSymbol contains a single instance of SubSymbol. tiny.swf contains nothing; it is the result of publishing a new, empty ActionScript 3 project.

If I modify test.swf so that SubSymbol is not exported for ActionScript, the error goes away, but in our real project we need the ability to dynamically load sprite classes that contain other, exported sprite classes as children.

Any ideas as to what is causing this, or how to fix it?

Edit: A couple of people have suggested that tiny.swf may contain a class with the same name as a class from test.swf or the parent (DynamicLoading.swf). It does not. As I said above, I created tiny.swf myself by simply publishing a brand-new, empty Flash CS4 project. Here is the complete output of swfdump -D when run on tiny.swf:

[HEADER]        File version: 10
[HEADER]        File is zlib compressed. Ratio: 41%
[HEADER]        File size: 1343
[HEADER]        Frame rate: 30.000000
[HEADER]        Frame count: 1
[HEADER]        Movie width: 550.00
[HEADER]        Movie height: 400.00
[045]         4 FILEATTRIBUTES as3 symbolclass
[04d]      1284 METADATA
[009]         3 SETBACKGROUNDCOLOR (ff/ff/ff)
[056]        11 SCENEDESCRIPTION
[001]         0 SHOWFRAME 1 (00:00:00,000)
[000]         0 END

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

柏拉图鍀咏恒 2024-09-02 08:18:07

当加载两个 swf 时,我看到过奇怪的行为。当两个 swf 具有同一类的不同版本时,就会出现问题。检查以确保 TopSymbol 中的 SubSymbol 实例与直接加载的 SubSymbol 相同。

I've seen funky behavior when two swfs are loaded. The issues arises when the two swfs have different versions of the same class. Check to make sure that the SubSymbol instance within TopSymbol is the same as the the SubSymbol you're loaded directly.

夜清冷一曲。 2024-09-02 08:18:07

由于有多个同名类,这看起来确实是个问题。由于只要没有加载tiny.swf,代码就可以工作,所以我倾向于认为这是我们的罪魁祸首。确保图书馆确实是空的。

This does seem like problem due to having more than one class with the same name. Since the code works as long as tiny.swf is not loaded, I'm incline to think that's our culprit. Make sure the library is indeed empty.

烟织青萝梦 2024-09-02 08:18:07

根据 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);
    }
}    

According to Adobe's documentation, if you want to directly access symbol classes in a loaded SWF (such as TopSymbol), you should specify the current application domain in the loading context. So for me, the bigger question is why your first example with the second loading commented out works. My only guess is that the complete event may be executing in the context of the Loader's current application domain.

Try explicitly loading test.swf into the current application domain, like this:

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);

Edit:

Since you're wanting to make sure dynamically loaded SWFs are loaded in a separate application domain, you may want to explicitly create your ApplicationDomain object for each loading. I have no idea what Loader uses for its app domain by default. Try something like this:

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);
    }
}    
另类 2024-09-02 08:18:07

为了排除 Adob​​e 加载类中的许多错误,我肯定会使用 greensock LoaderMax 库......
http://www.greensock.com/loadermax/
简单、免费+强大。

To rule out the many bugs in Adobe's loading classes, i'd definitely use the greensock LoaderMax library...
http://www.greensock.com/loadermax/
Simple, free + powerful.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文