从华为P30图片不显示谈WebView的资源加载模式

1,321 阅读4分钟

持续更新...

前言

在日常开发过程中,现在已经是第二次碰见华为手机在 APP 内置 WebView 上浏览图片出现图片不显示的问题了,在 wap 端是不存在这个问题的。还记得第一次碰见这个问题的时候是在与移动端开发人员探讨图片不显示 bug,排除了样式、图片因素和 js 干扰后,最后将矛头指向 WebView 加载问题,然后移动端开发人员在查阅 WebView 文档后指出是 WebView 模式引起的。作为一名专业的螺丝钉制造者,有必要将问题总结下来,让大家少踩坑~

WebView 的模式

  • Android@version < v5.0,默认是采用的 MIXED_CONTENT_ALWAYS_ALLOW 模式,即总是允许 WebView 同时加载 Https 和 Http;
  • Android@version >= v5.0,默认用 MIXED_CONTENT_NEVER_ALLOW 模式,即总是不允许 WebView 同时加载 Https 和 Http。

安全问题一直都是值得开发者探讨的问题,开发者本应对每个微小细节把关,保护好每一份数据。从 Android 5.0 开始,WebView 默认是采用的 MIXED_CONTENT_NEVER_ALLOW 模式,不再支持同时加载 Https 和 Http 混合模式。这主要处于对网络数据安全性的考虑,官方默认推荐使用 https。对于需要修改模型的,可以自行修改配置,不过实测在一些机型如华为 P30,WebView 使用混合 http 和 https 也不是万全之策,况且在规范上应尽量保持只用 http 或只用 https 为妙。

MIXED_CONTENT_NEVER_ALLOW

此模式要求 WebView 使用 https 加载资源。WebView 不允许一个安全的站点(https)去加载非安全的站点内容(http),比如,https 网页内容的图片是 http 链接。强烈建议 App 使用这种模式,因为这样更安全

MIXED_CONTENT_ALWAYS_ALLOW

允许 https 环境加载 http 资源,不安全。此模式下 WebView 是可以在一个安全的站点(Https)里加载非安全的站点内容(Http),这是 WebView 最不安全的操作模式,尽可能地不要使用这种模式。

MIXED_CONTENT_COMPATIBILITY_MODE

允许 https 环境兼容式阻塞加载 http 资源,较安全。此模式下,当涉及到混合式内容时,WebView 会尝试去兼容最新 Web 浏览器的风格。一些不安全的内容(Http)能被加载到一个安全的站点上(Https),而其他类型的内容将会被阻塞。这些内容的类型是被允许加载还是被阻塞可能会随着版本的不同而改变,并没有明确的定义。这种模式主要用于在 App 里面不能控制内容的渲染,但是又希望在一个安全的环境下运行。

实战情景

华为 P30 App 内置 WebView 环境加载图片不显示

  • 测试环境
    • 运行终端:app 内置 WebView 环境
    • 手机型号:华为 P30
    • os:Android 9.0

对于这个图片加载不显示的问题做了几个尝试:

  • 在不修改原本加载资源的情况下,使用混合模式MIXED_CONTENT_COMPATIBILITY_MODE尝试加载资源
/*app启用WebView混合模式尝试解决图片不显示*/
webSetting.setBlockNetworkImage(false); // 不阻塞网络图片
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
  //允许混合(http,https)
  //webSetting.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
  webSetting.setMixedContentMode(WebSettings.MIXED_CONTENT_COMPATIBILITY_MODE);
}

在设置好混合模式后来了一波测试,本以为顺利搞掂,最后杀出一个程咬金说有些图片不够s,所以不见得能显示出来(;д;)。

啊不,是因为有些图片没有安全证书问题显示不出来。ヾ(o・ω・)ノ

蓦然回首,发现在这个 https 安全环境的问题还是挺多的呀,有些机型加载 http 资源没有安全证还是不让显示的。有安全证书保平安呐!混合模式并不是万能的!

回到问题,当 WebView 加载网页发生证书认证错误时,会调用 WebViewClient 类的 onReceivedSslError 方法,在这个方法里,可以点击源码看到 SslErrorHandler 中有两个主要的方法可以调用:

  • cancel():停止加载问题页面
  • proceed():忽略 SSL 证书错误,继续加载页面

如果不考虑证书安全,则可以这样忽略报错:

WebView.setWebViewClient(new WebViewClient() {
  @Override
  public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
  handler.proceed();//接受所有网站的证书
}
});

然后对于不显示的图片,只能让后台手动修改链接,改为对应的 https 什么的。

另外,在测试时发现 X5 内核的 WebView 中,是找不到 MIXED_CONTENT_ALWAYS_ALLOW 这些模式参数的,对此要手动判断是否 X5 并设值:

MIXED_CONTENT_NEVER_ALLOW = 0;
MIXED_CONTENT_ALWAYS_ALLOW = 1;
MIXED_CONTENT_COMPATIBILITY_MODE = 2;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
  // 混合模式
  webSetting.setMixedContentMode(2);
}

测试至此,本人对混合模式是持谨慎态度的,不太建议使用,除非迫不得已的情况吧。

从规范上来讲,要么全部使用 http 的资源,要么全部使用 https。

  • 最后推荐全部资源使用 https
  • 对于爱偷懒的我来讲(~ ̄ ▽  ̄)~ ,更喜欢这种格式书写://www.xxx.com/

参考文档