如何让 Flutter 应用更好地使用 SVG?

栏目: IT技术 · 发布时间: 4年前

如何让 Flutter 应用更好地使用 SVG?

阿里妹导读: SVG 作为一个强大的矢量图标准格式,在图片清晰度的表现力上有着位图无法比拟的优势。那么是否 SVG 就是绝对的首选了呢?事实可能并非如此。本文将带大家了解 SVG 在 Flutter 应用中的性能问题,分享 UC 浏览器内核技术团队在 Flutter 应用中改进 SVG 应用的探索实践。

文末福利:下载 《AliFlutter 体系化建设和实践》电子 书。

例说历史

在计算机的世界里,很多空间优化都隐藏着计算消耗,比如下面这张色彩和形状丰富的 4k 图片(其实也可以是 8k,屏幕够大就可以看到),压缩后只有 5kB 大小。

如何让 Flutter 应用更好地使用 SVG?

如果这个 5kB 用 PNG 来存储的图片,是下图这个样子。

表现力天差地别。

为了达到类似的清晰度,一般操作系统会协助应用打包时在 UI 资源中归集多个分辨率的图片。

如何让 Flutter 应用更好地使用 SVG?

32x32

如何让 Flutter 应用更好地使用 SVG?

64x64

如何让 Flutter 应用更好地使用 SVG?

256x256

如何让 Flutter 应用更好地使用 SVG?

1024x1024

上面这一个图标,资源包占用超过 120kB,其中最大的一个版本,运行内存占用在 4MB。

这么看来,SVG 图片应该是绝对首选吧?

并非如此。在给 Flutter 做 SVG 支持分析之前,开发者可能觉得各个移动系统 API 中没有提供是个很大缺憾。

而经过光栅化代价数据分析后,也能理解了系统对盲目使用 SVG 带来问题的担忧。

比如还是上面这个 SVG 图片,在骁龙 626 的手机上,Flutter 光栅化到 64x64 的区域需要 34ms,一个 SVG 让应用与 60 帧流畅度彻底无缘。实测 IPhone X 需要 8ms,只能流畅显示两个。

另外补充一点,SVG 或者说矢量图的应用需求是 UI 扁平化趋势兴起后才出现的。在拟物化的时期,抛开光栅化速度不说,矢量图在显示写实风格的图标时,缺陷是无法容忍的。比 如 doggy, 用最先进的追踪矢量化后(右侧),已经数码感十足,存储占用也远超 PNG。

如何让 Flutter 应用更好地使用 SVG?

好在,扁平化的矢量图在工程推进时,也在有意无意回避前面说的问题,大部分都走简约风。所以只要避开陷阱,SVG 还是在很多场景可以做到表现优秀的。

应用现状

Flutter 项目主线没有支持

Flutter 的基础组件 Skia 代码中有 SVG 目录,但别误会了,Skia 只有序列化至 SVG 的功能,没有解码绘制 SVG 的能力。

框架开发计划目前也没有支持的打算:

https://github.com/flutter/flutter/issues/1831

OS 也没有支持的意向

这是可以理解的,因为庞大如 Android 和 iOS 也默认不支持:

  • https://stackoverflow.com/questions/34990236/how-to-use-svg-image-in-imageview

  • https://stackoverflow.com/questions/35691839/how-to-display-svg-image-using-swift

大家的共识是,全功能的 SVG 支持工作量不小,还有性能隐患(都是拐着弯提到)。

SVG 的锅,矢量字体方案不用背

前面 SVG 咨询,在建议解决方案中,都提到用矢量字体解决。矢量字体:

  • 主流 OS 都自带的支持。

  • 基本只能单色。

  • 不用依赖 xml。

  • 由于单色输出,很多图层绘制叠加等等不可控的性能影响要素都被排除。

  • 系统方便做位图缓存管理(我们开发者 工具 后续可以再研究)。

虽然在 SVG 投入不少研究,也不得不承认,字体矢量图输出是目前很务实高效的方案。

配合工具流程改进 SVG 应用

SVG 作为一个强大的矢量图标准格式,还是可以找到合适的应用的。比如多彩图标,方便热更新,生产工具对此格式的广泛支持。

让 SVG 再次伟大

在 OS 和 runtime 都抛弃 SVG 的情况下,flutter_svg 包毅然然扛起大旗,简单快捷的给 Flutter 提供了 SVG 渲染解码的能力,显示出 Flutter/Dart 不俗的扩展潜能。

flutter_svg 的使用非常简单,提供和 flutter framework 中 image_provider 类似的接口。下面两段代码就是分别显示来自 asset 和网络的 SVG 图片:


 

SvgPicture.asset(

'assets/adsmall.svg',

placeholderBuilder: (BuildContext context) => Container(

child: const CircularProgressIndicator()),

),


SvgPicture.network(

'https://raw.githubusercontent.com/dnfield/flutter_svg/master/example/assets/deborah_ufw/new-camera.svg',

placeholderBuilder: (BuildContext context) => Container(

child: const CircularProgressIndicator()),

),

用工具避坑

不能对 SVG 的性能隐患坐视不理。

