JavaScriptCore在实际项目中的使用的坑

备注:转载请说明出处http://blog.csdn.net/u011296699/article/details/50435559

JavaScriptCore在实际项目中的使用的坑

前言

写这篇文章目的在于备份自己解决这个坑的过程,便于以后忘了可以随时查看。

当然如果文章中有说的不正确的观点,欢迎交流,欢迎拍砖。

在刚开始使用JavaScriptCore与JS通信的时候,在网上搜了一推资料,这篇文章讲的不错,推荐看看UIWebView 中JavaScript 与 Objective-C 通信

文章里说了JS与OC在各个ios系统版本通信的机制

因为我的项目支持ios7+,我采用了JavaScriptCore框架实现的OC与JS的通信
先说说我在实际项目开发中遇到的坑。具体问题可以看这篇文章的说明:使用javascriptcore 框架后,UIWebView中页面跳转后,用JSExport绑定的方法无法调用

对遇到的技术问题我的解题思路是:

  • 了解原理--分析原因--对症下药

在分析原因之前我们先了解下浏览器加载渲染网页的过程

  • 1.用户输入网址(假设是个html页面,并且是第一次访问),浏览器向服务器发出请求,服务器返回html文件;
  • 2.浏览器开始载入html代码,发现<head>标签内有一个<link>标签引用外部CSS文件;
  • 3.浏览器又发出CSS文件的请求,服务器返回这个CSS文件;
  • 4.浏览器继续载入html中<body>部分的代码,并且CSS文件已经拿到手了,可以开始渲染页面了;
  • 5.浏览器在代码中发现一个<img>标签引用了一张图片,向服务器发出请求。此时浏览器不会等到图片下载完,而是继续渲染后面的代码;
  • 6.服务器返回图片文件,由于图片占用了一定面积,影响了后面段落的排布,因此浏览器需要回过头来重新渲染这部分代码;
  • 7.浏览器发现了一个包含一行Javascript代码的<script>标签,赶快运行它;
  • 8.Javascript脚本执行了这条语句,它命令浏览器隐藏掉代码中的某个<div> (style.display=”none”)。杯具啊,突然就少了这么一个元素,浏览器不得不重新渲染这部分代码;
  • 9.终于等到了</html>的到来,浏览器泪流满面……
  • 10.等等,还没完,用户点了一下界面中的“换肤”按钮,Javascript让浏览器换了一下<link>标签的CSS路径;
  • 11.浏览器召集了在座的各位<div><span><ul><li>们,“大伙儿收拾收拾行李,咱得重新来过……”,浏览器向服务器请求了新的CSS文件,重新渲染页面。
UIWebView三个代理被调用的时机
  • (BOOL)webView:(UIWebView )webView shouldStartLoadWithRequest:(NSURLRequest )request navigationType:(UIWebViewNavigationType)navigationType
  • (void)webViewDidStartLoad:(UIWebView *)webView
  • (void)webViewDidFinishLoad:(UIWebView *)webView

根据官网的文档说明

  • shouldStartLoadWithRequest:Sent before a web view begins loading a frame
  • webViewDidStartLoad:Sent after a web view starts loading a frame.
  • webViewDidFinishLoad:Sent after a web view finishes loading a frame

对浏览器(UIWebView)加载和渲染Html有了感性认识后,我们再了解下JavaScriptCore框架是怎么把OC的对象暴露让JS调用的。我的理解是通过JSExport协议把对象注册为暴露对象,JavaScriptCore框架把这个暴露对象转化成JS环境的对象,等着在OC获取到web的JS上下文后,把转化好的OC对象注入到JS上下文(也就是JavaScriptContent),感觉有点类似往web的DOM里面注入JS一样。通常的做法是通过KVC
获取到UIWebView的javaScriptContext,再往javaScriptContext里注入OC对象(由JavaSciptCore转换OC的对象)。

分析原因

了解了原理,我们来看下我遇到的坑的原因。当在html进行页面跳转的时候,JS调用OC对象出现undefined,很明显就是在JS调用OC对象时,还没有注入成功。

那么问题来了!

  • 第一:我们应该在什么时候正确的注入OC对象?
  • 第二:获取javaScriptContext的方法真的就只能是通过KVC获取吗?

我们先来看看第一个问题,在网上一搜,基本上都是说在
- (void)webViewDidFinishLoad:(UIWebView *)webView
方法里注入OC对象
jsContext[@”nativeApis”] = nativeApis;
为什么?据网上的说法是因为如果不在webViewDidFinishLoad这个方法里面注入的话,注入的对象很有可能会被销毁。

我们试试这个办法,很是奇怪,为什么在html跳转的时候就不行呢,我们在webViewDidFinishLoad方法里往JavaScriptContext注入OC对象,应该是注入成功了啊!!怎么在html跳转的时候,调用OC的对象就不存在呢?

分析原因,原来是webViewDidFinishLoad这个方法是在web的window.onload以后才调用(也就是html所有的资源已经加载和渲染结束后),这就明了了,在JS调用OC的对象时,还没有注入。

对这个问题我的理解是:应该在UIWebView创建好javaScripContext后注入。在UIWebView解析渲染Html标签之前,注入OC的对象,那么第一个问题就解决了。

ok,沿着这个思路分析下去,只要我们找到了在javaScriptContext创建成功后就注入OC的办法,这个坑应该就可以搞定了。

但是UIWebView并没有给我们webView:didCreateJavaScriptContext:forFrame:相关的代理接口,让我们在JavaScriptContext创建好后,返回给我们JSContext。现在获取JSContext的方法貌似网上都是说的通过KVC来获取。但是在UIWebViewDelegate的三个代理接口获取jsContext注入native对象时机又不对,这怎么解决?

带着没有解决不了的问题的心里,在网上找了一推资料,最后还是在强大的StackoverFlow找到了答案。

获取UIWebView的JSContext,还有其它的方法。

解决方案

看了这个文章,我遇到这个坑解决了:Why use JavaScriptCore in iOS7 if it can’t access a UIWebView’s runtime?

我们来看看他实现的原理:
通过获取App中创建的所有的UIWebView,使用UIWebView的stringByEvaluatingJavaScriptFromString方法,以UIWebView的hash值生成一个字符,往UIWebView的JavaScriptContext里设置一个标识。在等着监听到JavaScriptContext创建好后,获取当前JavaScriptContext中的这个标识和当前的UIWebView的hash值是否一致,如果一致当前的JavaScriptContext就是和当前的UIWebView是一致的,再把这个消息回调给相应的委托者,在- (void)webView:(UIWebView *)webView didCreateJavaScriptContext:(JSContext*) ctx这个代理回调方法里面把实现了JSExport协议的对象注入到JSContext里面。

到此我们总结下JS与OC通信在实际运用中的关键点:

  • 1、有效的获取当前UIWebView加载的内容的javaScriptContext
  • 2、在UIWebView开始解析html之前(或说JavaScriptContext已经创建好后)注入Native对象

参考文章

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值