Flutter WebView的性能优化

本文记录了基于WebView的Flutter数据可视化库echarts_flutter的性能优化。

对于任何基于WebView的小部件,页面的加载始终是性能的关键部分。 echarts_flutter也不例外 ,其基础是使用WebView呈现echarts的本地页面。

echarts_flutter的加载内容可以分为以下几部分:

  • 模板HTML
  • echarts脚本
  • echarts扩展脚本
  • 图表的逻辑代码

图表的模板HTML和逻辑代码很小,因此关键是加载echarts脚本和echarts扩展脚本。

echarts的最佳功能之一是它具有许多令人敬畏的扩展,例如WebGL 3D图表,GIS地图等。随着数据可视化要求变得越来越复杂,这些扩展与echarts本身一样重要。 因此,必须允许用户方便地导入扩展。 此外,为了避免麻烦的资产管理,我们希望将HTML和JavaScript都当作字符串来处理,因此WebView会将所有源作为URI加载。

会有一些问题:

  • 脚本应该在HTML内还是随后注入?
  • URI有一些字符限制,它需要一种安全的编码形式。

原始方法

一开始,我们认为,总的来说,我们最好将所有内容放入HTML中并将它们一起加载。 考虑到JavaScript中有很多非法的URI字符,我们应该在编写后将HTML转换为Base64。 因为我们不知道用户将导入什么扩展脚本,所以编码将由函数动态执行:

String _getHtml(
  String echartsScript,
  List< String > extensions,
  String extraScript,
) {
  ... // Compose and return all HTML and scripts
}


  @override
  void initState() {
    super .initState();
    // Convert to Base64 in init
    _htmlBase64 = 'data:text/html;base64,' + base64Encode(
      const Utf8Encoder().convert(_getHtml(
        echartsScript,
        widget.extensions ?? [],
        widget.extraScript ?? '' ,
      ))
    );
    _currentOption = widget.option;
  }


  @override
  Widget build(BuildContext context) {
    return WebView(
      // Load all of them
      initialUrl: _htmlBase64,
      ...
    );
  }

性能测试

让我们对羽毛分析进行简单的性能测试。 该测试用例具有三个图表,包括WebGL 3D图表和液体动画图表:

使用Flutter Dev Tool,我们可以获得CPU时间占用的明火图:

优化

图表及其扩展量很大。 因此,在运行时编写和转换字符串将花费大量时间。 但是这些是获取合法URI字符串的必要步骤,那么如何解决此问题呢?

放弃“将所有内容一起加载”的想法,然后通过valuateJavascript注入动态部分,而仅将静态部分放入HTML中,该怎么办? 这样可以节省一些转换工作。

为了确保可行性,让我们先做一个实验:仅从HTML中移出所有脚本,然后将它们与validateJavascript一起注入,然后检查性能:

  @override
  void initState() {
    super .initState();
    _htmlBase64 = 'data:text/html;base64,' + base64Encode(
      const Utf8Encoder().convert(_getHtml(
        // remove all scripts form the convert function
        // echartsScript,
        // widget.extensions ?? [],
        // widget.extraScript ?? '',
      ))
    );
    _currentOption = widget.option;
  }
  
  
  void init() async {
    final extensionsStr = this .widget.extensions.length > 0
    ? this .widget.extensions.reduce(
        ( value, element ) => (value ?? '' ) + '\n' + (element ?? '' )
      )
    : '' ;
    await _controller?.evaluateJavascript( '' '
      // inject after the page is loaded
      $echartsScript
      $extensionsStr
      const chart = echarts.init(document.getElementById(' chart '), null);
      ${this.widget.extraScript}
      chart.setOption($_currentOption, true);
    ' '' );
  }

结果是:

我们可以看到,加载HTML的时间减少了,而包含脚本注入的onPageFinished的时间却增加了。 总时间减少了。

因此,似乎转换大型字符串的成本很高。 相反,使用评估Java脚本是正确的方法。

因此,我们然后删除了所有动态转换部分,并将模板HTML加载为const字符串。 由于HTML现在是静态且简短,因此我们可以手动转义非法字符并直接输入UTF-8字符串,它不需要dart:convert库,看起来更加普通:

const htmlUtf8 = 'data:text/html;UTF-8,<!DOCTYPE html><html><head><meta charset="utf-8"><style type="text/css">body,html,%23chart{height: 100%;width: 100%;margin: 0px;}div {-webkit-tap-highlight-color:rgba(255,255,255,0);}</style></head><body><div id="chart" /></body></html>' ;


  @override
  void initState() {
    super .initState();
    _currentOption = widget.option;
  }
  
  @override
  Widget build(BuildContext context) {
    return WebView(
      initialUrl: htmlUtf8,
      ...
    );
  }

测试结果为:

我们可以看到时间进一步减少,尤其是在加载中。

因此,与原始版本相比,性能提高了很多。

Echarts脚本也是静态的,如果我们先前对其进行了转换并将其放入HTML中,该怎么办:

const echartsHtmlBase64 = '...' ;

  
  @override
  Widget build(BuildContext context) {
    return WebView(
      initialUrl: echartsHtmlBase64,
      ...
    );
  }

结果是:

相反,这需要更多时间。

因此,我们可以看到“以HTML格式放入脚本”并不一定比“由valuateJavascript注入”更好,并且由于某些编码原因甚至花费更多时间。

结论

总之,最终的优化解决方案是:将模板HTML加载到UTF-8 URI字符串中,并使用EvaluationJavascript注入所有脚本和逻辑代码。

注意:webview_flutter存在一个问题,即onPageFinished在IOS中不起作用,因此上述优化目前尚未应用于发行版。 您可以在 此commit中 查看其源代码

先前发布在https://levelup.gitconnected.com/a-performance-optimization-of-flutter-webview-6afa1a5b4300

From: https://hackernoon.com/a-performance-optimization-of-flutter-webview-8f5336wa

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值