UC 浏览器内核技术团队开发了一个【资源面板】工具,可以方便地连接 Flutter 应用,实时显示资源分配的内存,对其中的 SVG 图片,资源面板提供了预览和获取光栅化损耗的功能。

如何让 Flutter 应用更好地使用 SVG?

通过记录和对比 SVG 在实际移动设备上的光栅化损耗,我们可以方便地识别出有隐患的 SVG 文件,将 SVG 的应用安排妥当。

如何让 Flutter 应用更好地使用 SVG?

通过实际 Rasterization Cost 的对比可以看到,简约风格的图标,时间消耗到 16.66ms 来说在骁龙 626 上也还是可以接受的。

实现原理

flutter_svg

flutter_svg 是一个 dart package,提供解析来自 network、asset、memory 等 SVG 的能力。

由于解析结果并不是 ui.Image 这样的位图,所以 flutter_svg 并没有和 ImageCache 协作,而是自己实现了一套 PictureCache , PictureCache 中缓存的是 ui.Picture ,这个类实际是 skia 引擎的 SkPicture Wrapper,二进制方式记录具体的 SVG 绘制指令。

ui.Picture 类占用的内存不会很大,缓存基本上是为了避免反复 parse xml。

比如 SvgPicture.asset 的构造接口如下:


 

SvgPicture.asset(

String assetName, {

Key key,

this.matchTextDirection = false,

AssetBundle bundle,

String package,

this.width,

this.height,

this.fit = BoxFit.contain,

this.alignment = Alignment.center,

this.allowDrawingOutsideViewBox = false,

this.placeholderBuilder,

Color color,

BlendMode colorBlendMode = BlendMode.srcIn,

this.semanticsLabel,

this.excludeFromSemantics = false,

}) : pictureProvider = ExactAssetPicture(

allowDrawingOutsideViewBox == true

? svgStringDecoderOutsideViewBox

: svgStringDecoder,

assetName,

bundle: bundle,

package: package,

colorFilter: _getColorFilter(color, colorBlendMode)),

super(key: key);

SvgPicture 的 _picture,由 pictureProvider 的 stream 通知更新:


 

void _resolveImage() {

final PictureStream newStream = widget.pictureProvider

.resolve(createLocalPictureConfiguration(context));

assert(newStream != null);

_updateSourceStream(newStream);

}

pictureProvider 的 stream 由 来自 pictureCache 的 completer 填充 ui.Picture 。


 

// in PictureProvider<T>.resolve

stream.setCompleter(

_cache.putIfAbsent(

key,

() => load(key, onError: onError),

),

);

Debug 和 Profile 模式下,通过添加配合代码,开发者工具可以在 PictureCache 中查询所有现存的 SvgPicture 。

光栅化时间获取

光栅化的发起接口是 ui.Picutre.toImage 方法,具体的计时在 rasterizer 线程。

如何让 Flutter 应用更好地使用 SVG?

补充说明

Android VectorDrawable

Android 提供了一套 VectorDrawable 方案,是一个简化版的 SVG , 格式和特性不完全兼容,提供转换工具。从文档来看,确实是担心过度复杂的 SVG 影响性能。参考文档:

https://developer.android.com/studio/write/vector-asset-studio

单独 SVG 位图缓存优化

目前 Flutter 用的是一次性光栅化输出每帧的模式,和 chromium 的 cc 按区域构建位图再合成不同,如果在光栅化输出时标记 SVG 的 Picture,缓存这部分位图可以提升帧数,代价当然是内存损耗。

这个功能目前纯用 Dart 无法方便实现,因为在 dart.ui 线程中,RenderPicture 无法预见具体的光栅化分辨率。

最后

目前,【资源面板】可在阿里内部使用,团队正在争取让 Flutter 主线接受这一改动。 欢迎大家探讨交流。

  福利来了  

电子书下载

《AliFlutter 体系化建设和实践》

如何让 Flutter 应用更好地使用 SVG?

Flutter “快速开发、富有表现力和灵活的 UI、原生性能”的特性和理念让 移动开发者 痴狂着迷。阿里如何进行 flutter 体系化建设?有哪些技术沉淀?本书和大家分享闲鱼、UC、饿了么等技术团队在 Flutter 技术及业务应用上的实践和思考。

点击“阅读原文” 立即下载!

如何让 Flutter 应用更好地使用 SVG?

关注 「阿里技术」

把握前沿技术脉搏

戳我,下载电子书。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

程序员第二步

程序员第二步

尹华山 / 人民邮电出版社 / 2013-11 / 45.00元

这本书是写给程序员和项目经理的。作者结合自身的丰富成长历程,通俗易懂地讲述了一名程序员如何才能成为一名优秀的项目经理。内容涉及职业规划、学习方法、自我修炼、团队建设、项目管理等,书中理清了项目管理领域中典型的误区及具有迷惑性的观点,并对项目中的难点问题提出了针对性的解决方法。 全书行文流畅,严谨中带着活泼,理智中透着情感,给读者带来轻松愉快的阅读感受。书中诸多富有创见的观点,让人耳目一新,引......一起来看看 《程序员第二步》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码