5

腾讯X5内核 WebView 实践总结

 2 years ago
source link: http://www.androidchina.net/9576.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

本篇文章是基于 腾讯X5内核 WebView 实践的总结篇,较上篇文章更为完整,具体。

onPageFinished() 回调时机

通过 WebView 的回调函数,分析 onPageFinished() 回调时机

加载某个网址的Android端回调监测如下:

    shouldOverrideUrlLoading   time: 1519274808392

    onPageStarted: time: 1519274808561 // 169ms

    onPageFinished   time: 1519274809735 // 1174ms
    onReadableCallback: false

    shouldOverrideUrlLoading   time: 1519274811067 --url :shanbay.native.app://document/ready
    onReadableCallback: true

    onPageFinished   time: 1519274817879 // 9318ms
    onReadableCallback: true

据上数据分析: 第一次onPageFinished()回调触发是在 1174ms (较onPageStarted()方法) 第二次onPageFinished()回调触发是在 9318ms

通过 chrome://inspect监测的资源加载时序

chrome://inspect

Network 面板突出显示两种事件:DOMContentLoadedload

解析页面的初始标记时会触发 DOMContentLoaded。 此事件将在Network 面板上的两个地方显示:

  1. Overview 窗格中的蓝色竖线表示事件。
  2. Summary 窗格中,您可以看到事件的确切时间。

页面完全加载时将触发 load。此事件显示在三个地方:

  1. Overview 窗格中的红色竖线表示事件。
  2. Requests Table 中的红色竖线也表示事件。
  3. 在 Summary 窗格中,您可以看到事件的确切时间。

分析上图:

  1. DOMContentLoadedload 事件触发时机与Android端的回调触发时机不一致。
  2. 第一次onPageFinished()方法的调用和 document 类型文件加载完成时间相近,且经过多次测试是在该文件加载完成后调用。
  3. 第二次onPageFinished()方法回调时间和load时间相近。

初步总结:

  1. 第一次onPageFinished()方法是在document类型文件加载完成后调用的。
  2. 第二次onPageFinished()方法是在load完成时回调。
  3. 通过仔细查看shouldOverrideUrlLoadingonPageStarted方法时间差以及 图中 Overview 栏,会发现加载网页不是第一时间去请求数据的。所以 onPageStarted()方法较触发是有一定的延迟时间。

ready 替换 onPageFinished 实现

据上分析的结果我们会发现,onPageFinished()方法会调用多次,所以,如果我们将业务逻辑放到该方法中执行,如果不做控制,势必会出现一些问题。当然,由于网页类型的多样性,即使做了控制,依然会在特定的页面出现问题。

那么我们如何摆脱对onPageFinished()的依赖呢?

网页的加载状况,前端肯定会有生命周期的感知,那么我们为什么不依赖前端的通知来触发Native逻辑呢?

通过上述的思考,Native的事件触发完全交给前端去主动调取,而不是通过不靠谱的WebView回调。在前端的$.ready()方法中去通知移动端开始执行业务逻辑。

并且这种方式在时序性能方面有很大提升,比第二次onPageFinished()触发时机早很多(在较为复杂的页面相差更大)

上面我们通过 ready() 的主动通知,实现了 onPageFinished() 方法中业务逻辑的优化。

但是,在单页应用的网页中,$.ready() 只在主页面渲染完成时触发一次,在子页面并不会触发,而且,WebViewshouldOverrideUrlLoading()onPageStarted() 方法都不会回调。在一些单应用网页会触发 onPageFinished() 方法,它去请求了新的资源,所以我们感知到了回调。而个别网页并没有去请求新的资源,直接对资源进行了替换,这种情况,我们就感知不到 onPageFinished() 的回调。

当然,如果开发自己的页面就不存在这些多情况的处理,可以协商解决方案。

本文的主要实现是基于第三方网页做的功能扩展,所以需要考虑这些兼容性问题。

给出不成熟的参考方案:

  1. 前端 url 变化监听,通知移动端页面变化。
  2. onPageFinished() 方法中再去做一个保底操作,损失一部分性能换取用户的响应速度。

Js 注入时机以及时序控制

网络上的普遍做法是在 onPageFinished() 中注入 Js 脚本。

这种做法存在一些问题:

  • 可能会注入多次。
  • onPageFinished()第二次调用时机很迟,在复杂的页面性能损失很大。
  • 如果注入太多,会影响页面的体验。

由于项目注入的脚本行数达到 1W+,所以我们需要对时序做一些优化。保证调用时我们已经完成了注入。

这里我们主要注入生成一个 script 标签。

webView.loadUrl("javascript:(function() {" +
        "var scriptElement = document.getElementById('readability-script');" +
        "var parent = document.getElementsByTagName('body').item(0);" +
        "if(parent && !scriptElement) {" +
        "var script = document.createElement('script');" +
        "script.type = 'text/javascript';" +
        "script.id = 'readability-script';" +
         // Tell the browser to BASE64-decode the string into your script !!!
        "script.innerHTML = window.atob('" + mAssetsScript + "');" +
        "parent.appendChild(script);}" +
        "})()");

通过控制标签的唯一性来防止注入多次; 在页面初始化前完成本地 js 脚本的文件读取;不断尝试注入直到可以注入为止。

onProgressChanged() 回调中,不断的尝试读取节点注入脚本。

通过最开始对 onPageFinished() 的分析。是否可以尝试在第一次回调时开始注入脚本。但是,不能保证每个网页都会回调两次onPageFinished()

通常情况下,CSS不会阻塞HTML的解析,但如果CSS后面有JS,则会阻塞JS的执行直到CSS加载完成(即便JS是内联的脚本),从而间接阻塞HTML的解析。

资源加载回调

在研究WebView加载时序时发现了这个资源加载的回调onLoadResource()。这里简单介绍下,针对这个回调,可以做的事情很多。

在加载页面资源时会调用,每一个资源(比如图片)的加载都会调用一次。

webView.setWebViewClient(new WebViewClient(){
      @Override
      public boolean onLoadResource(WebView view, String url) {
      }
  });

可以实现预加载及手动缓存的功能。优化用户体验并且减少多次访问造成的流量浪费。

打造最舒适的 WebView 调试环境

作者:弥宣
链接:https://juejin.im/post/5c4919fc6fb9a049fe357f09
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

转载请注明:Android开发中文站 » 腾讯X5内核 WebView 实践总结


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK