ExternalInterface.call的问题
请留意以下情景,要利用外部应用程序弹出文件选择窗体,并返回所选的文件路径。
使用ExternalInterface.call来实现,当用户60秒(Flex已经设置最长的等待时间)内未能够完成文件选择会弹出"1502"的错误,严重影响用户体现。
所以,一些比较耗时的操作(超过60s),或无法确定何时调用完成的方法(如上述),应该使用异步调用的方式,当方法执行完成后进行回调。
<![CDATA[
实现异步调用机制
Flex端
定义一个Dictionary,用来缓存回调的方法
private var m_AsyncDict:Dictionary;
定义OpenFileDialogAsync方法,用fscommand来实现,fscommand是单向的调用,不存在60秒超时的问题。
//弹出打开文件窗口,选择单个文件
public function OpenFileDialogAsync(title:String = "请选择1个文件", initFolder:String = "", filter:String = "", callbackString:Function = null):void
{
//创建异步ID
var id:String = mx.utils.UIDUtil.createUID();
//创建自定义异步请求
var request:String = "<args>";
request += "<arg>" + id + "</arg>";
request += "<arg>" + title + "</arg>";
request += "<arg>" + initFolder + "</arg>";
request += "<arg>" + filter + "</arg>";
request += "</args>";
//缓存回调方法
if (callbackString != null)
m_AsyncDict[id] = callbackString;
flash.system.fscommand(FLASH_TO_APP_OPEN_FILE_DIALOG_ASYNC, request);
}
定义并注册AsyncCallback和CancelAsyncCall方法,让外部应用程序能够响应异步回调或取消异步回调。
public function AsyncCallback(id:String, value:Object):void
{
if (m_AsyncDict[id])
{
//执行回调方法
m_AsyncDict[id](value);
delete m_AsyncDict[id];
}
}
public function CancelAsyncCall(id:String):void
{
if (m_AsyncDict[id])
{
delete m_AsyncDict[id];
}
}
…
if (ExternalInterface.available)
{
ExternalInterface.addCallback(APP_TO_FLASH_ASYNC_CALLBACK, AsyncCallback);
ExternalInterface.addCallback(APP_TO_FLASH_CANCEL_ASYNC_CALL, CancelAsyncCall);
}
EXE端
为ShockwaveFlashObject注册FSCommand事件,并添加事件处理的方法。
axShockwaveFlash1.FlashCall += new AxShockwaveFlashObjects._IShockwaveFlashEvents_FlashCallEventHandler(axShockwaveFlash1_FlashCall);
…
void axShockwaveFlash1_FSCommand(object sender, AxShockwaveFlashObjects._IShockwaveFlashEvents_FSCommandEvent e)
{
//请求e.args是自定义Xml格式
XmlDocument request = new XmlDocument();
request.LoadXml(e.args);
if (e.command == FLASH_TO_APP_OPEN_FILE_DIALOG_ASYNC)
{
//获取回调的键值
string asyncId = request.DocumentElement.ChildNodes[0].InnerText;
//获取窗体参数
string title = request.DocumentElement.ChildNodes[1].InnerText;
string initFolder = request.DocumentElement.ChildNodes[2].InnerText;
string filter = request.DocumentElement.ChildNodes[3].InnerText;
using (OpenFileDialog dlg = new OpenFileDialog())
{
dlg.Title = title;
dlg.InitialDirectory = initFolder;
dlg.Filter = filter;
if (dlg.ShowDialog() == DialogResult.OK)
{
AsyncCallback(asyncId, dlg.FileName);
}
else
{
CancelAsyncCall(asyncId);
}
}
}
}
定义2个方法调用Flex里注册的AsyncCallback和CancelAsyncCall方法。
//异步回调
public void AsyncCallback(string asyncId, string value)
{
StringBuilder sb = new StringBuilder();
sb.Append("<invoke name=/"").Append(APP_TO_FLASH_ASYNC_CALLBACK).Append("/" returntype=/"xml/">");
sb.Append("<arguments>");
sb.Append("<string>").Append(asyncId).Append("</string>");
sb.Append("<string>").Append(value).Append("</string>");
sb.Append("</arguments>");
sb.Append("</invoke>");
axShockwaveFlash1.CallFunction(sb.ToString());
}
//撤销异步回调
private void CancelAsyncCall(string asyncId)
{
StringBuilder sb = new StringBuilder();
sb.Append("<invoke name=/"").Append(APP_TO_FLASH_CANCEL_ASYNC_CALL).Append("/" returntype=/"xml/">");
sb.Append("<arguments>");
sb.Append("<string>").Append(asyncId).Append("</string>");
sb.Append("</arguments>");
sb.Append("</invoke>");
axShockwaveFlash1.CallFunction(sb.ToString());
}
完整代码
Flex端
LocalAPI.as
package
{
import flash.external.ExternalInterface;
import flash.system.fscommand;
import flash.utils.Dictionary;
import mx.utils.UIDUtil;
public class LocalAPI
{
//通讯命令定义
private static const FLASH_TO_APP_OPEN_FILE_DIALOG:String = "OpenFileDialog";
private static const FLASH_TO_APP_OPEN_FILE_DIALOG_ASYNC:String = "OpenFileDialogAsync";
private static const APP_TO_FLASH_ASYNC_CALLBACK:String = "AsyncCallback";
private static const APP_TO_FLASH_CANCEL_ASYNC_CALL:String = "CancelAsyncCall";
//Async dict
private var m_AsyncDict:Dictionary;
//Singleton static obj
private static var g_Instance:LocalAPI = null;
//获取LocalAPI单件实例
public static function get Instance():LocalAPI
{
if (g_Instance == null)
{
g_Instance = new LocalAPI();
}
return g_Instance;
}
public function LocalAPI()
{
if (g_Instance != null)
throw new Error("Singleton class. Please use Instance static filed.");
m_AsyncDict = new Dictionary();
if (ExternalInterface.available)
{
ExternalInterface.addCallback(APP_TO_FLASH_ASYNC_CALLBACK, AsyncCallback);
ExternalInterface.addCallback(APP_TO_FLASH_CANCEL_ASYNC_CALL, CancelAsyncCall);
}
}
//弹出打开文件窗口,选择单个文件(60s局限)
public function OpenFileDialog(title:String = "请选择1个文件", initFolder:String = "", filter:String = ""):String
{
if(!ExternalInterface.available) return null;
return ExternalInterface.call(FLASH_TO_APP_OPEN_FILE_DIALOG, title, initFolder, filter);
}
//异步通信
//弹出打开文件窗口,选择单个文件
public function OpenFileDialogAsync(title:String = "请选择1个文件", initFolder:String = "", filter:String = "", callbackString:Function = null):void
{
//创建异步ID
var id:String = mx.utils.UIDUtil.createUID();
//创建自定义异步请求
var request:String = "<args>";
request += "<arg>" + id + "</arg>";
request += "<arg>" + title + "</arg>";
request += "<arg>" + initFolder + "</arg>";
request += "<arg>" + filter + "</arg>";
request += "</args>";
//缓存回调方法
if (callbackString != null)
m_AsyncDict[id] = callbackString;
flash.system.fscommand(FLASH_TO_APP_OPEN_FILE_DIALOG_ASYNC, request);
}
public function AsyncCallback(id:String, value:Object):void
{
if (m_AsyncDict[id])
{
//执行回调方法
m_AsyncDict[id](value);
delete m_AsyncDict[id];
}
}
public function CancelAsyncCall(id:String):void
{
if (m_AsyncDict[id])
{
delete m_AsyncDict[id];
}
}
}
}
Demo.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" fontSize="12">
<mx:Script>
private function GetFilePath():void
{
var path:String = LocalAPI.Instance.OpenFileDialog();
if (path != null)
_Path1.text = path;
}
//异步方式
private function GetFilePathAsync():void
{
LocalAPI.Instance.OpenFileDialogAsync("请选择1个文件", "", "", function(result:String):void
{
_Path2.text = result;
});
}
</mx:Script>
<mx:Button x="445.75" y="100" label="浏览1" click="GetFilePath()"/>
<mx:Button x="445.75" y="147" label="浏览2" click="GetFilePathAsync()"/>
<mx:Label x="71" y="102" text="文件路径"/>
<mx:TextInput x="188.25" y="100" width="249.5" id="_Path1"/>
<mx:TextInput x="188.25" y="147" width="249.5" id="_Path2"/>
<mx:Label x="71" y="149" text="文件路径(异步方式)"/>
<mx:Text x="71" y="196" width="459.75" height="213" id="_Trace"/>
</mx:Application>
EXE端
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Xml;
namespace WinFormDemo
{
public partial class Form1 : Form
{
//定义通信命令
private string FLASH_TO_APP_OPEN_FILE_DIALOG = "OpenFileDialog";
private string FLASH_TO_APP_OPEN_FILE_DIALOG_ASYNC = "OpenFileDialogAsync";
private string APP_TO_FLASH_ASYNC_CALLBACK = "AsyncCallback";
private string APP_TO_FLASH_CANCEL_ASYNC_CALL = "CancelAsyncCall";
private bool m_Init = false;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
if (m_Init) return;
string swf = System.IO.Path.Combine(Application.StartupPath, "Demo.swf");
axShockwaveFlash1.FSCommand += new AxShockwaveFlashObjects._IShockwaveFlashEvents_FSCommandEventHandler(axShockwaveFlash1_FSCommand);
axShockwaveFlash1.FlashCall += new AxShockwaveFlashObjects._IShockwaveFlashEvents_FlashCallEventHandler(axShockwaveFlash1_FlashCall);
axShockwaveFlash1.Movie = swf;
m_Init = true;
}
void axShockwaveFlash1_FlashCall(object sender, AxShockwaveFlashObjects._IShockwaveFlashEvents_FlashCallEvent e)
{
//请求e.request是Xml格式,详细请查阅文档
XmlDocument request = new XmlDocument();
request.LoadXml(e.request);
string command = request.DocumentElement.Attributes["name"].Value;
if (command == FLASH_TO_APP_OPEN_FILE_DIALOG)
{
//获取窗体参数
string title = request.DocumentElement.ChildNodes[0].ChildNodes[0].InnerText;
string initFolder = request.DocumentElement.ChildNodes[0].ChildNodes[1].InnerText;
string filter = request.DocumentElement.ChildNodes[0].ChildNodes[2].InnerText;
using (OpenFileDialog dlg = new OpenFileDialog())
{
dlg.Title = title;
dlg.InitialDirectory = initFolder;
dlg.Filter = filter;
if (dlg.ShowDialog() == DialogResult.OK)
{
axShockwaveFlash1.SetReturnValue("<string>" + dlg.FileName + "</string>");
}
else
{
axShockwaveFlash1.SetReturnValue("<null/>");
}
}
}
}
void axShockwaveFlash1_FSCommand(object sender, AxShockwaveFlashObjects._IShockwaveFlashEvents_FSCommandEvent e)
{
//请求e.args是自定义Xml格式
XmlDocument request = new XmlDocument();
request.LoadXml(e.args);
if (e.command == FLASH_TO_APP_OPEN_FILE_DIALOG_ASYNC)
{
//获取回调的键值
string asyncId = request.DocumentElement.ChildNodes[0].InnerText;
//获取窗体参数
string title = request.DocumentElement.ChildNodes[1].InnerText;
string initFolder = request.DocumentElement.ChildNodes[2].InnerText;
string filter = request.DocumentElement.ChildNodes[3].InnerText;
using (OpenFileDialog dlg = new OpenFileDialog())
{
dlg.Title = title;
dlg.InitialDirectory = initFolder;
dlg.Filter = filter;
if (dlg.ShowDialog() == DialogResult.OK)
{
AsyncCallback(asyncId, dlg.FileName);
}
else
{
CancelAsyncCall(asyncId);
}
}
}
}
//异步回调
public void AsyncCallback(string asyncId, string value)
{
StringBuilder sb = new StringBuilder();
sb.Append("<invoke name=/"").Append(APP_TO_FLASH_ASYNC_CALLBACK).Append("/" returntype=/"xml/">");
sb.Append("<arguments>");
sb.Append("<string>").Append(asyncId).Append("</string>");
sb.Append("<string>").Append(value).Append("</string>");
sb.Append("</arguments>");
sb.Append("</invoke>");
axShockwaveFlash1.CallFunction(sb.ToString());
}
//撤销异步回调
private void CancelAsyncCall(string asyncId)
{
StringBuilder sb = new StringBuilder();
sb.Append("<invoke name=/"").Append(APP_TO_FLASH_CANCEL_ASYNC_CALL).Append("/" returntype=/"xml/">");
sb.Append("<arguments>");
sb.Append("<string>").Append(asyncId).Append("</string>");
sb.Append("</arguments>");
sb.Append("</invoke>");
axShockwaveFlash1.CallFunction(sb.ToString());
}
}
}
系列索引
]]>