AS3 Air Desktop - 从 ApplicationStorageDirectory 中的 SWF 调度自定义事件

AS3 Air Desktop - Dispatch Custom Event from SWF in ApplicationStorageDirectory

我有一系列我创建的 Air 桌面游戏。在玩游戏之前,用户必须先登录。为了简化操作,我在一个单独的项目中创建了登录系统,并将其保存为名为 "dashboard.swf" 的 swf 文件。打开游戏时,它会加载 dashboard.swf 并显示登录屏幕。除了登录功能外,dashboard.swf 还处理许多其他内容,例如游戏中的通用设置。

我不想每次对 dashboard.swf 进行更改时都重新编译每个游戏。所以,我从服务器下载它。我最初是在 ApplicationDirectory 中下载、保存和加载 dashboard.swf,它在 Mac 上运行良好。在 Window 10 上测试并做了一些研究后,我发现非 OSX 机器的 ApplicationDirectory 是只读的。

因此,我将 dashboard.swf 的位置更改为 ApplicationStorageDirectory。当我 运行 它在我的 Mac 上时,swf 加载得很好,但是第一个被调度的自定义事件抛出错误:

TypeError: Error #1034: Type Coercion failed: cannot convert com.thisapp.event::CustomEvent@12786a9fed31 to com.thisapp.event.CustomEvent

两个 CustomEvent.as 文件是相同的。当 dashboard.swf 保存到 Mac 上的 ApplicationDirectory 并从中加载时,它会很好地触发。将其移至 ApplicationStorageDirectory 后,出现此错误。所以我知道这不是实际自定义调度程序的问题。冒泡是真实的,在 Cancellable 中也是如此。

在这种情况下会导致类型强制失败的原因是什么?

这是我的自定义调度程序:

public class CustomEvent extends Event {

    public static const GOT_RESULT: String = "gotResult";
    public var result: Object;

    public function CustomEvent(type: String, result: Object = null, bubbles: Boolean = false, cancelable: Boolean = false) {
        // constructor code
        super(type, bubbles, cancelable);
        this.result = result;
    }

    public override function clone(): Event {
        return new CustomEvent(type, result, bubbles, cancelable);
    }

}

来自我的 dashboard.swf:

dispatchEvent(new CustomEvent(CustomEvent.GOT_RESULT, null,true,true));

在桌面应用程序的主要 class 中:

var dashboardURL:String = File.applicationStorageDirectory.url +"dashboard.swf";

  var myContext:LoaderContext = new LoaderContext(); 
myContext.applicationDomain = ApplicationDomain.currentDomain; 
var urlReq:URLRequest = new URLRequest(dashboardURL); 
var ldr:Loader = new Loader();
ldr.load(urlReq, myContext); 
ldr.contentLoaderInfo.addEventListener(Event.COMPLETE,loadit);
    function loadit(e:Event){
dashboard = e.target.content as MovieClip;
addChild(dashboard);
dashboard.addEventListener(CustomEvent.GOT_RESULT, runLogin);
}

回答 请参阅下面@BadFeelingAboutThis 的回答以了解为什么会发生 1034 错误。这是我修复它的方法:

首先 - 从服务器下载 swf(我使用的是 GreenSock 的 LoaderMax):

private function dowloadDashboard(){
    var url:String  = "https://path/to/your/swf/on/the/server.swf";
    var queue:LoaderMax = new LoaderMax({name:"mainQueue",onComplete:completeHandler});
    //Note: the format is set to "binary"
    queue.append( new DataLoader(url, {name:"mySwf",format:"binary", estimatedBytes:3000}) );
    queue.load();

    function completeHandler(event:LoaderEvent):void {
    //Note: "mySwf" is the name I gave to the DataLoader above.
         var b:ByteArray = LoaderMax.getContent("mySwf");
    //loadDashboard() is the next function and I'm passing the ByteArray to it.
        loadDashboard(b);
    }
                }

下一步 - 使用 ByteArray 加载具有适当上下文的 swf:

private function loadDashboard(b:ByteArray) {
var myContext:LoaderContext = new LoaderContext();
        myContext.allowLoadBytesCodeExecution = true;
        myContext.allowCodeImport = true;
        myContext.applicationDomain = ApplicationDomain.currentDomain; 
var ldr:Loader = new Loader();
ldr.loadBytes(b,myContext);
ldr.contentLoaderInfo.addEventListener(Event.COMPLETE,loadDone);
}

最后 - 将您的 swf 添加到舞台:

        function loadit(e:Event){
            dashboard = e.target.content as MovieClip;
            addChild(dashboard);
}

希望对大家有所帮助!请记住,在我的情况下,我有一个 Desktop Air 应用程序正在下载和加载服务器上的 swf 文件。

此错误通常意味着您有相同的 Class 从不同来源进入您的根应用程序。

在您的情况下,CustomEvent class 必须同时存在于宿主 SWF 文件和加载的 SWF 文件中。

因为您加载的 SWF 与宿主 SWF 不在同一个应用程序域中,flash/AIR 不会看到与相同 class 重叠的 class。因此,您在 SWF 中加载的 CustomEvent 现在被视为 com.thisapp.event::CustomEvent@12786a9fed31,它(尽管完全相同)被视为与 com.thisapp.event:CustomEvent 完全不同的 class。由于您的代码引用了后者,任何时候来自主机的 CustomEvent 试图填充类型为 :CustomEvent 的对象引用时,它都会抛出一个强制转换错误,因为它们实际上并不相同 class。

通常,解决此问题的方法是为加载的 SWF 指定 context,以便它将加载的 Class 集成到它自己的域中。这应该用加载的 SWF CustomEvent

覆盖主机 CustomEvent
var ldr:Loader = new Loader();

//The second parameter for load takes a LoaderContext
ldr.load(new URLRequest(File.applicationStorageDirectory.url +"dashboard.swf"), new LoaderContext(false, ApplicationDomain.currentDomain)); 
ldr.contentLoaderInfo.addEventListener(Event.COMPLETE,loadit);

function loadit(e:Event){
    dashboard = e.target.content as MovieClip;
    addChild(dashboard);
    dashboard.addEventListener(CustomEvent.GOT_RESULT, runLogin);
}

阅读有关加载程序上下文的更多信息here and here