【问题标题】:iOS 8 JavaScript to UIWebView communication bugiOS 8 JavaScript 到 UIWebView 通信错误
【发布时间】:2014-11-16 23:54:54
【问题描述】:

iOS 8 中的新功能是一个错误,当发生多个 JavaScript 与本机代码通信时,该错误会阻止 UI。

原生到 Javascript 的通信是通过 UIWebViewstringByEvaluatingJavaScriptFromString,而 JavaScript 到原生的通信是通过自定义 URL 方案完成的。

如果我进行三个或更多 webview -> native 通信,前两个会立即发生,但后续的每次通信需要 10 秒。

有人遇到这种情况吗?

编辑:2014/09/23

具体来说,如果你连续对本机调用执行两次javascript,就会出现问题,

[self.webView stringByEvaluatingJavaScriptFromString:@"calliOS('aurlscheme://awebview?say=two'); calliOS('aurlscheme://awebview?say=cat');"];

其中 calliOS() 是一个 JavaScript 函数,用于 the iframe hack 通过 URL 方案调用本机代码。

如果您用直接调用“window.location”替换 iframe hack,

[self.webView stringByEvaluatingJavaScriptFromString:@"window.location='aurlscheme://awebview?say=two';window.location='aurlscheme://awebview?say=cat';"];

您丢失了除了最后一个 URL 方案请求。

总之,在对原生调用进行快速连续的 javascript 调用时,要么使用 iframe hack 并且 UI 冻结几秒钟,要么使用 window.location 方式并丢失所有内容,但最后一个请求。

【问题讨论】:

  • 我也对此感兴趣。你能得到自定义的 URL 方案吗?到目前为止,我没有在 iOS 8 上的 webView:shouldStartLoadWithRequest:navigationType 中收到通知。
  • 是的,它对我有用,虽然我使用application:application handleOpenURL:url 来捕获请求,解析 GET 查询字符串并将参数字典传递给相应的视图控制器。
  • @AlexanderCollins,请参阅下面的答案,它适用于 iOS 8。

标签: ios cocoa-touch ios8 uiwebview uikit


【解决方案1】:

这个解决方案对我有用。

每次 JavaScript 向原生代码、委托方法发送请求时,

- webView:shouldStartLoadWithRequest:navigationType:

必须在 JavaScript 发送新请求之前接收请求,否则我们会遇到各种错误。因此,我们在 JavaScript 中实现了一个缓存来保存待处理的请求,并且只有在收到前一个请求后才触发一个新的。

所以,解决办法是:

JavaScript

// We need an array (cache) to hold the pending request URLs.
var iOSRequestCache = [];

/**
 Add a new iOS request to the cache.
 @params (Dictionary) - The query parameters to send to the native code.
 */
function newiOSRequest(parameters) {
    // Make the full request string.
    var str = [];
    for (var p in parameters)
        if (parameters.hasOwnProperty(p)) {
            str.push(encodeURIComponent(p) + "=" + encodeURIComponent(parameters[p]));
        }
    var queryString = str.join("&");

    var request = 'myurlscheme://webview?' + queryString;

    // Add the new request to the cache.
    iOSRequestCache.push(request);

    console.log("Added new request: " + request);

    // If this new request is the only one in the cache, fire it off to the native side. Else, do nothing.
    if (iOSRequestCache.length == 1) {
        console.log("Fired request: " + request);
        window.location = iOSRequestCache[0];
    }
}

/**
 Called by the native side to notify that a request was processed and to procced with the next pending request, if any.
 */
function resolveiOSRequest(request) {
    console.log("Resolving request: " + request);
    // Search for the processed request in the cache and delete it.
    var requestIndex = iOSRequestCache.indexOf(request);
    if (requestIndex != -1) {
        iOSRequestCache.splice(requestIndex, 1);   // Remove the request from the array.
    }
    console.log("Deleting resolving request: " + request);

    if (iOSRequestCache.length >= 1) {
        console.log("Firing next request: " + request);
        window.location = iOSRequestCache[0];
    }

    console.log("Resolved request: " + request);
}

Objective-C(本机代码)

/*
 Called whenever a URL is requested inside the web view.
 */
- (BOOL)webView:(UIWebView *)inWeb shouldStartLoadWithRequest:(NSURLRequest *)inRequest navigationType:(UIWebViewNavigationType)inType
{
    // The URL Scheme we use for web view <-> native communication (we grab it from the .plist file of the project target.)
    NSString *URLScheme = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleURLTypes"][0][@"CFBundleURLSchemes"][0];

    // If we get a request from this URL scheme, we grab the parameters and take the appropriate action.
    if ([inRequest.URL.scheme isEqualToString:URLScheme]) {
        // Get the query parameters.
        NSMutableDictionary *params = [NSMutableDictionary dictionary];
        NSArray *pairs = [inRequest.URL.query componentsSeparatedByString:@"&"];
        for (NSString *pair in pairs)
        {
            NSArray *elements = [pair componentsSeparatedByString:@"="];
            NSString *key = elements[0];
            NSString *value = elements[1];
            key = [key stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
            value = [value stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
            [params setObject:value forKey:key];
        }

        // Here we notify the JavaScript that we received this communication so that the next one can be sent.
        NSString *javaScript = [NSString stringWithFormat:@"resolveiOSRequest('%@')", inRequest.URL.absoluteString];
        [self.webView stringByEvaluatingJavaScriptFromString:javaScript];

        // Call the method that handles our JavaScript to native requests.
        // HERE WE TAKE WHATEVER ACTION WE WANT WITH THE DICTIONARY THAT
        // THE JAVASCRIPT SENT US.
        [self handleWebViewMessage:params];
    }

    return YES;

}

用法:

newiOSRequest({
    animal               : "cat",
    favfood              : "gocat",
});

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多