编辑:解决方案是在收到JavaScript消息时从委托方法webView:shouldStartLoadWithRequest:navigationType:
返回NO,这解决了所有版本中的所有内容。
在iOS 8.3中,UIWebView
内的JavaScript对本机(Obj-C或Swift)通信的行为已发生变化。
我想说我想从JS发送一些消息到本地代码 1 :
window.open('myurlscheme://webview?message=1');
window.open('myurlscheme://webview?message=2');
window.open('myurlscheme://webview?message=3');
window.open('myurlscheme://webview?message=4');
我使用此委托方法来捕获本机代码中的消息:
- (BOOL)webView:(UIWebView *)inWeb shouldStartLoadWithRequest:(NSURLRequest *)inRequest navigationType:(UIWebViewNavigationType)inType {
if ([inRequest.URL.scheme isEqualToString:@"myurlscheme"]) {
NSLog(@"%@", inRequest.URL.absoluteString);
}
return YES;
}
我在Xcode控制台中获得以下内容:
15:19:56.387 TestJSObjCComms[55731:766108] myurlscheme://webview?meaning=0
15:19:56.404 TestJSObjCComms[55731:766108] myurlscheme://webview?meaning=1
16:20:06.300 TestJSObjCComms[57967:819448] void SendDelegateMessage(NSInvocation *): delegate (webView:decidePolicyForNavigationAction:request:frame:decisionListener:) failed to return after waiting 10 seconds. main run loop mode: kCFRunLoopDefaultMode
15:20:06.407 TestJSObjCComms[55731:766108] myurlscheme://webview?meaning=2
15:20:16.409 TestJSObjCComms[55731:766108] myurlscheme://webview?meaning=3
15:20:26.411 TestJSObjCComms[55731:766108] myurlscheme://webview?meaning=4
注意每条消息的时间戳。前两个是直接进入的,但是连续的消息以正好以10秒的间隔进入。在最后三条消息到达的30秒内,用户界面冻结!控制台中的错误对应于主线程(UI线程)被某些代码阻止10秒钟,超过此时间限制后系统将其终止。
我猜测UIWebView在iOS 8.3上的自己的线程中执行,但我还不能理解这一点。根据{{3}},UIWebView
允许JavaScript执行最多10秒。所以我猜JavaScript是以某种方式冻结的,只有当它被杀死时,消息才能通过本机代码。
另外,我认为window.open(myURL)
比window.location = myURL
工作得更好的原因是因为它必须创建一个新的线程或执行环境,这允许它在没有压缩的情况下将其消息发送到本机代码之前的消息在它们到达之前。
1 我使用window.open(myUrl)
代替window.location = myUrl
,这只允许最后一条消息到达本机代码(有关不同方法的详细信息,请参阅附录1)可用)。
编辑1
不好了。 setTimeout
再次来到" rescue"如果您将后续消息包含在setTimeout
中且约延迟50毫秒,则测试用例3似乎适用于iOS 8.3,8.1和7.0。
var buffer = [];
function doRequest(url) {
buffer.push(url);
if (buffer.length === 1) {
window.location = url;
}
}
function resolveRequest(request) {
var index = buffer.indexOf(request);
if (index != -1) {
buffer.splice(index, 1);
}
if (buffer.length) {
setTimeout(function() {
window.location = buffer[0];
}, 50);
}
}
但我正在寻找一种不会引入黑客和延误的永久性解决方案。走这条路就像在沙子的柱子上建立你的应用程序。
附录1:UIWebView的JavaScript< - >指南原生沟通。
测试用例
1)通过window.location = myURL
向本机代码发送两条或更多条连续消息(使用window.location.href = myURL
,window.location.replace(myURL)
等等)。
2)通过window.open(myURL)
或window.open(myURL, ‘_blank’)
发送两封或多封连续消息,或者创建以下代码之一并设置其src
属性:<iframe>
,{{1 }}。请注意,以下代码无效:<embed>
,<audio>
,<video>
,<script>
。
3)使用请求缓冲区发送两个或多个连续消息。这确保仅在当前请求已调用<style>
委托方法UIWebView
时调用下一个请求。
示例JavaScript:
webView:shouldStartLoadWithRequest:navigationType:
Native Obj-C:
var buffer = [];
function doRequest(url) {
buffer.push(url);
if (buffer.length === 1) {
window.location = url;
}
}
function resolveRequest(request) {
var index = buffer.indexOf(request);
if (index != -1) {
buffer.splice(index, 1);
}
if (buffer.length) {
window.location = buffer[0];
}
}
测试结果(使用iPhone 5模拟器)
iOS 7.1
案例1:只有最后一条消息到达。
案例2:消息按顺序到达,每次消息延迟约四分之一秒(即不太糟糕)。
案例3:消息按顺序到达,每次消息延迟约四分之一秒(即不太糟糕)。
iOS 8.1
案例1:与iOS 7.1相同
案例2:前两个通常是直接到达,但是下一个以10秒的间隔进入(同时, UI冻结)。
案例3:与iOS 7.1相同
iOS 8.3
案例1:与iOS 7.1相同
案例2:与iOS 8.1相同
案例3:前两个通常是直接到达,但下一个以10秒的间隔进入(同时, UI冻结)。