当前位置: 代码迷 >> JavaScript >> JS 与 AS 交互及效率对照
  详细解决方案

JS 与 AS 交互及效率对照

热度:174   发布时间:2012-11-22 00:16:41.0
JS 与 AS 交互及效率对比

?

? ? 想象下有这样的业务场景,我用flash实现了文件的上传,上传完之后,我们可能需要在页面上弹个提示框告诉用户上传结果。我们不可能把所有东西都放在flash里完成,因为那样flash的文件会加大,而且也不够灵活,因为flash是需要编译后才能跑的,所以我们最好把能用js实现的都剥离出来,让flash只做一些核心的功能。这样,我们就需要用as去调用js。同样的,如果js操作完了想通知到flash,就需要用js去调用flash中的as方法。此时可以看作as是js增强的一部分。

???? 不管是as调用js,还是js调用as,其实都围绕ExternalInterface。ExternalInterface 类是外部 API,这是一个在 ActionScript 和 Flash Player 容器之间实现直接通信的应用程序编程接口。当然插入flash的html标签中<param name='allowScriptAccess' value ='always' /> 不能为never,如果设置成never一下所有都是空谈了,一般情况下都设置成always,它也可以接受具体的domain或者IP。

???? ExternalInterface有两个提供相互调用的静态方法:
     /**
      * @param functionName {String} 
      * @param closure {Function}
      * @usage 将 ActionScript 方法注册为可提供外部js调用。
      */
     addCallback(functionName:String, closure:Function):void 

     /**
      * @param functionName {String} 
      * @param arguments {Function} (optional)
      * @usage 调用页面上的js。
      */
     call(functionName:String, ... arguments):*
?

Example 1:
假如页面上有一个叫sayHi的js函数, 如下:
window.onload=function (){
     function sayHi(){alert('Hello JS')}
}
?
要在flash中调用这个函数,可以用两个办法:
navigateToURL(new URLRequest( 'javascript:sayHi()' ), '_self'); //实际上打开一个新窗口,只不过url是javascript:sayHi(),这是土鳖方法
ExternalInterface.call( 'sayHi');// 用到了ExternalInterface类,这种方法也是adobe推荐的做法
?
运行结果,发现两个都没法执行,这是因为flashplayer只能看到页面上全局的javascript,所以要想能让flash拿到这个js函数,必须暴露出来。修改代码在运行
window.onload=function (){
     function sayHi(){alert('Hello JS')}
     window['sayHi'] = sayHi;
}
?
代码成功运行了,于是,我们可以总结出一点:提供给flash调用的js函数必须是全局的,获取寄宿在全局对象上的。
? ? ?有时候我们只用到了flash的功能,而没用到它的UI所以想把flash隐藏,比如做一个即时通讯的东西,只有通讯部分用到了flash, 当尝试把flash设置成display:none的时候,发现js与as根本无法相互调用,所以说如果需要隐藏flash,设置css是行不通的,可以设置它的宽高为1px来解决。

???? 如果是想在js中调用as方法,需要用ExternalInterface.addCallback注册as方法,让他暴露在flash实例上。比如flash中有这样的方法:
private function sayHi():void{
     Alert.show('Hi As');
}
External.addCallback('jsSayHi', sayHi);
?
这样就可以在js中执行jsSayHi这个代理方法,它会去执行sayHi这个as方法。jsSayHi这个代理方法会寄宿在flash dom元素上,作为dom元素的方法。
window.onload=function (){
     document.getElementById('flashId').sayHi();
}
?
测试一下,还是发现了问题,有时候能正常运行,但有时候会抛出错误:Uncaught TypeError: Object #<HTMLObjectElement> has no method 'jsSayHi',这是因为即使当前页面onload了,但是flash中的代码初始化还没运行完,所以还没有把代理方法注册到flash dom元素上。(查看实例3-jscallas_b.html)

