[闲鱼技术] Release Flutter的最后一千米

做者:闲鱼技术-凯航android

Flutter是一个使用Dart语言开发的跨平台移动UI框架,经过自建绘制引擎,能高性能、高保真地进行Android和IOS开发。在业界还未出现过Base Flutter的大型商业应用实战验证的状况下,闲鱼技术团队在最复杂且重要的商品详情页做了相关的技术实践并取得良好的结果。现尝试经过本文向有兴趣进行相似实践的开发者或团队分享过程当中的思考/实践过程。ios

Flutter特点

面对一系列移动开发技术:IOS、Android、Weex,RN, Kotlin,H5... Flutter究竟特点何在? git

image.png

开发语言选择

了解过Flutter的都知道,它采用Dart语言进行开发,而并不是Java,Javascript这类热门语言,这是Flutter团队对当前热门的10多种语言慎重评估后的选择。由于Dart囊括了多数编程语言的优势,它更符合Flutter构建界面的方式。 github

image.png
Dart更多优点可查看 为何Flutter会选择Dart

Flutter vs ReactNative框架对比

ReactNative Flutter
image.png
image.png

ReactNative算法

  • 采用Javascript开发,需学React,成本高
  • 须要JavaScript桥接器,实现JS到Native转化,性能耗损
  • 访问原生UI,频繁操做易出性能问题
  • 支持线上动态性,可有效避免频繁更新版本

Fluttershell

  • 采用Dart开发,可直接编译成Native代码(易学)
  • 自带UI组件和渲染器,仅依赖系统提供的Canvas(无桥接耗损)
  • 暂不支持线上动态性

Flutter更多特点能够连接为何说Flutter是革命性的?编程

每一个框架都是为解决特定问题而产生的,不存在最好的框架,只有最适合你团队的框架。闲鱼是个业务快速发展的App,为更多业务尝试和探索,它采用现有流行的框架,能支持线上动态化需求。但出于个性化交互以及流畅性体验(首页、商品详情、发布闲置等),主链路依旧只采用原生开发。为兼顾跨端开发及高性能需求,闲鱼通过充分调用,最终选择了Flutter。为验证Flutter的性能,闲鱼挑选重要且复杂的主链路业务(商品详情)做为首个Flutter页面实践点,以这种方式来快速暴露并解决Flutter相关问题。缓存

闲鱼Flutter突破点

Flutter与Native混合编程方案

随着Flutter版本的不断迭代,稳定性和质量逐渐完善,市场上纯Flutter开发的App也不断涌现。闲鱼对Flutter采起“由点到面,逐一替换” 的策略,先将商品详情迁移到Flutter页面,后续逐步扩展到其余功能模块,但这样就不可避免涉及到Flutter与Native页面混合调用的场景(以下图): 性能优化

image.png
对纯Flutter工程而言,它主要经过FlutterView中Navigator来管理页面间的跳转;对纯Native工程而言(如:android), 它主要经过系统中ActivityStackSupervisor类对页面切换进行管理,这样当Flutter与Native混合时,就面临浏览一组页面,两套页面管理方式(Flutter管理Flutter页面,Native管理Native页面), 若执行回退操做时,很难保证能回退到指望页面。另外,Flutter工程中界面都是一个继承自SurfaceView的FlutterView(说白了Flutter界面就一个View,不是Activity也不是Fragment),Flutter和Native组件间相互调用也不可避免。

Flutter Native(Android)
image.png
image.png

所以要混合调用就会涉及两个问题:并发

  • 混合栈管理
  • 组件间调用

混合栈管理

Flutter出现的目的旨在统一Android/IOS两端编程,所以彻底基于Flutter开发的App,只需提供一个包含FlutterView的页面,后续页面增长/删除/跳转均在FlutterView的Navigator中进行管理。但如今闲鱼只是将部分模块修改为Flutter开发,咱们不可能为统一页面栈管理而将其余全部页面用Flutter重作一次,权衡成本与风险,亟需统一管理Native页面和Flutter页面跳转交互的混合栈。为此,闲鱼提出了4种解决方案(以下图):

image.png
因为IOS有对外系统接口能够方便管理页面栈,所以主动记录页面栈信息就能够解决混合栈管理(方案1),但Android任务栈由系统管理,且融合复杂的Activity回收机制,为下降android学习成本,google并无对外提供页面栈管理API,方案1方式行不通。为统一android/IOS混合栈管理方式,从FlutterView上着手更为可靠,以此为引,闲鱼提出两种方式:

  1. 每启动一个Activity就启动一个新的FlutterView(方案4);
  2. 抽取单一FlutterView或FlutterNativeView,后续每启动一个Activity都对FlutterView或FlutterNativeView进行复用(方案2或方案3);

考虑到每启动一个页面都新建立一套新的Flutter渲染机制,开销太重,目前闲鱼Flutter实践采用方案2,相比而言,该方案性能相对稳定且易操做,下面就是否复用FlutterView进行对比,主要观测Java内存和Native内存增长状况:

数据代表:不复用FlutterView时平均打开一个页面(空页面),Java内存增加0.02M,Native内存增加0.73M;复用FlutterView时平均打开一个页面(空页面),Java内存增加0.019M,Native内存增加0.65M,所以,复用FlutterView在内存使用上是有优点的,如需更深了解可查看 Android Flutter内存初探。此外,相关方案的详细表述在 How to manage page stack in flutter/native hybrid App 以及 Support multiple shells in a single process均有阐述。

组件间调用

