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);
}
我有一系列我创建的 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);
}