???? 那么,flash有没有类似domready的时间点呢?查看好多资料没看到,但是可以模拟一个。我们认识当flash能正常调用页面上的js时,flash能正常跟js进行交互。我们在页面上定义一个flashready函数,让flash回调。
function flashready(){
     document.getElementById('flashId').jsSayHi(); 
     alert('flash is ready');    
}
?

ExternalInterface.call( 'flashready' );
?

? ? ?这样js对as的调用就能100%的成功了。

? ? ?对于静态资源往往会放在cdn上,比如有一台cdn域名是cdn.com,而主域名确是xxx.com,这样就出现了跨域的问题。浏览器的安全策略导致ajax不能正常的跨域请求,flash player的安全策略同样不允许flash跨域请求和调用。当遇到跨域时,flash player会抛出“安全沙箱冲突错误”。其实flash是支持跨域的,只不过要做一些设置。

???? 如果是跨域脚本执行,flash中有Secure.allowDomain(somedomain1, somedomain2)来允许制定的url请求,somedomain也可以是通配符"*",也就是不做源限制。如果flash的url是https的链接,则需要用allowInsecureDomain()来做沙箱桥,但如果当前html也是https,那不需要用它。尽量避免使用allowInsecureDomain会削弱https的安全性。但是在一些国产浏览器中,比如腾讯TT,遨游,360等浏览器中,经常会遇到第一次进来js能正常调用flash中的as,但是当刷新一次页面,发现调用不成功了。这是因为第二次访问的时候,flash被缓存到了本地,这些浏览器破坏了flash和浏览器的某种约定,所以导致他们不能相互调用。
???? 这个问题有两个做法:一、当flash文件很小时,用无缓存的方式解决,比如请求后面加随机数。二、延迟Flash的初始化功能。通过将Flash的ExternalInterface.addCallback初始化时间延后一些(比如500ms),就可以解决这个问题。
???? 顺便提一下,如果是flash中的http/socket等跨域请求,则需要一个叫crossdomain.xml的策略文件。这个文件放在服务器上,如果是跨域请求,在请求真实地址之前会去请求这个安全策略文件。
<cross-domain-policy>
<allow-access-from domain=”*.xxx.com”/>
<allow-access-from domain=”*.xxx.net”/>
<allow-access-from domain=”*.xxxcdn.com”/>
<allow-access-from domain=”*.allyes.org”/>
</cross-domain-policy>
?

???? 有人可能会考虑js与as之间频繁的相互调用会带来性能问题。简单的做了下测试
测试代码如下:

function flashready(){
       var swf = document.getElementById('performance'), //方案1
       //var swf = {'test':function(){return 1}}, //方案2
			test = swf.test,               
			i = 10000;    
							
	   var start = + new Date;
	   while(i--){
			test.call(swf);
	   }
	   alert(+ new Date - start);                 
}
?
flashready是一个全局的,提供给flash初始完回调的函数。注意代码中把获取flash dom放到一个变量里,因为dom操作本身开销大,如果不这么做会影响精确性。实验结果如下:
测试项目(测10次取平均值) chrome 19 firefox 9 ie6 ie7 ie8 ie9
js调用as 10000次耗时(方案1) 1747ms 1083ms 360ms 557ms 485ms 401ms
js调用js 10000次耗时(方案2) 1ms 4ms 15ms 31ms 15ms 0ms

观察结果,虽然说js与as执行的执行要比js与js之间慢很多很多,但是勉强还是能接受的,即使是最慢的chrome,平均调用一次也只需要0.17ms,但还是要尽量减少相互调用的次数,就像减少http请求一样。

综上所述,js与as的安全交互必须满足:
  1. <param name='allowScriptAccess' value ='always' />???
  2. flash不能隐藏(display:none)??
  3. 等被调用方初始化完成再去调用,as中可以用ExtercalInterface.call('flashready')来告知初始化完成
  4. 跨域执行,必须在flash中设置Secure.allowDomain或者Secure.allowInsecureDomain
最后做下小广告,欢迎加JS&PHP群(166643291)一起进步。

?

  相关解决方案