UIWebView(Javascript) 原理

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:something
function 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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值