背景介绍
Android应用采用Java或Kotlin编写,iOS应用采用Objective-C和Swift编写,但当我们要去开发支持多端的应用,每一端都需要独立研发、测试,直到上线。为了解决多端独立开发的问题,跨端技术的方案备受青睐。
两轮跨端技术的尝试要追溯到2019年,当时整个互联网行业都在提效的大背景推动下开始各种跨端方案的尝试,而前身还是两轮助力车团队的两轮大前端也开始了跨端技术方案的探索。当时可供选择的跨端方案有React Native方案、Weex方案,H5离线化方案以及当时较火的Flutter技术方案,而我们的目标是希望能够找到稳定、适合我们业务特性,并且可以继续进行深耕的技术方案进行调研和尝试。
思考和调研
主流的跨端方案主要分为两种,一种是将JavaScriptCore引擎当作虚拟机的方案,代表的框架是React Native。另一种使用自渲染引擎的方案,代表框架是Flutter。
框架层+原生渲染方案
它的开发语言选择了js,使用的语法和React完全一致,不用于一般React应用,它需要借助原生的能力来进行渲染,组件最终都会被渲染为原生组件。虽然给用户带来比较好的体验,但性能方面不会很高。
以Web为基础的H5 Hybrid离线化方案
相对来说这是开发成本最小的一种方案,因为它实际上是在写前端的界面,和普通开发H5网页并没有太大的区别。这一方案所面临的主要考验就是性能问题以及因为网络延迟而带来的用户体验问题。在此基础上我们引入了离线化的方案,我们计划是通过CRM平台发放H5离线包到达本地,使用WebView进行本地资源加载,来突破性能问题。
框架层+自渲染引擎方案
这种方案和上面的区别就是,它并没有直接借用原生能力去渲染组件,而是利用了更底层的渲染能力,自己去渲染组件。这种方式显然链路会比较短,性能方面也会更突出,同时在保持多端渲染一致性上面,也会比前面两种方案更加可靠,这类框架的典型例子就是Flutter。
方案对比与选择
我们在选择跨端方案的时候,不只是要考虑常见的几种重要指标,如编程语言、性能、技术架构等来判断是否适合我们团队和产品,更多还要去考虑开发效率、社区支持等工程化方面的指标,同时还要考虑团队现状、所选方案的生态和技术未来的发展方向。
对比方案制定
在社区、成熟度趋于一致的时候,跨平台方案的性能就成了重要的考虑因素和指标。性能更佳的Flutter当然是首选。另一方面,对于原本是Native开发的人来说,React Native与Flutter开发成本是相差不大的,从长远角度来看,选择Flutter技术方案似乎是较为合适的选择。
同时结合当时组内的情况,我们选择了H5 Hybrid离线化和Flutter作为比较方案进行端测的尝试。H5 Hybrid离线化方案相对来说比较简单,而且自主性比较强,方案逻辑简单风险较小。如果试验成功我们可以走出一条自主创新的跨端路线,而Flutter技术方案性能较高,社区也比较活跃,UI渲染框架做的较为彻底,也是当时跨端技术方案的代表方向。所以我们综合以上的考虑,希望通过实践拿到关键的对比数据,为后续最终的方案选择提供支持。
Flutter是什么
Flutter是Google的移动UI框架,可以快速在iOS和Android上构建高质量的原生用户界面,它是Google一个新的用于构建跨平台的手机App的SDK。写一份代码,可以多端运行,Flutter同时可以与现有的代码一起工作,被越来越多的开发者和组织使用,并且Flutter是完全免费和开源的。
从官方的介绍来看,Flutter的特点可以总结成三点。一是跨平台,现在Flutter 3基本可以实现跨6种平台,甚至支持嵌入式开发。到目前为止Flutter算是支持平台最多的框架了,良好的跨平台性,直接带来的好处就是减少开发成本。二是原生用户页面,让我们的体验更好,性能方面也会更好。用官方的话就是平滑而自然的滑动效果和平台感知,为用户带来全新的体验。三是开源免费,同Android系统一样,这些都是免费开源的。
方案线上验证
经过一段时间的调研和分析以后,我们基本上确定了两轮跨端方案的技术选型和对比方案。本着谨慎的原则,我们花费了一定的精力在这两个方案的验证对比、以及跨端技术方案的推进方法探索上。
H5 Hybrid离线化方案
针对该方案,我们的计划是分两期执行。第一期是完成框架的核心代码,并在BOS端完成一个页面的改造上线,拿到线上运行数据。第二期是在第一期数据的基础上打造CRM平台,完善方案的系统应用。
该方案的基本结构分为代码层、协议层、Hybrid离线SDK层以及离线包管理系统。其中的代码层是符合特定标准的H5工程开发产生的代码。当H5页面产生资源或样式的申请时,容器层会检测资源请求事件,并根据资源协议寻找本地资源包对应位置的资源并进行加载。本地资源加载完成后,形成数据流请求响应返回H5页面。
数据的流转在框架中经历了数据分发、协议解析、目标执行、结果反馈几个阶段。通过在端侧建立协议层的封装,保证了协议规则统一。整个协议过程做了SDK化的封装,保证了核心框架层的稳定。
Flutter方案
针对Flutter方案的尝试,我们需要在一开始优先解决工程结构、打包构建、上线集成和原生工程混合开发的问题。
针对于工程结构,Flutter在业务上的应用需要一个Host载体,我们选择了Flutter Module工程作为Flutter业务工程的Host工程。向上输出构建产物对原生工程的继承,向下组装和集合业务工程,形成统一的模块注入、页面注册的形式。
针对Flutter项目打包构建、上线集成和混合开发的问题,我们需要结合当时公司产物构建和管理规则进行Flutter产物的构建。我们的方案是在Jinkens上面创建单独的Job进行Flutter module产物的构建,最终把生成的Android和iOS产物集成到原生工程中,实现Flutter环境和代码的集成。以上便初步搭建完成了Flutter业务开发工程结构,并实现了在原生工程中混合开发、构建上线的一系列流程。
比对实验
在页面上线后,我们很快拿到了一手数据。在稳定性方面两种方案表现都比较好,没有出现页面crash的情况。在加载性能、白屏或者异常方面,Flutter方案要比H5 Hybrid离线化方案表现得更好。综合以上运行情况对比,我们基本确定了Flutter作为主要的大前端实践方案。
Flutter应用推进
框架1.0阶段
我们对Flutter技术工程化应用生产了各种轮子,以求达到对Flutter规范、高效应用的目的,比如数据的流转分发、网络模块的封装、MVVM结构的组织等。这一阶段的框架主要分为页面路由管理、数据总线、工程框架、网络请求、资源管理和组件管理。总体来说在这一阶段,我们对于Flutter应用基本形成了初具雏形的工程结构体系和基础能力建设。
框架2.0阶段
在这一阶段Flutter的应用算是进入了深水区,随着应用场景的增加,各种问题暴露出来,其中比较棘手的问题有Flutter状态管理、页面生命周期和混合插件缺失问题。针对Flutter状态管理、页面生命周期的问题,我们基于原生开发的经验,搭建了一套符合 MVVM标准的Flutter代码基础结构。
这套基础结构主要分为Page与VM两部分,组织关系如图所示。Page与VM逻辑分开,Page是对VM的封装,VM是业务逻辑的独立单元,包含了完整的UI以及逻辑层Module。
下面是Page基础结构功能定义说明。在这一结构中,我们是以PageRoute为基础进行扩展封装。PageLifecycle定义了生命周期和生命周期状态值;LifecycleEventNotify负责生命周期事件的传递;LifecycleNotifyManager是生命周期框架的核心枢纽,所有的事件汇总到这里向下调用生命周期API;LifecycleFromFlutter和LifecycleFromBoost收集flutter原生和flutter boost的生命周期的调用,总结调整后按照新的生命周期规范发送事件;PageAnimation是页面动画的集成工具;WidgetProviderRender是页面UI搭建的工具类,负责provider、widget的创建和组装,最终形成完整的页面;ModuleBinder是页面对Module的绑定工具,实现了module宿主的绑定,当宿主销毁时module能感知到并做出相应的反应。
下面是VM层基础结构。VM层和Page层基本类似,不同的是它会根据VM的特点进行自身生命周期的感知和处理,处理方式相对Page会更加复杂。
Flutter Map组件
针对以地图为代表的原生混合组件能力的建设,当时flutter社区的能力支持不足,没有合适的轮子使用,但是我们的业务场景又是比较重地图的。在这种情况下,我们就需要自己去开发一套地图组件。
如图是Flutter Map的总体结构图,我们利用的是flutter框架的PlatformView机制,在原生层实现view的实例化创建,再交给flutter进行UI的渲染,实现了原生view在flutter体系中的渲染和展示。接下来我们需要在flutter层与原生view进行操控,这里需要我们能够准确找到对应的原生view,并通过channel通道传达相关的指令。原生view收到指令以后,会根据规则做出相应的动作,并产生结果原路返回给调用方。原生view产生一些事件状态时,比如地图的拖动、加载完成等事件,需要及时传递给flutter端,同时flutter端有一些状态也需要传递给原生端,以便业务侧和原生view侧都能够及时做出相应的反应。这里我们以channel为基础建立了双工通道,实现了flutter与 platform两端实现实时进行事件通知的能力。
Flutter标准体系化建设阶段
在这一阶段我们的flutter在两轮大量的业务场景的应用实践下,基本趋于稳定的状态,经过我们在这一阶段对flutter的体系化改造,形成了集合flutter工程搭建、开发调试、基础能力沉淀以及开发标准输出的体系化结构。
Git仓库整理
在仓库结构方面,我们整合了散乱的flutter仓库维护状态,形成了现在统一、标准、聚合的仓库集合。目前的仓库叫HBFlutter,包括中间件子分组、基础组件子分组、native能力子分组和其他子分组。
组件分类
在基础能力沉淀方面,我们对现有基础组件进行了梳理和标准化改造,基本形成了一套体系化的flutter基础能力建设。
技术总结
Flutter技术目前在两轮得到了应用和推广,在哈啰App两轮业务场景下基本实现了全量Flutter化改造,Bos App也在今年开启了Flutter技术改造,目前已经基本实现大部分流程的Flutter化改造,目前线上运行相对平稳。两轮业务在应用Flutter技术后实现了开发效率的大幅度提升,同时也很大程度上解决了Android和iOS UI不一致的问题。后续我们将继续追踪Flutter技术的发展,并视业务场景的需求进行合理的应用和推广。
(本文作者:田克雨)