美文网首页留着看
iOS之UIWebView的坑

iOS之UIWebView的坑

作者: eva_lilizhang | 来源:发表于2017-06-29 19:01 被阅读2056次

iOS之UIWebView的坑

背景:我厂这个版本在开发航拍图,是一个H5页面,但是就是一个H5页面竟然引起了APP的crash。在当前航拍图页面,app进入后台,会直接crash。挂在gpus_returnnotpermittedkillclient
这个bug是iOS10.8以后就有的,特大好消息就是,iOS11苹果就把这个bug修复了,所以iOS11不会出现。

原因是,iOS进入后台禁止调用webGL,一帧都不允许,而这个H5还在调用,所以直接崩溃。而且游戏里面使用的openGL,在进入后台的时候是直接停止的

E04598C5-7BAD-4A29-B686-39FC80115D91.f9475027fb52451fa7a58f23c72e3c3a.png

失败方案:
参考链接:其中一个解决方案
[https://forums.developer.apple.com/thread/30896]

1.webView loadRequest 之间调用,set bEnable NO,但是会引起webView上整张图都加载不出来。
2.进入后台的时候 set bEnable NO,进入前台 set bEnable YES,但是依然没有什么用。

typedef void (*CallFuc)(id, SEL, BOOL);
typedef BOOL (*GetFuc)(id, SEL);
-(BOOL)webView:(UIWebView*)view enableGL:(BOOL)bEnable
{
  BOOL bRet = NO;
  do
  {
  Ivar internalVar = class_getInstanceVariable([view class], "_internal");
  if (!internalVar)
  {
  NSLog(@"enable GL _internal invalid!");
  break;
  }
   
  UIWebViewInternal* internalObj = object_getIvar(view, internalVar);
  Ivar browserVar = class_getInstanceVariable(object_getClass(internalObj), "browserView");
  if (!browserVar)
  {
  NSLog(@"enable GL browserView invalid!");
  break;
  }
   
  id webbrowser = object_getIvar(internalObj, browserVar);
  Ivar webViewVar = class_getInstanceVariable(object_getClass(webbrowser), "_webView");
  if (!webViewVar)
  {
  NSLog(@"enable GL _webView invalid!");
  break;
  }
   
  id webView = object_getIvar(webbrowser, webViewVar);
  if (!webView)
  {
  NSLog(@"enable GL webView obj nil!");
  }
   
  if(object_getClass(webView) != NSClassFromString(@"WebView"))
  {
  NSLog(@"enable GL webView not WebView!");
  break;
  }
   
  SEL selector = NSSelectorFromString(@"_setWebGLEnabled:");
  IMP impSet = [webView methodForSelector:selector];
  CallFuc func = (CallFuc)impSet;
  func(webView, selector, bEnable);
   
  SEL selectorGet = NSSelectorFromString(@"_webGLEnabled");
  IMP impGet = [webView methodForSelector:selectorGet];
  GetFuc funcGet = (GetFuc)impGet;
  BOOL val = funcGet(webView, selector);
   
  bRet = (val == bEnable);
   
  }while(NO);
   
  return bRet;
}

最终解决方案有两个:
1.H5协议,暴露出可以停止绘制和重新绘制的方法,app端在进前台和后台的时候调用。
2.用WKWebView把UIWebView替换掉。
参考文档:[https://developer.apple.com/documentation/webkit/wkwebview]

一些我用到的WKWebView代理如下,详细的参考官方文档;

- (void)loadWebViewWithURL:(NSString *)urlString
{
    NSURL *url = [self rtCreateWebViewURL:urlString];
    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
    [request setValue:[AIFAppContext sharedInstance].appName forHTTPHeaderField:@"X-AJK-APP"];
    NSString *cv = [[AJKSettingsManager sharedInstance] getAppVersion]?[[AJKSettingsManager sharedInstance] getAppVersion]:@"";
    [request setValue:cv forHTTPHeaderField:@"X-AJK-CV"];
    [self.webView loadRequest:request];
}

- (NSURL *)rtCreateWebViewURL:(NSString *)urlString
{
    return [NSURL URLWithString:urlString];
}

pragma mark - WKNavigationDelegate
- (void)webView:(WKWebView *)webView didStartProvisionalNavigation:(WKNavigation *)navigation{
    [self routeHandleWithRequest:webView.URL];
}

- (void)webView:(WKWebView *)webView didFinishNavigation:(WKNavigation *)navigation{
    weakify(self);
    [webView evaluateJavaScript:@"document.title" completionHandler:^(id _Nullable obj, NSError * _Nullable error) {
        if (obj) {
            strongify(self);
            NSString *title = [NSString stringWithFormat:@"%@",obj];
            self.title = title;
        }
    }];
    NSURL *requestURL = webView.URL;
    NSString *host = [requestURL host];
    if ([host rangeOfString:@"anjuke.com" options:NSCaseInsensitiveSearch].location != NSNotFound
        || [host rangeOfString:@"58.com" options:NSCaseInsensitiveSearch].location != NSNotFound
        || [host rangeOfString:@"anjuke.test" options:NSCaseInsensitiveSearch].location != NSNotFound) {
        weakify(self);
        [webView evaluateJavaScript:[NSString stringWithFormat:@"getShareContents()"] completionHandler:^(id _Nullable obj, NSError * _Nullable error) {
            if (obj) {
                strongify(self);
                NSString *str = [NSString stringWithFormat:@"%@",obj];
                if (str.length > 0) {
                    NSArray *shares = [str JSONValue];
                    if ([shares isKindOfClass:[NSArray class]] && shares.count > 0) {
                        [self setShareDicFromArray:shares];
                        [self showShareBtn];
                    }
                }
            }
        }];
    }

    [self.activity removeFromSuperview];
}

- (void)webView:(WKWebView *)webView didFailNavigation:(WKNavigation *)navigation withError:(NSError *)error{
    if (![self checkNetwork]) {
        [self.errorView showInView:self.view type:RTMessageTypeNoNetWork target:self action:@selector(reloadWebView)];
    }
    [self.activity removeFromSuperview];
}

- (void)webView:(WKWebView *)webView didFailProvisionalNavigation:(WKNavigation *)navigation withError:(NSError *)error{
    
}



pragma mark - WKScriptMessageHandler
- (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message{
    
}

html的alert不弹的问题

需要实现WKWebView的UIDelegate

#pragma mark - WKUIDelegate
- (void)webView:(WKWebView *)webView runJavaScriptAlertPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(void))completionHandler{
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"提示" message:message?:@"" preferredStyle:UIAlertControllerStyleAlert];
    [alertController addAction:([UIAlertAction actionWithTitle:@"确认" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        completionHandler();
    }])];
    [self presentViewController:alertController animated:YES completion:nil];
    
}

