探索贝壳跨端之旅:从零开始的 Flutter 实战指南

发表时间: 2021-12-13 11:51

一门新技术的兴起,必定是为了要解决现有技术解决不了的难题。跨端技术也遵循这一规律。传统的纯原生开发已经满足不了日益增长的业务需求,而跨端技术的产生就是为了要解决这一难题,具体表现在动态化内容需求增大和业务需求变化快、开发成本变大这两大方面。

跨端技术的变革

跨端最开始起源于浏览器。在 App 内使用 Native 的 Webview 来加载 Html5 页面,这是移动端最开始的跨端实践。接着,为了使 Html5 页面能够更多地使用底层 SDK 或者补充性功能,Hybird 开始流行起来。此后,各种各样的跨端框架也随之流行而来, 例如 PhoneGap、AppCan。

2015 年 4 月,Facebook 发布了 React Native,主张“Learn once, write everywhere”,采用 Javascript 开发 + 原生渲染技术,类似的还有 Weex 和快应用。同年 Flutter 也发布了,不过当时关注的人较少,直到 2018 年的 1.0 版本发布,关注和使用的人才多了起来。

贝壳决定建设跨端能力

2018 年,由于贝壳公司内部业务发展较快、需求较多,贝壳开始做平台化能力建设,但因缺乏跨端能力,贝壳开始考虑跨端能力方面的建设。综合了团队构成、业务特点、团队经验以及业界的动态等因素,贝壳成立了 Flutter 架构小组(以下统称小组)。

之后,赵宏伟带着小组成员开启了贝壳跨端探索之旅。那么在这场旅途中,贝壳在接入 Flutter 的过程中都进行了哪些探索呢?为此,InfoQ 邀请到了 Flutter 架构小组负责人赵宏伟以及小组成员肖鹏、裴伟来跟大家一起聊聊贝壳的 Flutter 接入实践。

相比其他跨端框架,Flutter 虽有一定的优势,但刚开始,贝壳并没有直接就选用 Flutter。

“当时组内准备考虑一些跨平台的方案做一些小的尝试和调研。后来,到 2018 年底,Flutter1.0 发布,小组开始对跨端方案进行了深度调研。起初,组内是对 RN 和 Flutter 做调研,FE 团队(贝壳前端团队)负责 Weex 的调研和使用。”赵宏伟回忆到。

经过调研之后,Flutter 架构小组首先放弃了 RN。原因如下:

1.渲染通信路径长。React Native 的逻辑层和 UI 的描述层都在 js;

2.React Native 跨端技术的实现思路是适配,两个移动端(iOS 和 Android)仍然存在很多组件差异;

3.React Native 的页面层级问题;

4.动态化需求不多;

5.有一些公司宣布放弃 React Native,以及 React 许可的一些问题,不确定性较多。

对于 Weex,贝壳前端同学在某些业务上做了部分实验,得到的结论是「效果一般」,没有对他们整个前端的行为有多大改变,因此也放弃了 Weex。而对于 Flutter,首先 Flutter 主打的自渲染,UI 到逻辑都是自身一套;其次 Flutter 基于对应平台独立编译,更适合对应平台的运行,并且支持多平台;最后,Flutter 支持 JIT 和 UT 模式,既能兼顾开发效率,也能兼顾运营效率。

基于以上调研与考虑,贝壳最终选择了 Flutter。

选定跨端框架,接下来就是为接入做准备了。我们了解到贝壳在接入 Flutter 的过程中,整个项目经历了 3 个阶段。

正式开启跨端之旅

据赵宏伟透露,第一个阶段他们的目标是快速落地验证。在此阶段中,他们也遇到了两个难题。第一是如何保证既能进行 Flutter 开发,又不影响非 Flutter 开发人员,并且官方的既有工程集成方案对他们的工程侵入性较大,如果按照最原始的方式打包效率也比较低,因此他们设计了自己的集成方案; 第二是 Flutter 层面需要组件化,小组内部也设计了组件化方案来解决快速落地问题。

