简介: 分手快乐,祝你快乐~git
做者:祈晴编程
===============架构
闲鱼是第一个使用Flutter混合开发的大型应用,但闲鱼客户端开发最深刻体会的痛点就是编译时长影响开发体验。在Flutter+Native这种开发模式下,Native编译速度慢,模块开发没法突破。闲鱼集成了集团众多中间件,不少功能没法经过flutter直接调用,需使用各类channel到native去调用对应功能。总而言之,闲鱼目前Flutter开发面临以下几个痛点:socket
此项目从立项至今已经很长一段时间,因为业务迭代快,native插件满天飞状况下,想要作到工程模块化拆分难度可想而知;以下图是项目立项为模块化拆分,业务方须要将各个业务拆分解耦合,拆分集团中间件,业务封装组件,Native业务代码,Flutter桥代码,Flutter组件库,Flutter侧业务代码等多个模块;项目初衷就是整理代码,提供一个Flutter可运行的干净环境,同时须要让flutter能够获取到native几乎全部能力,可是编译开发调试时候有想要速度快,效率高。能想到的最直接解决方案就是拆包,从0-1创建一个最小壳工程,而后拆分集团基本中间件,封装业务组件,Flutter插件等,以下是整个项目架构: async
平常模块化单页面级须要使用最小壳工程,其内部又channel的声明和实现,经过运行最小壳工程运行获得结果,Flutter侧模块开发经过IOC调用到最小壳工程的channel获得返回结果,最后将模块化开发以一种pub或者git依赖方式集成到闲鱼FWN主工程便可;模块化
业务模块化拆分历来都是一种吃力不讨好的活,明知道拆出来有收益,可是投入产出比不足,所以历史包袱代码愈来愈厚重,以致于下一个接收的人都不敢轻易修改代码;在模块化拆分时候,开始项目时候提出过新起一个干净的工程,而后一步步拆分集团中间件,期间拆出了Mtop/Login/FlutterBoost/UI Plugin,耗时3周/2人,获得部分结果就是新业务,新界面开发知足基本快速迭代开发,缺点也很明显以下所示:性能
(1)若本身是业务方,须要为Flutter侧去拆分包,去构建一个最小化壳工程,其成本是巨大的。
(2)Fass工程一体化依赖一个最小化壳工程的Native运行环境去运行Flutter侧代码,但是并不是全部的业务方都会提供一个最小化壳工程去运行Fass,那么Fass工程一体化/模块开发若是在集团其余运行环境下进展?
(3)最小化壳工程运行环境没法紧跟Native侧的各类版本,会致使运行结果不一致状况下也不敢随便使用;测试
若是解决此问题呢?我的提出过跨进程实现方式,在Android端侧跨进程调用实现方式一直很常见的场景,client访问server得结果,而Flutter侧和Native侧不就是client和server双端么?以下图所示,其实Flutter获取数据就是经过MethodChannel/EventChannel获取,所以能够换一种方式思考? 阿里云
期间在Android侧我使用过Android Binder去实现,新起一个APP作为壳工程,其内部实现了各类插件去访问主工程服务,获取结果真后返回给壳工程的Flutter调用,可是维护成本依然在;同时iOS侧没有对应的实现机制,所以此方式被抛弃;spa
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就能够验证结果;
好比以下的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数据正常收发的可行性,后续还须要在业务场景具体实验耕田;
结果对比:
没法方案1和方案2最终均可以解决编译运行时长的问题,但方案1在拆分模块和维护模块时候都有很高的成本,运行时长虽然下降了,可是模块化工做量却加大不少,方案2能够完美解决拆分红本和维护成本,可是不足之处就是运行环境苛刻,可操做性不足,其须要2部手机做为运行环境,另针对于一些页面跳转逻辑,可能客户端手机A触发到服务端手机B上,操做性不在同一台手机上;固然方案二虽然有必定缺陷,却能够解决不少问题,所以后续在闲鱼模块化拆分落地项目中,在思考是否有更加完美的解决方法。
原文连接 本文为阿里云原创内容,未经容许不得转载。