- (void)webView:(WKWebView *)webView runJavaScriptConfirmPanelWithMessage:(NSString *)message initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(BOOL))completionHandler{
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:@"提示" message:message?:@"" preferredStyle:UIAlertControllerStyleAlert];
    [alertController addAction:([UIAlertAction actionWithTitle:@"取消" style:UIAlertActionStyleCancel handler:^(UIAlertAction * _Nonnull action) {
        completionHandler(NO);
    }])];
    [alertController addAction:([UIAlertAction actionWithTitle:@"确认" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        completionHandler(YES);
    }])];
    [self presentViewController:alertController animated:YES completion:nil];
}

- (void)webView:(WKWebView *)webView runJavaScriptTextInputPanelWithPrompt:(NSString *)prompt defaultText:(NSString *)defaultText initiatedByFrame:(WKFrameInfo *)frame completionHandler:(void (^)(NSString * _Nullable))completionHandler{
    UIAlertController *alertController = [UIAlertController alertControllerWithTitle:prompt message:@"" preferredStyle:UIAlertControllerStyleAlert];
    [alertController addTextFieldWithConfigurationHandler:^(UITextField * _Nonnull textField) {
        textField.text = defaultText;
    }];
    [alertController addAction:([UIAlertAction actionWithTitle:@"完成" style:UIAlertActionStyleDefault handler:^(UIAlertAction * _Nonnull action) {
        completionHandler(alertController.textFields[0].text?:@"");
    }])];
    
    
    [self presentViewController:alertController animated:YES completion:nil];
}

所以下版本计划将原本app里面的ADViewController的UIWebView替换成WKWebView,希望没有什么新坑!

相关文章

网友评论

  • 陆宝宝:你好,代码看不太懂,是swifit吗?求demo
  • lazy_boy_coder:你这个"H5协议,暴露出可以停止绘制和重新绘制的方法,app端在进前台和后台的时候调用。"是什么意思,有具体资料可以参考嘛?
  • ldldlkdldld:貌似iOS 11并没有解决,只是崩溃堆栈换了而已。
  • 5c9a2d427760:自己研究下webview的源代码,就知道切后台停绘制是多么不靠谱的方案了
    HEYRIX:那还有更好的方法么
  • 5c9a2d427760:图片加载不出来这个,自己再仔细仔细验证。
  • 黑化肥发灰:小伙子可以啊
    黑化肥发灰:@Eva_dev 顺便说一句,头像是你女朋友吧,真好看!
    黑化肥发灰:@Eva_dev 又是要上线的时候出问题:sweat: :smiley:
    eva_lilizhang:@黑化肥发灰 :stuck_out_tongue_winking_eye:谢谢打赏,上午我自己乱点点出来的,然而今天上线,搞了一下午终于解决了

本文标题:iOS之UIWebView的坑

本文链接:https://www.haomeiwen.com/subject/rbkvcxtx.html