【问题标题】:UIWebView stringByEvaluatingJavaScriptFromString in backgroundUIWebView stringByEvaluatingJavaScriptFromString 在后台
【发布时间】:2012-02-08 15:45:40
【问题描述】:

在 iOS 应用程序中,我使用 stringByEvaluatingJavaScriptFromStringUIWebView 上运行一个相当大的脚本(就 JavaScript 字符串的长度而言很大)。调用 javascript 后会出现短暂的停顿,导致屏幕上的其他元素暂停片刻。

将 javascript 调用置于使用 self performSelectorInBackground 在后台调用的函数中会中断应用程序。是否有一种安全的方法可以在后台线程上调用 run this 或以其他方式防止界面暂停?

【问题讨论】:

    标签: objective-c ios uiwebview


    【解决方案1】:

    不,Webviews 和 Webkit JavaScript 引擎都是单线程的,不能在后台线程上使用。

    更好的选择是将 JavaScript 拆分为离散的执行块并使用 JavaScript 计时器将它们流水线化,如下所示(JS 代码,而不是 Obj-C):

    var i = 0;
    var operation = function() {
    
        switch (i) {
        case 0:
           //do first part of code
           break;
        case 1:
           //do second part of code
           break;
        case 2:
           //do third part of code
           break;
        etc...
        }
    
        //prepare to execute next block
        i++;
        if (i < TOTAL_PARTS) {
            setTimeout(operation, 0);
        }
    };
    operation();
    

    这将防止您的脚本在执行时阻止用户交互

    【讨论】:

    • 谢谢。这解决了一半的问题,甚至比必要的多一点。只需调用一个简短的计时器来完成所有工作的函数,就可以缩短暂停时间。如果我理解正确,一旦stringByEvaluatingJavaScriptFromString函数中的脚本执行,主线程就可以继续,所以js计时器调用的函数可以独立运行。仅在使用大字符串调用 stringByEvaluatingJavaScriptFromString 时仍有轻微的停顿。也许像 obj-c 方面的这种方法可以缓解这种情况。
    • 除了将整个脚本作为字符串传递之外,您还可以将其作为脚本标记附加到页面的 html 中,然后通过使用 stringByEvaluatingJavaScriptFromString 调用函数来触发它。
    • 这似乎有同样的改进,但仍然不是 100%。
    • 您是否尝试过像我最初建议的那样使用计时器将脚本分成较短的部分?
    【解决方案2】:

    嗯,我也在做同样的事情。我不得不运行一个同步的 ajax 请求,它冻结了我的 UI。所以这就是我修复它的方法:

    __block NSString *message;
        dispatch_queue_t q = dispatch_queue_create("sign up Q", NULL);
        dispatch_async(q, ^{
            NSString *function = [[NSString alloc] initWithFormat: @"signup(\'%@\',\'%@\',\'%@\')",self.email.text,self.password.text,self.name.text];
    
            dispatch_async(dispatch_get_main_queue(), ^{
                NSString *result = [self.webView stringByEvaluatingJavaScriptFromString:function];
                NSLog(@"%@",result);
    
                if ([result isEqualToString:@"1"]) {
                    message = [NSString stringWithFormat:@"Welcome %@",self.name.text];
                    [self.activityIndicator stopAnimating];
                    [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
                }
    
                else {
                    message = [NSString stringWithFormat:@"%@ is a registered user",self.name.text];
                    [self.activityIndicator stopAnimating];
                    [UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
                }
    
                UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Message" message:message delegate:self cancelButtonTitle:@"Okay" otherButtonTitles: nil];
                [alertView show];
            });
        });
    

    逻辑很简单。转到一个新线程,然后从其中分派到主队列,然后执行 JS 工作,一切对我来说都像一个魅力......

    【讨论】:

      【解决方案3】:

      您对UIWebView 所做的任何事情都必须在主线程上完成。它是一个 UI 元素,所以这就是 performSelectorInBackground 破坏您的应用程序的原因。

      【讨论】:

        【解决方案4】:

        您可以尝试将该调用放入 NSOperation。由于您使用的是 UI 元素,因此请务必使用 [NSOperationQueue mainQueue]。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-04-14
          • 2015-07-15
          • 2012-03-29
          • 2014-08-26
          • 1970-01-01
          • 2011-08-21
          • 2012-01-30
          • 1970-01-01
          相关资源
          最近更新 更多