Android WebView onPageFinished 对于 Document 意味着什么?

这是因为之前遇到一个线上问题引起的,这篇文章阅读的对象也比较适合 Android 和前端阅读。

我们说下 标题的 Document 对象,这个对 Android 开发者而言有些陌生,可以详见 MDN 里面的解释,简单的说就是它可以去代表我们要加载的这个网页对象。里面包含一些基本的信息和所谓生命周期。

Android WebView 提供了基于 WebViewClient 的页面周期的监听方法,常见的有:

  • onPageStarted 当页面开始请求
  • onPageFinished 页面完成加载
  • onReceivedError 当加载出现错误的时候

更多方法可以详见 https://developer.android.com/reference/android/webkit/WebViewClient

在 JS 中,我们监听页面加载有两种常见的方法,一种是监听 window.onload 另外一个是 document 里的 readychange 来实现的。

其中 window.onload 定义是当页面完全加载时候触发,包括页面的上包含的图片和样式资源,脚本资源。

而 readychange 会监听三个状态的变化:

  • loading 开始加载document
  • interactive DOM 节点树生成完毕可进行操作
  • complete DOM 节点树生成完毕并且资源也已经加载完成

接下来我们做个试验:

我们实现一段监听代码:

<head>
    ...
    <script>
        window.startDate = Date.now()
    </script>
    <style type="text/css">
      ...
    </style>

</head>

<body>
    ...   
<script>
  function writeLog(str) {
      var el = document.createElement("P")
      var now = Date.now()
      
      el.innerText = str + ':' + now + ' (+' + (now - startDate) + ')'
      var wrap = document.querySelector('#js-contents')
      wrap.appendChild(el)
  }
  document.addEventListener('readystatechange', function() {
    if (document.readyState == 'loading') {
        writeLog("readystatechange-loading")
    } else if (document.readyState == 'interactive') {
        writeLog("readystatechange-interactive")
    } else if (document.readyState == 'complete') {
        writeLog("readystatechange-complete")
    }
  })
  window.onload = function() {
    writeLog("window onLoad")
  }
</script>

页面代码

我们会打印出我们比较关注的时间变化。

Android WebView 这边我们需要进行页面加载的测试,然后再 onPageFinished 执行一段 JS 代码。

override fun onPageFinished(view: WebView, url: String) {
                super.onPageFinished(view, url)
                pageLoaded = true
                view.loadUrl("javascript:writeLog('onPageFinished')")
            }

测试发现

onPageFinished 与 window.onload / readyState complete 几乎一致。

这个确认后,我们就再回到之前我们说遇到的 Bug 的问题。

由于客户端出于稳定考虑,实现 Loading 通常会基于 onPageFinished 来关闭,而我们客户端同时还需要监听页面的一些消息,实际上从上面知道 onPageFinished 和 window.load 几乎一致,因此页面的一些资源会影响到比如 img 或者 prefetch 。

由于我们是单页应用,但是这次打包出现了一些配置,会将部分脚本到 prefetch里面去进行预加载,然后 onPageFinished 会等到 prefetch 完成加载后才去执行。这样便造成了之前一直没碰到的,收到了 JS 发出的消息,但是迟迟客户端无法响应。随着单页应用增多,页面纯粹就是一些 JS / CSS , 但是在脚本资源加载的时候,我们要合理的去使用 prefetch / preload 这些标签。

扩展阅读