美文网首页
UIWebview使用jsContext获取上下文失效

UIWebview使用jsContext获取上下文失效

作者: 幽玄727 | 来源:发表于2022-03-31 17:52 被阅读0次

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

    因为我的项目支持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,还有其它的方法。

    解决方案
    http://stackoverflow.com/questions/18920536/why-use-javascriptcore-in-ios7-if-it-cant-access-a-uiwebviews-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对象

    相关文章

      网友评论

          本文标题:UIWebview使用jsContext获取上下文失效

          本文链接:https://www.haomeiwen.com/subject/olpnjrtx.html