做者:闲鱼技术-宗心git
2012年应届毕业加入阿里巴巴,主导了闲鱼基于Flutter的新混合架构,同时推动了Flutter在闲鱼各业务线的落地。将来将持续关注终端技术的演变及趋势。github
Flutter是Google开源的跨端便携UI工具包,除了具备很是优秀的跨端渲染一致性,还具有很是高效的研发体验,丰富的开箱即用的UI组件,以及跟Native媲美的性能体验。因为它的众多优点,也使得Flutter成为了近些年来热门的新技术。web
经过以上的特色能够看出,Flutter能够极大的加速客户端的研发效率,与此同时获得优秀的性能体验,基于个人思考,Flutter会为如下团队带来较大的收益:后端
闲鱼在以上的场景中属于第一种场景,服务3亿用户的闲鱼App的背后,开发资源投入并很少,与竞对相比,咱们是一只再小不过的团队,在这种场景下,Flutter为闲鱼业务的稳定发展以及提供更多的创新产品给予了很大的帮助。浏览器
但与此同时,Flutter在设计上带来的优点同时又会带来新的问题。全部的新技术都是脱胎于老技术的,Flutter也不例外,其身上带有不少Chrome的影子。咱们再作一层简化,若是咱们认为Flutter是一个使用Dart语言的浏览器容器,请你们思考一下两个问题如何解决。缓存
带着上面两个问题,咱们来到闲鱼场景下的具体Case以及解决方案的演进过程。性能优化
在这种状况下,闲鱼须要考虑的是首先要考虑引入Flutter容器后的内存压力,保证不要产生更多的内存溢出。与此同时咱们但愿能让Flutter和Native之间的页面切换是顺畅的,对不一样技术栈之间的同窗透明。所以咱们有针对性的进行了屡次迭代。架构
在没有任何改造的状况下以iOS为例,你能够经过建立新的FlutterViewController来建立一个新的Flutter容器,这个方案下,当建立多个FlutterViewController时会同时在内存中建立多个Flutter Engine的Runtime(虽然底层Dart VM依然只有一个),这对内存消耗是至关大的,同时多个Flutter Engine的Runtime会形成每一个Runtime内的数据没法直接共享,形成数据同步困难。框架
这种状况下,闲鱼选择了全局共享同一个FlutterViewController的方式保证了内存占用的最小化,同时经过基础框架Flutter Boost提供了Native栈与Flutter栈的通讯与管理,保证了当Native打开或关闭一个新的Flutter页面时,Dart侧的Navigator也作到自动的打开或关闭一个新的Widget。目前Google官方的提供的方案上就是参考闲鱼早先的这个版本进行的实现的。工具
然而在这种状况下,若是出现如闲鱼图中所示多个Tab的场景下,整个堆栈逻辑就会产生混乱,所以闲鱼在这个基础上对Flutter Boost的方案进行了升级并开源,经过在Dart侧提供一个BoostContainerManager的方式,提供了对多个Navigator的管理能力,若是打比方来看这件事,就至关于,针对Flutter的容器提供了一个相似WebView的OpenWindow的能力,每作一次OpenWindow的调用,就会产生一个新的Navigator,这样开发者就能够自由的选择是在Navigator里进行Push和Pop,仍是直接经过Flutter Boost新开一个Navigator进行独立管理。
Flutter Boost目前已在github开源,因为闲鱼目前线上版本只支持Flutter 1.2的版本,所以须要支持1.5的同窗等稍等,咱们会在近期更新支持1.5的Flutter Boost版本。
因为闲鱼是一个闲置交易社区,所以图片和视频相对较多,对图片视频的线上性能以及内存占用有较严格的要求。目前Flutter已提供的几种方案中(Platform View以及Flutter Plugin),不管是对内存的占用仍是整个的线上流畅度上还存在必定的问题,这就形成了当大部分同窗跟闲鱼同样实现一个复杂的图文Feed推荐场景的时候,很是容易产生内存溢出。而实际上,闲鱼在以上的场景下有针对性的作出了较大的优化。
在整个的Native UI到Flutter渲染引擎桥接的过程当中,咱们选用了Flutter Plugin中提供的FlutterTextureRegistry的能力,在去年上半年咱们优先针对视频的场景进行了优化,优化的思路主要是针对Flutter Engine底层的外接纹理接口进行修改,将原有接口中必须传入一个PixelBuffer的内存对象这一限制作了扩展,增长一个新的接口保证其能够传入一个GPU对象的TextureID.
如图中所示,优化后的整个链路Flutter Engine能够直接经过Native端已经生成好的TextureID进行Flutter侧的渲染,这样就将链路从Native侧生成的TextureID->copy的内存对象PixelBuffer->生成新的TextureID->渲染,转变为Native侧生成的TextureID->渲染。整个链路极大的缩短,保证了整个的渲染效率以及更小的内存消耗。闲鱼在将这套方案上线后,又尝试将该方案应用于图片渲染的场景下,使得图片的缓存,CDN优化,图片裁切等方案与Native归一,在享受已有集团中间件的性能优化的同时,也获得了更小的内存消耗,方案落地后,内存溢出大幅减小。
目前该方案因为须要配合Flutter Engine的修改,所以暂时没法提供完整的方案至开源社区,咱们正在跟google积极沟通整个修改方案,相信在这一两个月内会将试验性的Engine Patch开源至社区,供有兴趣的同窗参考。
将以上两个问题解决之后,闲鱼开始了Flutter在业务侧的全面落地,然而很快又遇到新的问题,在多人协做过程当中:
在方案的前期,咱们使用了社区的Flutter Redux方案,因为最早落地的详情,发布等页面较为复杂,所以咱们有针对性的对View进行了组件化的拆分,但因为业务的复杂性,很快这套方案就出现了问题,对于单个页面来讲,State的属性以及Reducer的数量都很是多,当产生新需求堆叠的时候,修改困难,容易产生线上问题。
针对以上的状况,咱们进行了整个方案的第二个迭代,在原有Page的基础上提供了Component的概念,使得每一个Component具有完整的Redux元素,保证了UI,逻辑,数据的完整隔离,每一个Component单元下代码相对较少,易于维护和开发,但随之而来的问题是,当页面须要产生数据同步时,整个的复杂性飙升,在Page的维度上失去了统一状态管理的优点。
在这种状况下闲鱼换个角度看端侧的架构设计,咱们参考React Redux框架中的Connect的思想,移除掉在Component的Store,随之而来的是新的Connector做为Page和Component的数据联通的桥梁,咱们基于此实现了Page State到Component State的转换,以及Component State变化后对Page State的自动同步,从而保证了将复杂业务有效的拆解成子问题,同时享受到统一状态管理的优点。与此同时基于新的框架,在统一了你们的开发标准的状况下,新框架也在底层有针对性的提供了对长列表,多列表拼接等case下的一些性能优化,保证了每一位同窗在按照标准开发后,能够获得相对目前市面上其余的Flutter业务框架相比更好的性能。
目前这套方案Fish Redux已经在github开源,目前支持1.5版本,感兴趣的同窗能够去github进行了解。
闲鱼在去年经历了业务的快速成长,在这个阶段上,咱们同时进行了大量的Flutter的技术改造和升级,在尝试新技术的同时,如何能保证线上的稳定,线下的有更多的时间进行新技术的尝试和落地,咱们须要一些新的思路和工做方式上的改变。
以咱们平常工做为例,Flutter的研发同窗,在每次开发完成后,须要在本地进行Flutter产物的编译并上传到远端Repo,以便对Native同窗透明,保证平常的研发不受Flutter改造的干扰。在这个过程当中,Flutter侧的业务开发同窗面临着不少打包上传更新同步等繁琐的工做,一不当心就会出错,后续的排查等让Flutter前期的开发变成了开发5分钟,打包测试2小时。同时Flutter到底有没有解决研发效率快的问题,以及同窗们在落地过程当中有没有Follow业务架构的标准,这一切都是未知的。
在痛定思痛之后,咱们认为数据化+自动化是解决这些问题的一个较好的思路。所以咱们首先从源头对代码进行管控,经过commit,将代码与后台的需求以及bug一一关联,对于不符合要求的commit信息,不容许进行代码合并,从而保证了后续数据报表分析的数据源头是健康的。
在完成代码和任务关联后,经过webhook就能够比较轻松的完成后续的工做,将每次的commit有效的关联到咱们的持续集成平台的任务上来,经过闲鱼CI工做平台将平常打包自动化测试等流程变为自动化的行为,从而极大的减小了平常的工做。粗略统计下来,在去年自动化体系落地的过程当中单就自动打Flutter包上传以及触发最终的App打包这一流程就让每位同窗天天节省一个小时以上的工做量,效果很是明显。另外,基于代码关联需求的这套体系,能够相对容易的构建后续的数据报表对整个过程和结果进行细化的分析,用数据驱动过程改进,保证新技术的落地过程的收益有理有据。
回顾一下上下文,
除了本文说起的各类方案外,闲鱼目前还在多个方向上发力,并对针对Flutter生态的将来进行持续的关注,分享几个如今在作的事情:
让工程师去从事更多创造性的工做,是咱们一直努力的目标。闲鱼团队也会在新的一年更多的完善Flutter体系的建设,将更多已有的沉淀回馈给社区,帮助Flutter社区一块儿健康成长。