闲鱼对Flutter-Native混合工程解耦的探索
作者:祈晴
1. 闲鱼Flutter现状
闲鱼是第一个使用Flutter混合开发的大型应用,但闲鱼客户端开发最深入体会的痛点就是编译时长影响开发体验。在Flutter+Native这种开发模式下,Native编译速度慢,模块开发无法突破。闲鱼集成了集团众多中间件,很多功能无法通过flutter直接调用,需使用各种channel到native去调用对应功能。总而言之,闲鱼目前Flutter开发面临如下几个痛点:
- Flutter侧混合编译速度慢,Android首次编译10min+,iOS首次编译20min+;
- 混合栈编程中历史包袱导致IOS/Android双端返回给Flutter侧的数据可能存在不一致性;
- 集成模块开发效率相比模块开发较低,单模块页面测试性能数据无法展开;
2.解决方案一
2.1方案概述
此项目从立项至今已经很长一段时间,由于业务迭代快,native插件满天飞情况下,想要做到工程模块化拆分难度可想而知;如下图是项目立项为模块化拆分,业务方需要将各个业务拆分解耦合,拆分集团中间件,业务封装组件,Native业务代码,Flutter桥代码,Flutter组件库,Flutter侧业务代码等多个模块;项目初衷就是整理代码,提供一个Flutter可运行的干净环境,同时需要让flutter可以获取到native几乎所有能力,但是编译开发调试时候有想要速度快,效率高。能想到的最直接解决方案就是拆包,从0-1建立一个最小壳工程,然后拆分集团基本中间件,封装业务组件,Flutter插件等,如下是整个项目架构:
日常模块化单页面级需要使用最小壳工程,其内部又channel的声明和实现,通过运行最小壳工程运行得到结果,Flutter侧模块开发通过IOC调用到最小壳工程的channel得到返回结果,最后将模块化开发以一种pub或者git依赖方式集成到闲鱼FWN主工程即可;
2.2 阶段性产出
业务模块化拆分从来都是一种吃力不讨好的活,明知道拆出来有收益,但是投入产出比不足,因此历史包袱代码越来越厚重,以至于下一个接收的人都不敢轻易修改代码;在模块化拆分时候,开始项目时候提出过新起一个干净的工程,然后一步步拆分集团中间件,期间拆出了Mtop/Login/FlutterBoost/UI Plugin,耗时3周/2人,得到部分结果就是新业务,新界面开发满足基本快速迭代开发,缺点也很明显如下所示:
- 拆分梳理Native的中间件繁琐,工作量巨大,最小化壳工程耗时3周/2人
- 推动业务方拆分基础组件库更难,目前项目进展不顺
- 维护成本高,拆分壳工程运行结果和主工程可能不一致
- 业务迫切其结果,但投入产出比不足,比如Flutter单页面性能测试,Flutter侧模块化拆分,Fass工程一体基石
3.解决方案二
3.1 换位思考
(1)若自己是业务方,需要为Flutter侧去拆分包,去构建一个最小化壳工程,其成本是巨大的。
(2)Fass工程一体化依赖一个最小化壳工程的Native运行环境去运行Flutter侧代码,可是并非所有的业务方都会提供一个最小化壳工程去运行Fass,那么Fass工程一体化/模块开发如果在集团其他运行环境下进展?
(3)最小化壳工程运行环境无法紧跟Native侧的各种版本,会导致运行结果不一致情况下也不敢随便使用;
如果解决此问题呢?个人提出过跨进程实现方式,在Android端侧跨进程调用实现方式一直很常见的场景,client访问server得结果,而Flutter侧和Native侧不就是client和server双端么?如下图所示,其实Flutter获取数据就是通过MethodChannel/EventChannel获取,因此可以换一种方式思考?
3.2 IPC跨进程通信,Android Binder
期间在Android侧我使用过Android Binder去实现,新起一个APP做为壳工程,其内部实现了各种插件去访问主工程服务,获取结果然后返回给壳工程的Flutter调用,但是维护成本依然在;同时iOS侧没有对应的实现机制,因此此方式被抛弃;
3.3 具体方案:Hook代理+Socket服务
Android开发应该都熟悉hook和插件化技术,其实从之前的Flutter到Native的Chanel架构就可以想到一种思路,既然解决不了Native问题,那就解决Channel的问题吧,Native端侧的IPC方式无法实现,换到Flutter侧和Native侧的Channel通信侧去实现IPC吧。参考业务对于插件化hook机制/IPC机制的理解,结合自身对于flutter channel的理解,可以实现一种利用socket服务去hook method channel和event channel实现方式,去代理客户端的method channel和event channel,将处理结果通过socket交给服务端去处理拿到服务端真正的method channel和event channel数据即可,这才是我心中想要的实现方式就是如此,整个架构图如下:
客户端是一台手机,服务端也是一台手机,服务端跑闲鱼FWN主工程,客户端跑一个干净的Flutter工程;客户端先通过Flutter侧代码去找使用本端有对应的Channel,如果有则使用返回结果,如果没有则通过Socket请求结果到服务端主工程上,主工程根据Socket定义的协议字段去解析然后发起一个channel拿结果,之后通过socket将解决返回给客户端,客户端拿到了socket结果数据后执行想要的渲染方式即可;
或许你有质疑点:比如为什么要用2台手机,使用一台不可以么?
这里我推荐使用2台手机有如下2个原因:
(1)一台手机运行2个APP,如果server在后台可能会导致进程资源被回收,Socket通信中断;
(2)使用2台手机有一个极大好处是,你运行Android的Flutter侧Client代码,但是往往你需要验证Native侧双端Server代码数据,如果客户端手机/服务端手机是2台,只需要改下客户端的IP地址去请求Android手机的Server还是IOS手机的Server就可以验证结果;
3.4 尝试验证
比如如下的method channel代码如下:
Future<T> invokeMethod<T>(String method, [ dynamic arguments ]) async { assert(method != null); final ByteData result = await binaryMessenger.send( name, codec.encodeMethodCall(MethodCall(method, arguments)), ); if (result == null) { throw MissingPluginException('No implementation found for method $method on channel $name'); } final T typedResult = codec.decodeEnvelope(result); return typedResult; }
修复result == null的场景,如果是我们指定的客户端,则通过socket去拿server数据,重点理解Fish MOD:START到Fish MOD:END代码思想就理解了;
Future<T> invokeMethod<T>(String method, [dynamic arguments]) async { assert(method != null); final ByteData result = await binaryMessenger.send( name, codec.encodeMethodCall(MethodCall(method, arguments)), ); if (result == null) { //Fish MOD:START //throw MissingPluginException( // 'No implementation found for method $method on channel $name'); //socket从服务端手机获取值 final dynamic serverData = await SocketClient.methodDataForClient(clientParams); //Fish MOD:END } final T typedResult = codec.decodeEnvelope(result); return typedResult; }
最后通过此中方式验证了MethodChannel/EventChannel数据正常收发的可行性,后续还需要在业务场景具体实验耕田;
4.结果对比和展望
结果对比:
无法方案1和方案2最终都可以解决编译运行时长的问题,但方案1在拆分模块和维护模块时候都有很高的成本,运行时长虽然降低了,但是模块化工作量却加大很多,方案2可以完美解决拆分成本和维护成本,但是不足之处就是运行环境苛刻,可操作性不足,其需要2部手机作为运行环境,另针对于一些页面跳转逻辑,可能客户端手机A触发到服务端手机B上,操作性不在同一台手机上;当然方案二虽然有一定缺陷,却可以解决很多问题,因此后续在闲鱼模块化拆分落地项目中,在思考是否有更加完美的解决方法。
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
mPaaS 移动安全加固正式公测上线:公有云限时免费体验
由于 Android 系统本身的开源特性,使应用极易遭到盗版侵袭、反编译破解等攻击,严重影响应用的数据与隐私安全。为解决上述问题,在 APK 上传至应用市场之前,先对 APK 进行加固并对加固后的 APK 进行兼容性测试,可最大限度保障应用不被破解。 mPaaS 移动安全加固,为 Android 移动应用提供稳定、简单、有效的安全保护,提升应用的整体安全水平,力保应用不被破解和攻击。 源于阿里淘系应用前沿的安全加固能力 高稳定性:全线覆盖安卓机型 —— 从 Android5.0 到 Android Q,全线覆盖主流安卓机型与系统版本。同时支持 Arm、Arch64、X86、X64,更可在复杂环境下稳定运行,兼容性行业领先。 高安全性:源于淘系业务深度锤炼 —— 源自于淘系、支付宝等亿级业务的深度锤炼与安全性考验,性能稳定,充分保障应用安全性。 产品原理及核心能力 通过对 Android 应用重新编译、加壳保护、修改其指令调用顺序等手段来增强应用的反破解能力。在加固过程中,注重加固强度与兼容性并重,避免一般加固功能由于盲目追求加固强度而导致加固后应用完全不可用的问题。 充分保障 App ...
- 下一篇
Flutter 快速上手,秒变大前端
Flutter是什么 Flutter是谷歌的移动UI框架,可以快速在iOS和Android上构建高质量的原生用户界面。Flutter可以与现有的代码一起工作。在全世界,Flutter正在被越来越多的开发者和组织使用,并且Flutter是完全免费、开源的。简单来说,Flutter是一款移动应用程序SDK,包含框架、控件和一些工具,可以用一套代码同时构建Android和iOS应用,并且性能可以达到原生应用一样的性能。Flutter简介 配置开发环境 Flutter开发可以在macOS,Linux或Windows上完成。虽然您可以在Flutter工具链中使用任何编辑器,但IntelliJ IDEA,Android Studio和Visual Studio Code的IDE插件可以简化开发工作。 下载flutter SDK。地址 将flutter的bin目录添加到path中。 执行flutter doctor命令,他会安装flutter框架,包括dart,而且提示你任何其他需要安装的依赖。 安装其他依赖。 在IDE中安装flutter插件。 体验 ▐ Android Studio (为Flut...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- CentOS8编译安装MySQL8.0.19
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS7,CentOS8安装Elasticsearch6.8.6
- MySQL8.0.19开启GTID主从同步CentOS8
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
- Windows10,CentOS7,CentOS8安装Nodejs环境