編集: 解決策は、JavaScript メッセージが受信されたときにデリゲート メソッドから NO を返すことでしたwebView:shouldStartLoadWithRequest:navigationType:
。これにより、すべてのバージョンですべてが解決されました。
UIWebView
iOS 8.3 では、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
各メッセージのタイムスタンプに注意してください。最初の 2 つはすぐに届きますが、連続するメッセージはちょうど 10 秒間隔で届きます。最後の 3 つのメッセージが到着する 30 秒間に、UI がフリーズします。コンソールのエラーは、メイン スレッド (UI スレッド) がなんらかのコードによって 10 秒間ブロックされ、この時間制限を超えた後にシステムによって強制終了されたことに対応しています。
UIWebView は iOS 8.3 の独自のスレッドで実行されると推測していますが、これについてはまだよくわかりません。この回答によると、 UIWebView
JavaScript は最大 10 秒間実行できます。したがって、JavaScript が何らかの理由でフリーズしていると思います。それが強制終了された場合にのみ、メッセージはネイティブ コードに到達します。
window.open(myURL)
また、よりうまく機能する理由は、新しいスレッドまたは実行環境を作成する必要があるためだと思いwindow.location = myURL
ます。これにより、以前のメッセージが到着する前につぶすことなく、メッセージをネイティブ コードに送信できます。
1window.open(myUrl)
の代わりにを使用しますwindow.location = myUrl
。これは、最後のメッセージがネイティブ コードに到達することのみを許可します (使用可能なさまざまな方法の詳細については、付録 1 を参照してください)。
編集1
ああいいえ。setTimeout
再び「救助」に来ます。setTimeout
テスト ケース 3 は、後続のメッセージを約50 ミリ秒の遅延でラップすると、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 <-> ネイティブ通信のガイド。
テストケース
window.location = myURL
1)ネイティブ コードを介して 2 つ以上の連続したメッセージを送信します ( window.location.href = myURL
、window.location.replace(myURL)
などを使用しても同等です)。
2)次のタグのいずれかを作成し、その属性を設定することによりwindow.open(myURL)
、 またはを介して 2 つ以上の連続したメッセージを送信します: 、。次のタグは機能しないことに注意してください: 、、、。window.open(myURL, ‘_blank’)
src
<iframe>
<embed>
<audio>
<video>
<script>
<style>
3) リクエストのバッファを使用して、2 つ以上の連続したメッセージを送信します。UIWebView
これにより、現在のリクエストがすでにデリゲート メソッドを呼び出している場合にのみ、次のリクエストが呼び出されるようになりwebView:shouldStartLoadWithRequest:navigationType:
ます。
JavaScript の例:
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];
}
}
ネイティブ Obj-C:
- (BOOL)webView:(UIWebView *)inWeb shouldStartLoadWithRequest:(NSURLRequest *)inRequest navigationType:(UIWebViewNavigationType)inType {
if ([inRequest.URL.scheme isEqualToString:@"myurlscheme"]) {
NSString *javaScript = [NSString stringWithFormat:@"resolveRequest('%@')", inRequest.URL.absoluteString];
[self.webView stringByEvaluatingJavaScriptFromString:javaScript];
NSLog(@"%@", inRequest.URL.absoluteString);
}
return YES;
}
テスト結果 (iPhone 5 シミュレーターを使用)
iOS7.1
ケース 1: 最後のメッセージのみが到着します。
ケース 2: メッセージが順番に到着し、それぞれの間に約 1/4 秒の遅延があります (つまり、それほど悪くはありません)。
ケース 3: メッセージが順番に到着し、それぞれの間に約 1/4 秒の遅延があります (つまり、それほど悪くはありません)。
iOS 8.1
ケース 1: iOS 7.1 と同じ
ケース 2: 通常、最初の 2 つはすぐに到着しますが、次の 2 つは 10 秒間隔で到着します (その間、UI はフリーズします)。
ケース 3: iOS 7.1 と同じ
iOS 8.3
ケース 1: iOS 7.1 と同じ
ケース 2: iOS 8.1 と同じ
ケース 3: 通常、最初の 2 つはすぐに到着しますが、次の 2 つは 10 秒間隔で到着します (その間、UI はフリーズします)。