在以上问题得到解决之后,他们在部分业务进行快速实验,“当时的业务场景下反馈还不错。”赵宏伟说。接下来就来到第二阶段。在这一阶段中,他们更全面注重开发和用户体验。因此,他们将建设分为了三部分。

第一部分是 Flutter 在持续集成方面建设;

第二部分是业务开发核心基础库能力建设(标准化);

第三部分是容灾降级及稳定性方面的建设。

完成了以上三部分的建设,项目就到了第三阶段,贝壳 Flutter 架构组开始进行多端一体化能力建设。2020 年,小组针对 Flutter for web 做了一些研究,并做出了贝壳 Flutter For web 的容灾降级系统。

“今年(2021 年)我们一直在研究 Flutter 多端一体化方案, 官方也在 2.0 中主推了 web 方向的应用,目前主要是在优化 Flutter For web 的应用体验,包括加载和部署优化,内存优化,三端一体化的核心基础补充以及监控埋点能力补充。我们内部的监控系统一部分 Web 页面也是使用 Flutter 来开发的。”赵宏伟说。

截止到采访时,贝壳 80%(24 款)的 App 已经接入了 Flutter。

当提到这么多 App 接入 Flutter 之后带给贝壳的收益时,赵宏伟兴奋地说:“效率,这个是我们最直观的感觉,整个 2020 年贝壳所有业务线都参与过 Flutter,一致的感受都是效率,以前两个人开发,现在一个人就可以完成。”

除了最直观的效率上的提升,贝壳还做了一整套的标准化 UI 组件(UI 组件 160+,业务组件 30+),这是属于他们的技术创新。并且参与此项目的成员在 Flutter 容器、Aop 编译、监控、UI 自动化和动态化等相关研究中还积累了大量的技术经验,进一步提升了个人技术能力。此外,据小组成员萧鹏描述,他们还将这些 UI 自动化的创新技术整理成文章进行了输出,把创新技术分享给更多有需要的开发者们。

Flutter 的成功接入,虽然让贝壳在跨端能力方面的建设得到了很大的提升,但有些问题,目前小组仍没有较好的解决方案,首先就是内存问题。当提到内存时,赵宏伟略显苦恼。“除了容器自身的内存,还有图片的一些边界场景和释放,以及一些三方图片库存在图片释放和图片过大的 Crash 问题。”赵宏伟说。

针对这一问题,贝壳采用了共享引擎和图片纹理方案来管理 Flutter 图片缓存,虽然得到了很大的改善,但当业务场景中出现多容器时,容器消耗内存仍然不小。再者是 Platform View 和渲染问题,Flutter 在 Platform View 上有一部分内存和适配问题。据了解,这些问题需要深入到引擎内部,更改成本较高,贝壳便没有过多的干预,而是选择了积极适配新版。最后是动态化问题,在 iOS 侧,小组内部没有比较好的动态化方案,目前只保证维护好性能和低成本。

除了以上的一些问题,据赵宏伟透露,贝壳在引入 Flutter 之后,还产生了一些新问题。主要体现在以下几点:

一、集成问题

首先是在第一阶段的集成方案不够完善,RD 反馈调试较为困难,QA 反馈打包时间长,错误率高。其次是配置问题,因小组内部在 Flutter SDK 上做了定制,所以配置起来就比较困难;再加上多个项目的 Flutter SDK 版本不一致,开发人员需要手动组合 Flutter tools 和 Flutter SDK,这一过程增加了成本;还有在打包的时候,需要先将项目在制定打包机上打包,再在主包上打包,这个时间就比较长。于是,他们就重新做了一版集成方案,并开发了 Flutter SDK 自动化集成更新方案,目前在 Flutter、Dart 编译部分就如同一个 target 任务,和其他代码一起进行并发编译,大大提高了效率。

二、包大小问题

在 C 端落地时候,贝壳更加关注包大小。贝壳当时接入的 Flutter1.12.13,其包大小一度达到 30MB,业务方紧急要求瘦身,因此 Flutter 架构小组通过改造 Flutter 编译器以及 Flutter Engine 对 Flutter 产物数据段做了分离压缩,可以内置,可以远程下发,并去掉符号表和无用文件,将包体积从 30MB 缩小到 17MB。在后续的规划中,赵宏伟说:“我们会结合 Flutter for Web 方案,在远程下发失败后动态转到 for Web 页面,提高页面打开成功率,保证业务正常流转。”

