Native 调用 JS
在 Native中执行 JS语句非常简单, JS作为脚本语言它的执行需要解释器的存在,即浏览器,所以 UIWebView作为浏览器控件,提供了 native调用 JS的对象方法:
//script 是要执行的 JS 语句 //返回值为 JS 执行结果,如果 JS 执行失败则返回 nil,如果 JS 执行没有返回值,则返回值为空字符串 - (nullable NSString *)stringByEvaluatingJavaScriptFromString:(NSString *)script;
注意:
1 该方法是一个同步方法,可能会阻塞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. 使用WKWebView的evaluateJavaScript:completionHandler:代替这个方法。
2. 自定义一个延迟执行alert的方法来防止阻塞,然后我们调用自定义的alert方法。同理,耗时较长的js方法也可以放到setTimeout中。
function asyncAlert(content) { setTimeout(function(){ alert(content); },1); }
JS调用Native
//return YES,webView 就会加载这个链接;return NO,webView 就不会加载这个连接。我们就在这个拦截的代理方法中处理自己的URL。 #pragma mark - UIWebViewDelegate - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
URL请求方式一
js修通过改document的location;
URL请求方式二
新建一个看不见的iFrame,修改它的 src;
两种方式都会触发回调 webView的 shouldStartLoadWithRequest,request的url就是新赋值的 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; }
// 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; }