商城首页欢迎来到中国正版软件门户

您的位置: 首页 > 文章列表 > 编程开发 > flex与js通信与彼此之间的互调整理(二)

flex与js通信与彼此之间的互调整理(二)

  发布于2026-07-22 阅读(0)

扫一扫,手机访问

之前聊过Flex和JS之间的通信,当时还在琢磨这两门语言怎么搭上线。一晃任务快做完了,公司的Realspace产品之前只开放了JS的API,但用Flex的用户也不少,要求上加三维API。几个月过去,产品已经上线,代码也开源了——https://github.com/SuperMap/Flex-Realspace,里面是一个完整的AS与JS交互的实践案例。

网上关于Flex和JS通信的文章不少,但有个细节不知道大家有没有踩过坑:Flex调用JS时,JS文件好像必须放在固定目录,还得在index.template.html里动手脚。更头疼的是,团队里其他人导进Flash Builder后,这个文件会被重新生成,又得改一遍。如果正在用这个技术做产品,下面这个方案或许能帮上忙,至少当年我折腾出来时,觉得省了不少事。

大型项目里,JS代码通常会有很多用原型构建的类,Flex端也一样。假设场景是:用Flex做项目,但有些功能只能靠JS实现,于是把部分功能封装成JS文件,希望通过AS与JS通信来调用。JS文件写好了,但团队协作时总要在多个地方修改,很麻烦。这篇文章的核心思路,就是解决这个痛点。

一、设计好代码

首先得理清原理:JS文件加载后嵌入HTML页面,Flex端通过ExternalInterface.call("js方法",参数)调用JS方法,JS端通过ExternalInterface.addCallback("对外公开的方法名",具体实现)回调Flex。问题在于,JS里功能很多,每次都调用不同方法,AS和JS耦合太高,不符合设计模式,而且麻烦。反过来,JS回调Flex时,若动态注册多个方法,还容易命名冲突。

为了实现低耦合,只设两座桥:AS调用JS永远只通过一个固定方法,JS回调Flex也只用Flex初始化时动态注册的唯一方法。这样通信永远只经过这两个通道,避免了很多问题。但怎么区分每次调用不同的功能?答案就在参数上——说白了,通过参数判断行为。

在Realspace产品中,AS调用JS的统一入口是:

var result:Object = ExternalInterface.call("SuperMap.Web.Util.ApplicationManager.initBridgeFlexToJs", object);

这里的object是一个键值对,例如:

var realArgument:Array = [strServerUrl.toString() + "$String"];
var array:Object = {
    action: "FUNCTION",
    key: this.KEY,
    functionName: "checkPluginUpdate",
    isReturn: true,
    realArgument: realArgument
};

这个结构类似JSON,但传递的是行为指令。核心不变:方法永远调用同一个,通过参数区分。具体格式可以自定义,解析时稍麻烦,但思路清晰。

二、打包JS代码

如果代码符合上述逻辑,通信只通过两座桥,接下来的步骤就顺理成章了。当时花了大约一个月做出这个方案,已经可以流畅交互,但上级要求JS代码不能开放,而且每次修改index.template.html太麻烦,希望Flex开发者完全看不到JS文件,最好把JS打包成SWC,通过调用SWC里的JS来实现功能。听起来像不可能的任务,但研究后发现可行,而且有额外优势。

如何把JS文件打包成SWC?新建一个库项目,将所有JS文件放进去,如图:

项目名为bridge,里面包含JS文件和对应的AS文件。注意每个JS文件旁边都有一个...Stream.as文件,这是关键。Flex提供了一种流方式打包:为每个JS文件创建一个继承于flash.utils.ByteArray的类,例如ApplicationManagerStream

package SuperMap.Js {
    import flash.utils.ByteArray;
    [Embed(source="ApplicationManager.js", mimeType="application/octet-stream")]
    public class ApplicationManagerStream extends ByteArray {
        public function ApplicationManagerStream() {
            super();
        }
    }
}

