嘉宾 | 张龑
编辑 | 马红伟
在大前端盛行的行业背景下,利用跨端技术来快速迭代新业务已经成为一种潜在趋势。在一众跨端技术选型框架中,后起之秀 Flutter 脱颖而出,各大企业纷纷开始尝试使用,但结果却呈现了“两边倒”态势,有成功的也有失败弃用的,失败的原因暂且先不论,本文中我们不妨来探索一下 Flutter 成功的秘诀。
如何构建一套体验不亚于原生端、并且具备复杂绘制(类小游戏)能力的 App 框架?如何深挖 Flutter 的“潜能”?如何时宜地使用 Flutter 来节约当前团队的研发成本?带着这些问题,我们采访到网易有道 Android 开发专家张龑,他将结合网易有道 Flutter 项目的诞生始末、落地情况、落地难点,为跨端研发人员概括一些在实际研发中闭坑的方法,以及对于一码多端(Web 端和 PC 端)上的探索和展望。
InfoQ:请介绍一下现阶段 Flutter 在网易有道的落地情况?谈谈你们的落地规模、落地成果、做了哪些优化?
张龑:网易有道在大力发展新方向的业务探索上面,几乎都有使用 Flutter 进行落地的实践,尤其在近年新涉及的素质类教育等业务上使用得更加广泛,老业务上也在使用 Flutter 进行各种切换的尝试。
我们在使用 Flutter 开展新业务后,对比未使用前整体人力成本差不多能节约 35% 以上,这是个非常可观的数字。上线之后,在大规模的用户验证下,体验和原生差别甚微,甚至于我们后来探索的一些较为复杂的交互过程用户都觉得特别流畅。
在决定启用 Flutter 作为新切入点的前期,我们做了很多的 Demo 以及预生产工程,把之前习惯的一些设计模式、书写规范都套用在 Flutter 上,并生产出了一系列高可复用的框架和工具类,这些工具类包含了对复杂计算的简化、对各种碎片屏幕的适配转换、各种日常使用的组件提取、网络工具、性能监控以及监控的日志系统等,具体的细节下面会讲到。
InfoQ:基于 Flutter 框架,“构建一套体验不亚于原生端、并且具备复杂绘制(类小游戏)能力的 App 框架”的技术实现思路请简述一下。
张龑:所有流畅的体验感都来自于对细节的处理,因为流畅感其实是人眼视角对屏幕绘制方法的一种直观反馈。Flutter 框架本身对于复杂绘制的支持就很友好,毕竟 Skia 引擎已经在 Google 体系中沉淀了很多年,在图像处理上做了很多类似以前 Android 的内存大户——Bitmap 的内存优化,图像计算上的浮点运算也非常精确。
在此基础上,我们还对插值器、绘制计算上的大量拆分以及细节参数进行了调优,包含数据计算的加载时间点等,配合上强大的 Flare 引擎(现在叫 Rive)对骨骼的拆解控制,再通过大量的测试数据对比以及对低端手机的验证(在 2014 年买的 Android 手机上面运行得很流畅),在这种复杂交互绘制的应用上面的表现依然很接近原生。
InfoQ:落地过程中有遇到过什么大的挑战吗?如何解决的?
张龑:首先,在复杂的交互式绘制中,对于 Flutter 这样一个不熟悉的平台框架,初期对其手势和绘制计算的掌握和优化是很磨人的一件事,我们利用了大量的 Demo 实验,来对所需业务的手势参数以及计算中的坑点进行总结统计,最后沉淀了几个最综合和典型的例子作为参考样本,其他的都沿用经验并具体处理。
其次,在 Flutter 混合开发中,其和原生渲染引擎的切换过程在先前版本中很慢,当然有的团队为了避免这个问题,直接进行了双引擎的混合切换渲染优化,但我们还是坚持把 Flutter 的业务放在叶子节点上面,避免“来回的切换过程”,在新版本的 Flutter 中这个速度已经大大提升了。
第三,由于要跨端的原因,屏幕的碎片化由一个平台变成了两个平台,如果设计多套布局,那么适配起来的工作量特别大。基于此,我们开发了一个动态计算布局的适配工具,且用一套基础的设计稿去尽量满足“非变异的屏幕”,又对一些计算和调用方式进行了优化,将整个适配的工作委托给这个适配器来完成。
在解决了上述的问题之后,最后就是在代码架构上面如何设计能更方便地进行统一开发。之前都是各端的开发,架构和工程代码质量的标准化,在后续流程中特别是团队有新人进入的时候显得尤为重要,我们结合自己的业务、结合平日大家的一些开发习惯以及设计模式,重新在 Dart 中构建了一套比较符合我们开发的结构流程,让大家开发起来更舒服和标准。
InfoQ:对于 Flutter 的性能优化,你们做了哪些实践?最后沉淀了哪些经验?
张龑:总结下来,主要进行了以下 4 点优化:
在普通的列表展示页面,我们更注重的是在异步数据更新和加载过程中,何种数据的提前加载不会对列表的滑动造成卡顿问题,在数据更新的过程中,尽量保持一种差分更新数据的策略,避免大量的 Widget 重绘导致页面的闪动刷新问题。
避免将一些不依赖动画更新的 Widget 添加进动画树里面,让频繁执行动画的树去刷新那些不需要更改的树,以免造成内存和渲染性能上的浪费,并创建一个单独绘制的子树,提高重复绘制的性能。另外,在布局计算边界上,尽量让子布局的计算不去影响父布局的运算,从而导致整个树的不必要刷新。最后,将重复性高的、有规律的背景或者线条使用 CustomPainter 里的 Canvas 来进行绘制,减少图片的 I/O 过程。
在浮点运算上面,可以采用适合的小数精确度来解决,由于“尾随机”产生的自定义绘制上的不精准 bug,以及因此出现的不必要的重复计算。
对于复杂的绘制拆解,可以对过长的骨骼动画进行分段的控制方式,减少一次性通过引擎加载的内存量,降低出 bug 的几率。
InfoQ:你认为 Flutter Web 值得尝试吗?是否适用于所有业务?
张龑:Dart 语言发明之初就是用来写前端的,在设计风格相差不大的情况下,通过共同组建组装的方式,轻易便能完成移动端到前端的一些部署,能够节约大量的开发时间。但目前 Flutter Web 的问题还很多,并没有想象中的那么完美,如果是大型的前端项目要落地生产线的话,还是选择多年沉淀下来的稳定前端技术更合适一些,对于前文提到的一些小业务,或者通用性高的业务,还是很值得去尝试的。
InfoQ:Flutter Web 还有哪些想象空间?有什么建议可以给到其他想实践的团队?
张龑:我们正在尝试使用 Flutter Web 进行在线互动,通过定义精简指令集以及 online 的实时通信计算,帮助老师和学生进行远程的实时互动,比如老师在互动题版上画了一个几何图形,可以让学生在上面连接辅助线或者补全图形等,这些操作都可以在同样的题版上进行,而且能做到 Web 和移动端互动、移动端和移动端互动、PC 和移动端互动等。
同时,我们也在使用 Flutter Web 开发用于推广的 H5 页面,整体效果很好。但是如果在和其他端组件没有互通性的业务上,并且业务本身也很复杂的情况下,多年发展下的纯前端技术肯定更合适,Flutter Web 的运用还是要结合具体的应用场景。
此外,Flutter-PC 同样很值得期待,我们已经用其做了一些实践,包括将移动端代码直接打包成 PC 应用,以及新探索的一些业务打包成纯 Native 的产物之后,体验和性能方面都很不错,测评也不错。如果之后 Flutter-PC 推出正式版了,我们将会考虑使用这个技术来替代现有的 Electron 框架,这样整个团队的很多组件,代码结构将能够实现最大的技术复用,同时也会节约很多设计资源。可以想象一下,如果开发各端的代码都是从组件库中直接拿来进行组装,开发成本无疑将会大大降低。
InfoQ:Web-App 一体化实现过程中,有没有遇到一些坑点?如何解决的?代码可复用性是如何做的?
张龑:Web-App 一体化的过程中,首先需要注意的就是一些组件库的引入以及页面的通信过程 document.on["js2dartEvent"],不是所有组件库都同时支持 Web 版本,这个时候可能需要自己做一些改造,Window 的对象也要变成 import 'dart.html' 中的 ui.Window。
其次,在移动端上的手势坐标换算成 Web 版坐标的时候,要注意相对坐标和绝对坐标的转换问题,另外在编译成了 Web 之后,再去加载其他的 html 需要申请 HtmlElmentView 并将其使用 Ui.platformViewRegistry.registryViewFactory(webViewId, (int viewId) => _iFrameElement) 的方法注册进去。
除此之外,我们在做一体化的过程还会遇到 background 覆盖掉绘制的 CustomPainter、iframe 加载中的刷新闪动等问题,不过随着 Flutter Web 的正式版发布,这些问题都已经解决掉了,其余绝大部分的代码都是直接可复用的。
InfoQ:当下跨平台开发已经成为业界一个大的研究课题,从 Hybrid 的方案到 React Native、Weex、再到 Flutter,技术框架纷繁杂乱,那我们应该如何选择适合自己的跨平台开发框架呢?技术选型上需要注意哪些事项?
张龑:一般我们在技术选型时会先拟定一个业务目标,然后围绕业务目标展开大致 4 个维度——性能、平台稳定性、工期、学习成本的分析,再围绕着制定的维度进行 Demo 实验、数据性能对比,结合团队现阶段、长期阶段的业务目标和技术目标进行选型。例如此前我们发现 React Native 的绘制在很多低端机器上面非常消耗性能,因此需要寻找一个方案去逐步替换 React Native,便围绕此目标展开尝试,并一步步转移到生产线上,最终实现了使用 Flutter 节约掉很多初期探索业务的开发成本。
再例如,除了大家都熟知的移动端跨端方案 React Native、Weex 等,目前我们对外的授课系统是用 Electron 或者 Qt 的技术框架完成的,Electron 很容易上手,但是由于使用的人很少且版本迭代还有历史性的 bug 很多,最主要的是 Chromium 这个裁剪版的 Chrome 内核不合适做太多重型客户端的事,兼容性和性能也很一般,复用性低。而 Qt 虽然性能好,但是要求团队 C++ 的能力水平很高,众所周知打造一支多人 C++ 开发日常业务的团队不太现实,这种情况下我们就会很期待 Flutter-PC 的稳定。
综上所述,对于开发框架的选择只有一个真理,即永远都只有最适合自己团队的,而非最好。
InfoQ:您观察到的大前端、移动端的发展趋势是怎样的?跨平台会出现大一统的局面吗?
张龑:近年来随着互联网领头羊们的大力推进,大前端时代确实已经呈现出一定的势头,而且随着各种产品的迭代更新以及框架的不断优化升级,势必会成为新业务最好的助力,同时在探索新业务方向上也能节约很多成本,此外很多语法上的逐渐统一也在推动着很多移动端的同学进行转型。
尽管如此,反观过去的很多很好的跨端技术,例如 React Native、Qt、Electron 等,最终都没有能真正地取代原生移动端,这是因为无论从设计理念还是性能追求上,短期内跨端都无法替代原生端。跨端旨在解决开发成本问题,一旦将兼容平台的因素考虑进去,就无法集中精力去解决更深层次的性能问题。
但是 Flutter 是不一样的,它致力于把产物编译成为各个端最贴近原生的东西,这就大大提升了运行的性能,它更像是一个强大的翻译官,这也是 Flutter 值得期待的原因。不过即便如此,Flutter 仍旧还有很长的路要走,需要大量的生态去验证它的优势,时间会证明它的强大,短期内 Flutter 也会成为纯原生端更好的一种补充手段。
InfoQ:作为一线的客户端开发人员,您有什么建议想对年轻的前端、客户端开发人员讲的?
张龑:建议谈不上,技术每时每刻都在进步,后浪永远都来得更加的猛烈和精彩。我们应该把语言、技术框架看成是手中的工具,解决问题的思路和方法论才是最需要去关注的。所以每个热爱技术的同学都应该保持自己的进步,除了自己平时接触得最多的技术方向之外,也要多和其他方向的同学们保持交流,尽量做到触类旁通,深挖一个点之后再扩展到整个面上,让自己保持足够广的视野,技术都是在不断的交流和实践中成长起来的,有时间多参考优秀的源码设计思路,多写一些让自己赏心悦目的代码。
嘉宾介绍:
张龑,网易有道 Android 开发专家。2018 年加入网易,10 年以上移动端及前端开发经验,主要研究方向为跨端技术、交互绘制技术、高性能、高复用架构等,在定制化 Launcher、清理工具、动画引擎、Flutter 等领域具备大规模用户的实践经验。