image.png
组件间采用比较常见场景就是黑屏问题,出现该问题多数为Layer冲突。从上图(右)可知UI渲染原理:GPU的VSync信号同步到UI线程,UI线程使用Dart构建抽象的视图结构(Layer Tree),接着在GPU线程进行图层合成,且视图数据提供给Skia引擎进行渲染生成GPU数据,最终经过OpenGL或Vulkan提供给GPU,由此能够看出Flutter并不关心显示器、视频控制器以及GPU具体工做细节,它只关心发出的VSync信号,以求尽量快地在两个VSync信号之间计算并合成视图数据并提供给GPU。Flutter开发者都知道Flutter界面渲染时,使用的是FlutterViewController.view的Layer,假若Flutter页面跳转到Native作界面渲染相关逻辑时, Native也使用同一个Layer,这将会致使Flutter在release模式没法渲染,LayerTree合成失败即Layer冲突。不过这问题解决也很简单,只须要采用Window或独立View方式唤起Native便可。

解决了Flutter与Native混合编程所面临的问题后,接下来要处理的就是混编工程问题,出现该问题的缘由仍是咱们的项目不是彻底的Flutter工程(即:android /ios + Flutter)所致。混合工程项目结构以及Flutter产物以下图:

项目结构

image.png

Flutter产物

image.png

其实对通常Flutter工程而言,采用AndroidStudio编译Flutter与编译Native工程方式同样,当将其部署到server端采用mtl编译时,server缺乏Flutter编译环境,于是致使Flutter工程没法编译。解决此问题能够采起两种方式:

  1. 在每一个server端部署Flutter编译环境
  2. Native工程远程依赖Flutter编译产物

对1,对各server端都去部署Flutter环境有点不切实际(若server就那么几台也能够);对2,闲鱼的作法是将Flutter工程编译出的中间产物以AAR形式导出并上传至maven库,最后Native工程以依赖包形式将AAR打入最终apk中,这样处理后解耦了Native团队对Flutter团队的依赖。固然,具体实践过程当中确定没有这么简单,咱们在编译过程当中对Pod/Gradle编译脚本、engine以及flutter_tools等均有所优化,对后续集团推广Flutter奠基了基础。

阿里Flutter生态适配

将Flutter应用于闲鱼,不可避免须要使用集团提供的基础组件库,但这些组件库都是Native,考虑到为后续Flutter在集团推广,打造阿里Flutter生态圈,闲鱼团队对集团内部基础组件库作了适配支撑,后续可创建私有仓库,直接Git引用。

生态适配原理及性能

image.png
上图(左)概述了Flutter平台通道,使用MethodChannel在Client(UI)和主机(平台)之间传递消息,消息和响应异步传递以确保用户界面保持正常响应。对UI,Flutter的MethodChannel类能够发送与方法调用相对应的消息;对平台,Android端MethodChannel类和IOS端FlutterMethodChannel类能够接收方法调用并发送结果,同时方法调用还能够逆向发送,即以平台做为实现Dart方法的Client。值得注意的是Flutter Plugin开发相关原理也是如此。上图(右)还对MethodChannel吞吐量性能进行了简略表述。

多媒体解决方案

在之内容为王的时代,多媒体技术备受关注,性能的好坏直接影响用户体验,但Flutter多媒体默认功能存在如下缺陷:

  • 功能单一,如:播放器缺乏滤镜,与Native淘宝播放器存在必定差距
  • 兼容性欠佳

为改善体验,优化性能,闲鱼对Flutter播放器以及图片性能做出以下改进:

Texture对接自定义视频播放器

image.png

具体方案:

image.png

图片性能优化

有过移动开发经验的都知道,图片展现是OOM出现的高频场景,而Flutter默认采用基于LRU算法的图片缓存策略,且图片缓存MaxSize=1000,占用内存较高,为此闲鱼采用如下两种策略对图片性能进行优化

image.png

Flutter 上线效果 & 性能对比 & 成熟度

解决了Flutter存在的问题,要的就是产品可以以一种性能稳定、交互流畅、界面美观的姿态呈如今用户面前,下面就以闲鱼宝贝详情线上Flutter版本为引,对Flutter应用页面展现、性能以及成熟度进行阐述。

闲鱼宝贝详情Flutter应用线上效果

image.png

Native性能对比

测试环境

  • 测试机型:iphone6
  • IOS版本:11.3
  • 闲鱼版本: 6.1.3
  • 测试方法: 在Flutter版本和Native版本各自宝贝详情页面执行相同操做:即从我发布的页面进行10个不一样详情页面

注: 下图仅对Flutter和Native性能进行了粗略对比

image.png

Flutter成熟度

image.png
上图为crash收敛曲线图,可容易看出通过几个版本迭代,crash率基本趋于稳定,体感与Native能够媲美。

延展讨论

目前Flutter尚处于Preview阶段,没有通过大规模实践验证,框架成熟度及稳定性仍有待完善。上文仅仅是将闲鱼团队在实践Flutter开发时碰到部分问题及解决方案进行了简略阐述,一个产品从开发到上线所面临的问题,确定远不及这些。随着Flutter覆盖场景的增长,难题也会不断涌现,健全有效的性能及稳定性监控体系不可或缺。为建设基于Flutter全新的一体化研发体系,提升开发效率,对动态化需求、规范Dart编码、统一中间件桥接机制、快速发版能力及完备的自动化测试建设等一系列问题亟需解决,假若您对此感兴趣,欢迎一块儿交流学习~

简历投递:guicai.gyx@alibaba-inc.com

相关文章
相关标签/搜索