当在使用 Flutter for Web 时,Flutter 会将所有的业务打包成一个整包,但对于单独访问一个页面的场景,加载就会比较慢,为解决这一问题,小组对整个包进行了拆分,并把资源上传到 CDN 来解决加载问题。

三、异常监控问题

在做异常监控时,在瘦身模式下,获取的堆栈是没有办法被解析的,很多都是系统堆栈问题,没有办法进一步解决,并且很多都是没用的堆栈,无法将问题细致化。针对这一问题,贝壳做了一套后端的分流解析服务。这套服务首先是将 Flutter Exception 进行符号化,然后对堆栈进行分析,去掉无用和白名单内的堆栈行后进行归因,如果归不到业务方,再采取页面分流的方式,来达到异常监控的效果。

四、图片导致的内存问题

Flutter 本身内存方面消耗比较严重,而自己又是一套独立的图片缓存,因此,在大图片场景下,加上 Native 的图片内存占用,整体图片导致的内存问题比较多。面对这一问题,贝壳采用了外接纹理方案,让 Flutter 和 Native 共享一份图片缓存,以及在大图场景下使用外接纹理方案进行边界处理,这对内存都做了比较好的优化。

除了以上的一些问题,还有一个比较大的挑战。“在引入 Flutter 之后,当面对产品经理的提出的一些交互效果,Flutter 本身技术能力又无法支持时,就需要小组内部去找其他的解决方案来解决,可能会考虑原生,或同时使用原生安卓来做。”小组成员裴伟补充道。

虽然产生了以上这些新问题,但好在小组内部也提出了相应的解决方案。同时,贝壳也一直在寻找更优的解决方法。

贝壳 Flutter 的成功接入也让贝壳开发团队的开发效率得到了跨级别的提升。但在此过程中,贝壳也遭遇到过一个“灵魂拷问”—— 效率 or 体验?

一个永恒的话题—— 要效率,还是要体验?

一款产品,一个 App 被生产出来,就一定是为了满足用户的某种需求,被人们使用的,因此用户体验要好。而对一个公司而言,一个高效率的组织形式也非常重要,高效的开发可为公司解决人力、财力等各方面的问题。当问到在接入 Flutter 的过程中,贝壳是如何权衡效率和体验的问题时,赵宏伟的回答是比较明确的。

“像直播、视频、地图这一类的,不建议使用 Flutter,其他的在使用 Flutter 时,该提效的就提效,该注重体验就注重体验。当然每个 App 也有自己的着重方向,要根据自己的场景去舍弃效率或者体验。”赵宏伟表示。

在面对贝壳的业务场景时,贝壳引入 Flutter 的关键目标就是要解决开发效率问题。因此,为了进一步提高开发效率,贝壳也做了接下来的打算。首先是研究多端一体化,即将 Flutter 能够应用到 web 上,包括加载、部署优化、内存优化,以及三端一体化的核心基础补充和监控埋点能力补充。目前也在研究中。

其次是在应用上,贝壳内部会有一些 Native 和 Html5 的业务要做。Html5 主要面向站外,贝壳希望能够用一套代码解决三端需求,这样提效会更进一步。赵宏伟接着说:“我们可能会考虑 Flutter 在 VR 渲染上的一些应用,不过目前还在设想阶段。”

除此之外,贝壳对新的 OS,比如 鸿蒙,在兼容模式下也做了适配,并开始着手做了一些准备。“当然也要配合公司的产品策略,是否大范围的适配跟进。”赵宏伟补充到。

写在最后

本次的 Flutter 接入实战,不仅解决了贝壳的“效率提升”问题,还见证了贝壳从 0 到 1 的跨端能力建设。从最初不够完善的基础建设,到引入 Flutter 过程中所遇到的各种困难,贝壳都在实践过程中努力寻找解决方案并构建了一些属于贝壳的创新技术。