[TOC]

简介

不可避免地愈来愈多的前端页面被嵌入到Android App中,对我们系统稳定性的维护工作和分析工作带来一定的挑战和不小的精力消耗。有时候,具备对WebView的基本排查、分析能力可以很好地缩短问题定位周期,减轻精力消耗和心智负载。本篇从客户端系统层的角度来阐述如何初步地粗略排查Android WebView白屏问题,提供一个定位大概问题的思路。

注意!本文讲的是WebView加载失败、保持白屏的状态,不是短时白屏、短暂白屏的情况(这通常取决于网络和前端实现的性能)。

白屏原理及成因

WebView在一块默认的空白的、颜色为白色的画布(Canvas)上绘制网页,当网页绘制工作的前两步——加载、解析等出现问题时,或者在DOM解析、JS执行时,或WebView绘制时出现问题,此时无法绘制网页,仅留下原始的白色画布(也有纯黑色的画布,好像之前在U4内核碰见过)。就是白屏问题的形成原因。除短暂的白屏外,长期保持白屏的状态可能来自于网络、前端、业务逻辑以及WebView兼容性。

白屏问题总的来说分为两类。一类是网络请求和HTML/CSS/JS解析的因素,另一类是JS执行、WebView摆放、渲染的因素。

下图展示HTML基本加载流程。可以看到有一个空白期。注意,实际上空白期要更大些。因为后面的DOM解析和渲染仍然可能失败,这种情况下仍然是空白的——也就是本文讨论的一种非短时的白屏情况

分析白屏原因

先总结一下白屏的两大原因:

  1. 请求没有发出,或没有成功下载到核心资源
    1. 应用业务逻辑问题、使用的WebView有bug、网络问题、安全问题(跨域/证书/认证)、权限问题)
    2. 加载异常(无法打开、加载某个资源,如本地资源无权限、路径不对、没有该资源、远程资源404、核心资源下载失败、网络问题/服务器问题/cdn问题)
  2. 解析和渲染异常(html/CSS/JS DOM)
    1. 前端实现有问题,或者客户端WebView版本太老
    2. 渲染异常(兼容性问题导致的无法显示;有时候,硬件加速也可能搞搞震)

其中还有一些少见的、一般属于应用业务逻辑问题的情况:如缓存问题(如应用自身业务逻辑缓存了部分资源,在缓存过程或读取过程中处理不当)

从解决问题的角度来说(尤其是解决偶发的、非必现的、单用户的、远程的问题),针对缓存问题(及疑似的缓存问题),清空app缓存或重新安装app,有一定的科学性和有效性(虽然它不根本解决问题)。

另外一种情况也有可能出现白屏——但它不是问题,而是正常现象。如打开的url对应的schema是调起其他app或服务来处理的(如一个打开应用商店的schema、调起其他app的schema)等。这时,WebView按照编写的业务逻辑发出Intent(在Android上是这样)启动处理schema的实体。当系统内没有安装或提供能处理该schema的app/服务时,就会在用户的手机屏幕上留下空白的看上去像是白屏问题的界面。如果这时应用和系统均没有弹出任何提示,会令用户误解,也会令暂时没有摸清楚情况的开发人员紧张。

定位白屏的触发因素

分为有调试台和无调试台的情况。它们的基本流程差不多,首先是确认请求以及请求的响应是正常的,接着确认核心资源的加载情况,最后确认js的执行情况以及最后的渲染效果。

前置知识