每个JS文件对应一个Stream类,放在同一文件夹下,配合application/octet-stream类型。这样在输出目录中会自动生成SWC文件。

但SWC里是二进制数据,不能直接当AS类调用。还需要一个辅助类,比如IncludeStream.as

package SuperMap {
    import SuperMap.Js.ApplicationManagerStream;
    import SuperMap.Js.HashTableStream;
    import SuperMap.Js.SceneDivStream;
    import SuperMap.lib_Ajax.IServerJa va6RStream;
    import SuperMap.lib_Ajax.JsStream;
    import SuperMap.lib_Ajax.MicrosoftAjaxStream;
    import SuperMap.lib_Realspace.RealspaceStream;
    public class IncludeStream {
        public function IncludeStream() {}
        public function toString():String {
            var microsoftAjaxStream:MicrosoftAjaxStream = new MicrosoftAjaxStream();
            var jsStream:JsStream = new JsStream();
            var iServerJa va6RStream:IServerJa va6RStream = new IServerJa va6RStream();
            var realspaceStream:RealspaceStream = new RealspaceStream();
            var hashTableStream:HashTableStream = new HashTableStream();
            var applicationManagerStream:ApplicationManagerStream = new ApplicationManagerStream();
            var sceneDivStream:SceneDivStream = new SceneDivStream();
            return microsoftAjaxStream.toString() + jsStream.toString() + iServerJa va6RStream.toString() + realspaceStream.toString() + hashTableStream.toString() + applicationManagerStream.toString() + sceneDivStream.toString();
        }
    }
}

通过toString()方法,可以获取所有JS代码的字符串形式。然后把这个SWC引用到Flex项目中,就准备好了。

三、调用JS文件

AS只能调用嵌入到HTML页面中的JS,而目前只有字符串形式的代码,所以需要将字符串转化为标准JS并注入页面。在Flex项目启动的最开始,加入以下代码:

// 初始化一个方法,将字符串转换为JS代码并注入到HTML中
var str:String = "function parseStringToJs(str){var oHead = document.getElementsByTagName('HEAD').item(0);";
str += "var oScript = document.createElement(\"script\");";
str += "oScript.language = \"ja vascript\";";
str += "oScript.type = \"text/ja vascript\";";
str += "oScript.id = \"test\";";
str += "oScript.defer = true;";
str += "oScript.text = str;";
str += "oHead.appendChild(oScript);}";
// 调用window.eval创建这个函数
ExternalInterface.call("eval", str);
var includeStream:IncludeStream = new IncludeStream();
// 获取JS字符串
var strey:String = includeStream.toString();
// 如果代码中有反斜杠,需要转义(根据实际情况决定)
var char:RegExp = /\\/g;
strey = strey.replace(char, "\\\\");
// 注入JS代码
ExternalInterface.call("parseStringToJs", strey);
// 注册JS回调AS的入口
ExternalInterface.addCallback("initBridgeJsToFlex", ApplicationManager.initBridgeJsToFlex);

注释已经很清楚。核心思路:先用ExternalInterface.call调用eval创建一个将字符串转成JS代码的方法parseStringToJs,再调用它把字符串形式的源代码注入到HTML中。这样就不需要修改index.template.html,也不用添加外部引用。之后,通过ExternalInterface.call直接调用JS端暴露的桥方法,并通过ApplicationManager.initBridgeJsToFlex接收回调。只需添加SWC包,就能使用所有JS功能。

至于eval的性能问题——代码长度约8万多字符,实测没有明显卡顿。如果哪位有更好的办法,欢迎分享。希望这个方案对类似场景有帮助。

本文转载于:https://www.jb51.net/article/33693.htm 如有侵犯,请联系zhengruancom@outlook.com删除。
免责声明:正软商城发布此文仅为传递信息,不代表正软商城认同其观点或证实其描述。

热门关注