之前寫了一篇flex和js之間的通信,還記得最開始研究這兩門語言如何交互,一晃我的任務(wù)也快做完了,公司的realspace產(chǎn)品之開了js的API,但是現(xiàn)在使用flex產(chǎn)品的人也比較多,要求開設(shè)flex的三維API,已經(jīng)過去幾個月了,產(chǎn)品也上線了,有興趣的可以在https://github.com/SuperMap/Flex-Realspace下載,這是一個利用as與js交互放在了一個完整的產(chǎn)品里。
網(wǎng)上也有很多關(guān)于flex和js通信的文章,不過不知道大家有沒有注意到好像都需要把js文件放在固定的文件下(flex調(diào)用js),并且需要在index.template.html文件里面動手腳,而且你把源代碼考給團隊的其他人,發(fā)現(xiàn)flash builder導進去后重新生成了index.template.html,又得自己修改一下,不是很麻煩嗎?如果你是需要此技術(shù)做自己的產(chǎn)品的話我建議你一定要看一下我想出的辦法,也許會幫了不少人哦!
一個大項目或者產(chǎn)品,在js代碼里面估計都會有很多用原型建的類,在flex端估計也有很多功能。我們的假設(shè)場景是你正在使用flex制作項目,不過有一些功能只能通過js來實現(xiàn),所以你將部分功能使用js封裝成了一些js的文件,希望通過as與js之間的通信來使用as語言調(diào)用js里面的功能。js文件基本已經(jīng)寫好了,但是因為需要在不少地方懂手腳團隊協(xié)作起來不方便,我這篇文章就是用來解決這個問題的。
一、設(shè)計好代碼
首先我們需要明白的是:js文件在啟動之后被加載到html頁面里面,我們可以通過ExternalInterface.call("js的方法",參數(shù));在flex端調(diào)用js端的方法,我們可以通過ExternalInterface.addCallback("對外公開的方法名",具體實現(xiàn)的方法);來解決在js端調(diào)用flex端的方法。
現(xiàn)在我們需要思考:js里面有很多功能,我們每次調(diào)用js功能都得去調(diào)用相關(guān)的方法嗎?那樣as和js之間的耦合不是太高了,不符合設(shè)計模式,并且很麻煩。同樣js這邊需要回調(diào)(常用在事件回調(diào)上)flex的方法時,方法很多,我們是不是也要在flex端動態(tài)注冊那么多個方法名稱呢?那樣不怕名稱沖突嗎?所以這樣的實現(xiàn)都是不可取的。
為了達到低耦合,我將as和js之間的通信設(shè)定為只有兩座橋,as調(diào)用js功能永遠只會通過調(diào)用js里面的固定方法,js回調(diào)flex的功能也只能調(diào)用在程序初始化flex端動態(tài)創(chuàng)建的唯一的一個方法。這樣as和js之間的通信永遠只通過這兩個方法來交互,就不會出現(xiàn)之前的很多問題。但是大家肯定想問如何知道我這一次調(diào)用的功能和我下一次調(diào)用的有什么區(qū)別怎么辦?那就是在參數(shù)上面做手腳了。說白了就是通過參數(shù)來判斷你的行為。
在我們flex realspace產(chǎn)品面as調(diào)用js的功能都是通過var result:Object=ExternalInterface.call("SuperMap.Web.Util.ApplicationManager.initBridgeFlexToJs",object);這一局來的,SuperMap.Web.Util.ApplicationManager.initBridgeFlexToJs是在js端的一個文件里面的方法,唯一的as訪問js的通道,特殊的是object,這是一個鍵值對,形如:
復制代碼 代碼如下:
var realArgument:Array=[strServerUrl.toString()+"$String"];
var array:Object={
action:"FUNCTION",
key:this.KEY,
functionName:"checkPluginUpdate",
isReturn:true,
realArgument:realArgument
};
上面的array就是我說的object,里面的action:"FUNCTION"代表我需要調(diào)用的是方法,調(diào)用的對象的key(唯一標示)是this.KEY,方法名稱是checkPluginUpdate,有返回值,方法的參數(shù)是realArgument(一個特殊數(shù)組)。
其實這些有點類似json,用來傳遞數(shù)據(jù)的,而我自定義的一個格式是用來傳遞行為操作的,這個你們可以自己定義,反正核心就是通過參數(shù)來判斷你的行為,方法永遠調(diào)用同一個,這點明白了就是自己設(shè)計上的事情,不過在解析的時候有點麻煩哈!
二、打包js代碼
如果你的代碼符合我這樣的邏輯,交互通過兩座橋來通信,那么我們就開始下一步,其實這樣設(shè)計后已經(jīng)可以使得自己的產(chǎn)品交互起來很方便了,我當時花了一個月就是這么做的,都做的差不多了,上面交代說js代碼不能開放出來,而且每次都需要修改index.template.html文件,太麻煩,希望flex端的開放人員完全看不到j(luò)s的任何東西,要求需要把js文件打包成為swc文件,通過去調(diào)用swc文件里面的js來實現(xiàn)功能,我當時一聽這簡直就是不可能的嘛!不過后來研究了好久,發(fā)現(xiàn)這樣可以實現(xiàn),并且還有它的優(yōu)勢。
首先我們需要知道如何把js文件打包成為swc文件,新建一個庫項目,將你的js文件都放進去,如下圖:
![](/d/20211017/cb7a72eaa7c0ea6d9b71b26d63d6d8d8.gif)
我建的項目叫bridge,里面有我所有的js文件,還有不少as文件,細心的發(fā)現(xiàn)每一個js文件旁邊都有一個...Stream.as文件,這里就是重點了,我們?nèi)绾尾拍馨裫s文件打包到swc里面呢,flex提供了一種流的方式,例如上面的ApplicationManager.js文件,我們創(chuàng)建一個類ApplicationManagerStream繼承于flash.utils.ByteArray,代碼如下:
復制代碼 代碼如下:
package SuperMap.Js
{
import flash.utils.ByteArray;
/**
* 制定js文件以及打包方式
*/
[Embed(source="ApplicationManager.js", mimeType="application/octet-stream")]
public class ApplicationManagerStream extends ByteArray
{
public function ApplicationManagerStream()
{
super();
}
}
}
寫好你對應(yīng)的js文件,一定要和此類放在一個文件夾下,配上打包的類型application/octet-stream,每一個js文件都配置一個相應(yīng)的as類,這樣在
里面會自動生成好打包好的swc文件
現(xiàn)在問題又來了,里面是二進制的東西,我們?nèi)绾问褂冒?,不是我們平時寫的as類,寫了什么方法引用這個包直接使用就行。
所以我們還需要一個as類。這里我創(chuàng)建IncludeStream.as,代碼如下:
復制代碼 代碼如下:
package SuperMap
{
import SuperMap.Js.ApplicationManagerStream;
import SuperMap.Js.HashTableStream;
import SuperMap.Js.SceneDivStream;
import SuperMap.lib_Ajax.IServerJava6RStream;
import SuperMap.lib_Ajax.JsStream;
import SuperMap.lib_Ajax.MicrosoftAjaxStream;
import SuperMap.lib_Realspace.RealspaceStream;
/**
* 此類主要用于以字符串形式獲取二進制形式的js代碼
*/
public class IncludeStream
{
/**
* 構(gòu)造函數(shù)
*/
public function IncludeStream()
{
}
/**
* 返回js文件里面代碼的字符串形式
*/
public function toString():String
{
var microsoftAjaxStream:MicrosoftAjaxStream=new MicrosoftAjaxStream();
var jsStream:JsStream=new JsStream();
var iServerJava6RStream:IServerJava6RStream=new IServerJava6RStream();
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()+iServerJava6RStream.toString()+realspaceStream.toString()+hashTableStream.toString()+applicationManagerStream.toString()+sceneDivStream.toString();
}
}
}
有了這個類,我們可以通過實例化一個此對象,然后通過方法toString()獲取到字符串形式的js的所有代碼。這又是一個問題,我們還是不能使用,沒法調(diào)用。不過先這樣打包好,引用到你的flex項目里面再說。
三、調(diào)用js文件
首先我們要知道as調(diào)用js的方法只能調(diào)用js形式的方法,也就是嵌入到了html頁面的js,而現(xiàn)在我們只有字符串形式的js源代碼,所以需要想辦法將它轉(zhuǎn)化為標準的js代碼,嵌入到html頁面里面。
在你的flex項目啟動的最開始的地方恰如如下代碼:
復制代碼 代碼如下:
//初始化一個字符串形式的方法parseStringToJs,用于將字符串轉(zhuǎn)換為js語言
var str:String = "function parseStringToJs(str){var oHead = document.getElementsByTagName('HEAD').item(0);";
str+="var oScript = document.createElement(\"script\");";
str+="oScript.language = \"javascript\";";
str+="oScript.type = \"text/javascript\";";
str+="oScript.id = \"test\";";
str+="oScript.defer = true;";
str+="oScript.text = str;";
str+="oHead.appendChild(oScript);}";
//調(diào)用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的API
ExternalInterface.call("parseStringToJs",strey);
//初始化js調(diào)用as的入口方法initBridgeJsToFlex,最終飯方法的實現(xiàn)轉(zhuǎn)給方法ApplicationManager.initBridgeJsToFlex
ExternalInterface.addCallback("initBridgeJsToFlex",ApplicationManager.initBridgeJsToFlex);
注釋已經(jīng)寫的很清楚了,不過正則表達式這一步可以不要,我這里是因為我的代碼里出現(xiàn)了反斜杠的東西,所以我這樣是為了轉(zhuǎn)義一下,這里的思路就是先使用ExternalInterface.call去調(diào)用window對象的方法eval在window下創(chuàng)建一個將字符串轉(zhuǎn)化為js代碼的方法parseStringToJs,然后再調(diào)用此方法將我們的字符串的源代碼進行轉(zhuǎn)化,這樣html中就存在js的代碼了,就不用再去修改index.template.html文件添加引用和添加入口了。并且在其他地方你可以通過ExternalInterface.call直接調(diào)用js端公開的橋,并且通過ApplicationManager.initBridgeJsToFlex來接收js的回調(diào)函數(shù),這樣我們只需要添加swc這個包就可以使用js的所用功能。
其實我也不想使用eval的,eval是很消耗性能的,我的源代碼長度大概是8萬多,還好,沒見卡,我也只會這種方法,不要拿板磚拍我哦!有其他辦法的朋友告訴我哦!期望對某些人有幫助吧!