UIWebView(Javascript) 原理

来源:互联网 发布:Linux中文系统 编辑:程序博客网 时间:2024/06/07 11:22

Native 调用 JS

Native中执行 JS语句非常简单, JS作为脚本语言它的执行需要解释器的存在,即浏览器,所以 UIWebView作为浏览器控件,提供了 native调用 JS的对象方法:

//script 是要执行的 JS 语句//返回值为 JS 执行结果,如果 JS 执行失败则返回 nil,如果 JS 执行没有返回值,则返回值为空字符串- (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;


注意:


该方法是一个同步方法,可能会阻塞UI,由于 javascript是单线程的原因,会阻塞原有 js代码的执行。这里我们的解决办法是在 js端用defer iframe的插入延后执行。


2该方法必须是主线程中执行,而主线程的执行时间过长就会 block UI的更新。所以我们应该尽量让 stringByEvaluatingJavaScriptFromString方法执行的时间短。


调用时机
- (void)webViewDidFinishLoad:(UIWebView*)webView{    NSString* str = [self.webView stringByEvaluatingJavaScriptFromString:@"functionXXX()"];}

例子

js弹出alert的时候卡顿alert也会阻塞界面,等待用户响应,而stringByEvaluatingJavaScriptFromString又会等待js执行完毕返回。这就造成了死锁。


解决方案

1. 使用WKWebViewevaluateJavaScript:completionHandler:代替这个方法。


2. 自定义一个延迟执行alert的方法来防止阻塞,然后我们调用自定义的alert方法。同理,耗时较长的js方法也可以放到setTimeout中。


function asyncAlert(content) {    setTimeout(function(){        alert(content);    },1);}


JS调用Native


JS发起一个假的URL请求,然后利用UIWebView的代理方法拦截这次请求,然后再做相应的处理。
//return YES,webView 就会加载这个链接;return NO,webView 就不会加载这个连接。我们就在这个拦截的代理方法中处理自己的URL。#pragma mark - UIWebViewDelegate- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType



URL请求方式一


js修通过改documentlocation;


URL请求方式二


新建一个看不见的iFrame,修改它的 src;


两种方式都会触发回调 webView shouldStartLoadWithRequest,requesturl就是新赋值的 location或者 src,上层截获这个 url的参数,对此分发即可。


参数以 JSON的形式传递,附加在 url之后,将 JSON进行了 Base64编码,可以保证 url中不会出现一些非法的字符。


根据scheme来区分是调用原生的方法还是正常的网页跳转。然后根据host参数来区分执行什么操作


注意: 


1. 该方法是异步调用。


2.document.location有一个很严重的问题,就是如果我们连续2 js native,连续2次改 document.location的话,在 native delegate方法中,只能截获后面那次请求,前一次请求由于很快被替换掉,所以被忽略掉了。为避免多次请求,被替换覆盖的问题,JS端用iFrame的方式替代用document.location的方式


3. 如果当前网页正使用window.location.href加载网页的同时,调用window.location.href去调用OC原生方法,会导致加载网页的操作被取消掉。


JS 代码:

   var messagingIframe;    messagingIframe = document.createElement('iframe');    messagingIframe.style.display = 'none';    document.documentElement.appendChild(messagingIframe);    function TestIOSJS(){        messagingIframe.src = "ios/test/click";    };

当触发上面的JS时,webview会收到下面的回调

- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType{    NSString *url = request.URL.absoluteString;    if([url hasSuffix:@"ios/test/click"]){        //do something you want        return NO;    }    return YES;}

以下是 PhoneGap 相关调用的示例代码:
// Javascript 语言// 通知 iPhone UIWebView 加载 url 对应的资源// url 的格式为: gap:somethingfunction loadURL(url) {    var iFrame;    iFrame = document.createElement("iframe");    iFrame.setAttribute("src", url);    iFrame.setAttribute("style", "display:none;");    iFrame.setAttribute("height", "0px");    iFrame.setAttribute("width", "0px");    iFrame.setAttribute("frameborder", "0");    document.body.appendChild(iFrame);    // 发起请求后这个 iFrame 就没用了,所以把它从 dom 上移除掉    iFrame.parentNode.removeChild(iFrame);    iFrame = null;}

示例代码:
https://github.com/marcuswestin/WebViewJavascriptBridge

0 0