在Android中,建立adb连接后,可以通过实现了DevTools协议的工具(如Chrome提供的chrome://inspect)对运行于Android设备的WebView进行调试。

部分经过定制的WebView,可能会关闭该协议(或需要特殊操作后),导致无法远程调试。

请求/响应异常

加载网页的前提是获取对应网页的核心资源。在发出请求、接收响应、下载资源这三大步骤中出现问题,会导致核心资源不能完整获取。

实际上在过去接收到并分析的线上问题中,绝大多数均为网络问题(编程上,是核心资源没有加载成功)。

  • 首先是经验法:非群发、本地不能复现、偶现的问题,绝大多数为网络问题(数据表明的)。此外,即使用户声称能稳定复现,那也不能完全排除网络问题——因为部分地区的网络提供商很坑/该应用厂商采用的CDN偏偏在这个用户所在的位置抽风。进一步的,大多数WebView画面加载的内容并不复杂,并不是新技术,出问题的概率较低。

  • 有调试台的情况:reload页面,查看Network,检查资源加载情况

  • 无调试台:

  1. WebView回调
    1. onPageStarted:没有回调,可断言白屏原因为类型Ⅰ(核心资源未就绪)。请求可能没有成功送达,也可能是服务器没有发回响应,或者是因为网络问题没有接收到响应(丢失或超时)
    2. onReceivedErroronReceivedSslError:加载资源时出现异常、SSL握手异常(注意我们的业务场景是系统层而非App层,不出问题的情况下不会像App应用层那样关注这个回调
    3. onPageFinished:页面加载整体顺利完成,流程结束
    4. onProgressChanged:加载进度回调
    5. shouldOverrideUrlLoading:页面被重定向或加载新的url时回调;返回true时表示该URL由App处理(而不交给WebView去加载),这个回调可以实现对url的拦截、GET的参数组装、实现自定义的schema。可以监测该回调的返回值,返回为false时,可以确认WebView不会做任何工作,理论上留下白屏
  2. 加载URL仅包含HTML时,几乎完全可以断言白屏问题不是渲染问题/兼容问题。几乎就是网络问题和请求问题(比如请求没有发出,DNS问题,加载本地资源的权限问题,认证问题)
  3. 加载本地资源时:首先排查权限问题。主要是路径存在且正常、scheme正确且支持、SELINUX权限无异常(WebView的架构是被设计为独立的进程。权限问题的报错可能不在应用进程中,而是在WebView的进程中,看日志不能只看应用进程的。)
  4. 业务逻辑问题:没有统一的问题解决思路,特别是(远程的)设备正在运行一个三方应用时,只能排除法和本地调试。曾经碰到一个渣渣应用,它接入某厂商的广告SDK。其业务逻辑是(反编译得知的),首屏GET广告页,并加载出来展示广告。在GET请求中加入了设备IMEI,运行于我们的某台设备(平板,不含IMEI等)中因为获取不到相关信息,因此在广告页加载不出来,傻楞着不动,展示白屏。最初分析为应用兼容性问题导致的卡死,经过深入分析后确认是其没有成功加载广告,按照其本身业务逻辑,广告没有展示就不肯触发展示下一个Activity,看上去是卡死,实质上是资源加载失败导致的WebView白屏。这个加载失败主要原因还是自身业务逻辑的问题。
  5. 安全问题:这个主要是前端本身的问题,但是稳定性KPI是算在背锅端头上的。跨域、防火墙、没有设置usesCleartextTraffic时使用了HTTP、HTTP与HTTPS混用(前端页面问题;有时候,主资源是HTTP时,WebView会安全问题不加载之,导致整个页面缺失主资源而停止加载)、证书验证等。通常用排除法定位、让前端来看,远程客户端基本没有办法处理(除非定制WebView)
  6. 网络问题:DNS没有解析到目标主机的IP或解析得到错误的IP;TCP连接失败、Socket超时、跳数耗尽;弱网:可能因为弱网问题,超时+重试超出了阈值后不再自动重试;
  7. 逻辑问题:部分资源成功加载,但是设置了等待所有资源加载完成再显示(业务逻辑自行设定),因此等待某些慢速资源甚至是已经404的资源

通常偶发的、非群发的白屏问题中,网络问题嫌疑大。此时只要判断问题原因不在解析渲染,请求也发出了,响应也正常(通过WebView回调可以看到,用户远程难以架一个调试台),基本上大多数情况都是网络问题。网络情况复杂,即时远程用户测速称网络没有问题,也不能在没有任何证据的支持下判断远程到服务器的网络情况。

  • 网络问题(无调试台的情况下)
  1. 判断DNS是否正常(结合自身业务和基建提供的能力;也可以直接看下面第二步的有没有发出请求)
  2. 查看是否发出请求(同上,也可以直接看第三步)
  3. 是否返回了HTML(WebView是否回调onPageStarted
  4. 是否发起了CSS和JS请求(主资源完成加载,查看progress)
  5. 解析H、C等,执行JS、生成DOM,计算、摆放(解析、渲染结束)(主要靠日志收集、环境信息汇总以及本地复现)
  6. 渲染(这里上屏,白屏结束)(如果画面有异常那就是别的问题,跟白屏一点关系都没有。)

解析与渲染

兼容性问题比较复杂,一时间难以判断问题属于哪个端或由哪块进行修改。可以确认的是,出现这种问题基本都要调试台才能顺利完成问题定位。

  1. 解析HTML CSS JS,构造DOM(前端页面本身兼容性问题、所使用的WebView兼容性问题)——这个非常简单,直接打开调试台看就行了
    1. 页面包含不支持的标签
    2. 页面代码有错误
    3. 权限问题(加载本地资源要注意有无权限)
  2. 渲染(很少出现因渲染问题导致的白屏)——根据解析构造的DOM对页面布局、绘制(布局阶段为计算位置+摆放,内容没有上屏显示)
    1. WebView自身问题(崩溃。可能因漏洞、内存、bug等崩溃)(渲染进程如果崩溃,会回调onRenderProcessGone
    2. WebView兼容性问题(不同版本的兼容性问题;前端写的样式和标签太牛皮了也有可能出现兼容性问题)

总结

以最少的精力,相对较科学的定位(非短时的、远程的、没有/调试条件的)白屏问题,步骤大概如下

  1. 调试台(如果条件允许,进入调试台也许只需要几秒钟就可以看出问题原因)
  2. 网络、请求问题排查;本地资源排查权限
  3. WebView回调(需要打日志或者其他跟踪方法),三方WebView本方法失灵
  4. 在不同的WebView中测试
  5. 删除可疑的前端标签和样式;WebView拦截部分可疑资源不要加载。。。。
  6. 回退代码或下架应用

附录

回调

  1. 创建页面:onCreate()
  2. 页面加载(收到服务器响应了):onPageStarted()
  3. 页面加载完成:onPageFinished()
  4. 初始化耗时:从onCreate()到onPageStarted
  5. 加载资源:从onPageStarted()到onPageFinished()
  6. All:从onCreate()到onPageFinished()

拦截资源加载

1
2
3
4
5
6
7
8
9
/**
* 发生资源加载,拦截顺序
*
* 此方法添加于API21,调用于非UI线程,拦截资源请求并返回数据,返回null时WebView将继续加载资源
* @param view
* @param request
* @return
*/
public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request)