11 月 23 日,字节跳动技术沙龙 | Flutter 技术专场 在北京后山艺术空间圆满结束。咱们邀请到字节跳动移动平台部 Flutter 架构师袁辉辉,Google Flutter 团队工程师 Justin McCandless,字节跳动移动平台部 Flutter 资深工程师李梦云,阿里巴巴高级技术专家王树彬和你们进行分享交流。前端
如下是阿里巴巴高级技术专家王树彬的分享主题沉淀,《闲鱼 Flutter 架构演进和实践》。算法
演讲内容大纲:数据库
闲鱼为何选择 Flutter 闲鱼的架构及其演进 回顾及展望
今天主要分享的内容是闲鱼一年半在 Flutter 的演进,但愿有某个阶段某个场景和你们契合,给你们一些启发和讨论。编程
先作一个自我介绍,我是闲鱼的架构负责人,在 2009 年就加入阿里了,2009 年是淘宝首次实现营收平衡的那一年,也是第一次双十一的那一年,在那之后迎来了淘宝快速发展的时期,在那个快速发展的背景下,我作了商家百亿级数据的在线分析系统以及营销系统,再后来就是用到无线,在无线时代时去作手淘上 0 到 1 的地理围栏和 LBS 的无线特性的项目。redux
闲鱼是 2014 年创立的,2015 年我来到闲鱼,经历闲鱼从早期到如今超过 2000 万 DAU 的发展阶段,这 4 年时间看到闲鱼这样涨起来。中间到了必定团队规模之后,我就去负责闲鱼架构,以适应闲鱼的快速发展。小程序
Flutter 也并非最完美的一种框架,没有最好的只有最适合的,究竟是什么场景让闲鱼选择了 Flutter?架构
大的业务背景是这样的。首先,闲鱼是 C2C 的交易市场,随着中国网购商品不断丰富,社会上存在愈来愈多大量的闲置物品,出现了这样一个存量市场,这个市场规模有多大?从闲鱼角度看,在 2019 年财年,就有 6000 万以上的用户发布了本身闲置的商品,这个数字还在增加,到如今天天已经有 100 万人发布超过 200 万件商品。而后,由于它是 C2C 对等交易,这样的用户会造成更多互动和社区属性,能够看到闲鱼里已经沉淀 130 多万个鱼塘,造成这样的社区。这是一个大的背景,就是高速发展的存量市场。app
端侧业务特色是什么样的呢?有几个特色:框架
业务心智。闲鱼是独立的业务心智,促使咱们把主要开发力量投在闲鱼 APP 上,有一些外投的场景,像投到手淘、支付宝或者外面的场景,那是少许的页面。less
高苹果用户占比。闲鱼的特色是苹果用户占比高。这个特色致使早期时咱们在设计上对设计的要求很是极致,两端的设计要求一致且要以苹果设计为主,这时候若是作安卓开发的同窗就能够想象到安卓这边的痛苦了。
C2C、社区业务背景。致使有不少互动、商品图片及视频的需求,另外快速发展期有不少大的业务尝试。
这样几个特色就致使咱们下大决心迁到用 Flutter 来作,那是到了 2017 年末的时候。在中间也曾经尝试过用 Weex。
如今闲鱼的技术栈是这样的:活动类和外投类的产品页面用 H五、小程序、Weex 开发;主链路、产品化的页面都用 Flutter 开发,这部分在闲鱼的覆盖率有 80%以上。原来 Native 开发同窗都转型到用 Flutter 开发了。这些是技术因素,此外还有团队也是很重要的因素,Native 同窗写 Flutter 会转换更快一些,闲鱼有 Native 人员基础,因此才有决心深刻去作 Flutter。
这是发展到如今闲鱼的架构:下面是从项目管理、打包平台、持续集成到自动化测试;中间的包括页面组件、Native 混合、音视频组件等;再往上一点是承载业务的容器,让业务可以把本身业务流程下沉的容器(Dynamic 和 Serverless)。
闲鱼架构的演进
这个架构怎么演进来的?大概有三个阶段:第一,混合开发,引入期。第二,规模化,发展期,愈来愈多页面和人员投入时,会面对一些新的问题。第三,一体化,新探索,成熟之后考虑下一步怎么探索,原来只是从跨两端角度提升效率,如今把云(Server)也考虑进去,从云端一体的角度考虑效能的进一步提高空间。
引入期一般作些什么事情?闲鱼从最开始要用 Flutter,到最后上线用了 3-4 个月时间,由于的前车可鉴少。可是到今年,杭州有一家初创公司,它从决定开始用 Flutter,到最后上线,只用了大概 1 个月,能够看出如今这个引入期成本已经降下来了。虽然降了,可是引入期最大的工做内容仍是在混合开发这一块,也就是怎么把 Flutter 混合到本身已有 APP,除非你是开发一个全新的纯 Flutter 应用。
混合开发大概包括几种:
第一种,混合栈,这是躲不掉的,怎样把页面混合起来,混合栈是这个架构图里的 Boost,已经开源了。
第二种,工程化,从代码管理到持续集成、打包、监控、高可用,这一串的链路。
第三种,音视频和资源一体化,这块主要是考虑高性能图片、视频组件与 Native 复用。
(1)单页面栈和多页面栈。从 Native 角度看,当新开一个 Flutter 页面,这个页面要在新的 Activity(以 Android 为例)中打开,仍是在同一个 Activity 里面面来回切换 Flutter 页面?若是每个 Flutter 页面都挂在一个新的 Activity 下,就算多页面栈的设计。反之就是单页面栈。哪一种更好?若是想跟 Native 自己的栈可以很好结合,逻辑和顺序都能彻底一致的话,那多页面栈是最合适的。那单页面栈适合用在什么场景?好比在 Flutter 页面弹出浮层或者 Dialog 的场景,这时直接在 Flutter 内部用 Navigator.push 就能够达到想要的效果,可是除此以外,大多数场景都须要多页面栈的方式。flutter_boost 这两种都支持。
(2)单引擎和多引擎。上面说的多页面栈涉及一个问题,每一个 Flutter 页面都挂在一个新的 Activity(以 Android 为例)下,那这些页面的 Flutter 引擎究竟是用同一个引擎仍是多个引擎呢?这也是混合栈要考虑的重要问题。直观来看,要适应多页面栈,那就用多引擎,每一个页面都起一个 Flutter 引擎最简单了,但最大的问题是内存会暴涨,虽然如今官方也想去深度复用这个引擎,让引擎可以很轻量化,可是目前这个尚未 Ready。因此从单、多引擎来说,目前咱们使用的方案仍是单引擎的方案。单引擎的方案的优势就是内存省了,全部的 Flutter 都在一个引擎里。
(3)单引擎虽然解决了内存暴涨问题,但因为引擎内的栈跟外面 Native 栈的页面生命周期不同,用单个 Flutter 引擎至关于把这个 Flutter 页面在 Native 页面间来回移动,Native 的生命周期跟 Flutter 生命周期要彻底对上,若是对很差的话就会出现转场白屏、埋点丢失、页面销毁不当等各类奇怪的问题。对单引擎设计来说,实现基本流程一般没问题,可是最重要的是里面细节问题特别多,须要仔细处理各个细节问题。这方面你们能够参考 flutter_boost。
这块问题一般是由于原来有 Native 同窗,如今有 Flutter 的同窗,要一块儿去开发。这个问题刚才有一个同窗也提问到了,这时工程到底怎么作?是用 Flutter 包 Native,仍是用 Native 包 Flutter?这是两种思惟方式。标准的 Flutter 是在 Flutter 下面挂了安卓、iOS 的工程,用这样 Flutter 包 Native 的方式。还有一种方式是能够反过来,能够把 Flutter add to Native 的思路,闲鱼用这个思路作了一个 flutter_boot 工具,已经开源,你们有兴趣也能够参考。
闲鱼原来在 Native 时期,有成熟的图片库,也有成熟的视频组件作滤镜、编解码优化之类优化的,这些组件在 Flutter 上要复用起来,若是用官方默认的方式在生产环境会发现性能有问题。
这是关于视频复用的方案,第一个官方直接给的默认实现,在没有格式转换的状况下问题不大,但须要格式转换时,性能就会有问题。以打开摄像头作预览的场景为例,默认的第一种方案是把 Texture 提取到 Java 或者 Flutter 运行期的 Buffer 对象里,每一帧经过 Channel 传递到 Flutter,再贴到 Texture 上,这个办法缺点是有一次读、一次写的过程,性能不是最优的。从咱们的实测看,iPhone 7 上 720P 的视频,这种方案平均每帧多消耗 5-6ms。
后来咱们尝试两种方案把性能提高,第一种方案的思路是“新方案 1”这样的,想办法把 Native 侧的 Texture 跟 Flutter 侧的 Texture 复用,直接传递 Texture ID,这个好处是没有往上 Dump 内存的过程。这种方式很差的地方是须要改 Flutter 的代码,打 Patch。还有一种更好的方案,这是咱们后来发现的,这种方案不用打 Patch,能够用 Surface Texture 直接复用的机制,在 Native 和 Flutter 侧是一样的 Surface Texture,就能够复用了,建议你们用这种方式更好一些。
工程变复杂了,页面变多了,这时候重要关注的点是怎样让人员之间、模块之间解耦。团队大了并行开发更多,有几个小组须要并行开发,再也不是一个大组了。另外,也要关注代码标准化。
刚才说到解耦,大概有两个层面之间的解耦:一个是“Card”的解耦,长页面中每一行是一个业务组件 Card, Card 之间让它们尽可能有少的互相依赖。还有一部分是 Card 的内部解耦,主要是解 UI、逻辑和数据分离的问题,这个问题业界有一种 Redux 方案,闲鱼作的 fish_redux 是把组件 Card 间的解耦能力加到了 Redux 上,即把一个大页面拆成多个组件,组件间也能够解耦解了。
规模化期另一个考虑的问题是高可用。Flutter 有一个特色,由 Flutter 引发的整个 APP 的 Crash 不多,更有效的方式是监控它的异常,对异常的监控就须要在这个阶段把异常作聚类、信息裁剪。另外,还须要对页面 FPS,可交互时长等的监控。
此外,CI 和自动化测试方面,在规模期也有一些事情要作,原来的自动化测试有一些没有办法用在 Flutter 上,例如基于 Instrumentation 和 UiAutomator 的测试方案。只有基于 Monkey 的能够直接用。
这里详细介绍下解耦时用到的 AOP 技术,AOP 是解侵入的手段, JAVA 和 OC 里这块都容易,可是在 Dart 里没有成熟方案。AOP 的一种解决方案是基于代码生成,另一种解决方案是在 Dill 里修改,Dill 是 Dart 到最终产物中间的语言。
这块的思路是什么样?原来有一个 APP 工程,你想对它加一些功能,这个用 AOP 方式切进来,让原 APP 工程感知不到。解决方案是让 aop-project 的 Main 引用 app-project 的 Main。打包时选生成 aop.dill,包含这两个工程全部的代码,再通过 aspectd 的处理,把两个工程的切面编织到一块儿,成为最终产出物。
怎么去改这个 aop.dill?Flutter 在编译中有一个很好的设计,它提供转换的能力,可让你在编译流程中拿到语法对象之后,对这个对象作本身的转换。基于这样的原理,读取语法代码的树,而后对它作遍历,把工程里的 AOP 注解解析出来,编织到一块儿就能够了。作 AOP 时要考虑,好比以 foo() 函数为例,是想在 foo 外面作界面插入,仍是在 foo 里面第一行和末行插入,甚至还有一种多是在 foo 函数中间某一行作插入,这些都是 AOP 不一样的编织动做,对应几个操做分别有不一样的注解。
第一个概念,动态模板是在业务层想作业务流程下沉时用到。好比一个交易链路里有可定制的优惠模块、物流模块、商品信息模块等,可能有很是多组件,能够把这个流程沉淀,变成中台,让上层多个业务可以复用组装,在这个主干的流程上作本身的扩展点。咱们的一个解法是用动态模板的方式,让 Flutter 支持模板化。
第二个概念是 Serverless 框架,能够把服务端的业务层代码跟客户端代码统一。
上面讲的是一体化的端侧的概念,下面这部分是服务端的容器,很重要的基础设施是 FaaS 平台和 ServiceMesh。标准的 Faas 不须要咱们本身去建,云上和阿里内部都有,咱们要作的是让 Dart 跑在 Faas 上,作一个符合 Faas 规范的 Dart 语言的 Runtime 容器,核心要解决什么问题?一个是让 Runtime 能适应不一样的 Faas 平台,它有足够的解耦性。另一个重要的问题是解决语言无关性问题, Dart 语言确定碰到原来生态如 Java 或者某种语言的,像闲鱼的是 Java 生态,那怎么去调这些异构服务,要解决语言无关性的问题。大概思路跟 Servicemesh 差很少,用 Sidecar 解决语言无关性的问题。
中间是通道服务响应框架,这个框架主要是通道层的抽象,为何要有通道层的抽象?原来是基于无线网关接口,如今基于 Faas,能够把这个 Faas 当作一个本地接口去调,甚至能够把 Faas 上跑的函数当作本地端的函数,这样去调用。
一体化的演进过程:第一步,刚才讲的是一个逻辑下移的过程,把原来端侧的逻辑写到了 Faas 层,这就是逻辑下移的过程。第二步刚才讲的模板是属于 SSR 范畴,属于把渲染下移的过程。最后是远景,把 UI 自动化生成掉。一体化的设想在没用以前业务可能有不少疑惑,到底这种开发模式你们会不会接受、能不能真的带来效率的提高,可是把这个推出来之后,咱们有几个业务基于这个去作,效果仍是很是不错的,把原来很是重的云端逻辑归一化了,例以下单页的逻辑,客户端下单要算优惠,组件联动,端侧有很重的逻辑,服务端也很重的逻辑,如今下单的业务同窗用一体化把这两个逻辑归一在一块儿,都到 Faas 上解这个逻辑时,就变得可维护性好了不少。
有的同窗可能会疑惑一体化后,端上原来多是有状态的,在 FaaS 上怎么解决端上状态的问题?
这个问题的解法有几个思路,首先对于客户端编程来说,咱们用框架把本地函数和 FaaS 函数统一封装路由,须要访问远程的状况才会路由到远程。另外,基于 Redux 的 Action 方式调用 FaaS,Action 中能够先把客户端完整 State 状态传到 Faas 上,Faas 函数能够知道客户端的完整状态。有一些反作用是请求次数变多,对于这种特殊状况须要注意。
一体化还有一个重要的问题是工程化归一,使开发 FaaS 像在本地工程在写客户端代码同样。工程化归一业界有开源软件 Serverless Framework 能够复用,亚马逊、Google 云在 Faas 层都有 CLI 工具组件,基于开源产品去打通工程一体化就容易一些。
大概回顾一下今天要讲的内容:
第一,闲鱼选择 Flutter 选择的因素,给你们作参考,闲鱼是在这样几个业务特色:独立 APP、双端一致性要求,苹果用户占比高;APP 中以产品化页面为主。另外,团队组成也是一个重要因素。
第二,闲鱼架构演进过程,从引入期、到发展期、到一体化的探索,每一个阶段要去考虑的问题和关键的技术点。
最后,展望接下来要作的:
Flutter 和 Faas 刚刚讲的一体化这块刚刚起步没多久,有一些典型的业务场景落地了,可是尚未大规模的作更多铺开。UI2code 尝试。另外,咱们但愿把 Flutter 基础设施、基础组件能力开放,能让超大型的 APP 使用。最后,Flutter 的社区生态,要靠你们一块儿共建和完善。
提问:我是一名 iOS 开发者,目前在作跨平台工做。跨平台开发者都知道阿里系有一个至关知名的 Weex,您也提到闲鱼在 2016 年主要业务都是由 Weex 去承载的,咱们也有作 Weex 的相关工做,它有可以作到多端同构,你能够自定义一些驱动,甚至部署到小程序上。从 Weex 更符合大前端、一体化的趋势,Weex 在跨平台以及原平生台上都有至关的表现。个人问题是,做为阿里嫡系跨平台优秀的引擎,是什么契机决定让大家闲鱼团队全面拥抱 Flutter?可以给咱们一个更好的参考。
回答:这个问题是场景的问题。Weex 跟 Flutter 在跨端、动态性、性能方面的表现,是一个三角形。目前的技术框架都很难把这三要素都解决很是好,好比你刚才说 Weex 很好,可以很完美的解决这三个问题,但其实 Flutter 的关键优点是在性能更好,比 Weex 好。Weex 的优点是它前端的生态和容器的普遍性以及它的动态性。说究竟是场景问题,闲鱼 80%是主链路场景,更可能是性能这块的考虑,只有 20%是属于外投活动类的,这部分咱们用 Weex、H五、小程序作,主链路仍是更高性能的选择。杭州那个创业公司例子也是这样的,在 RN 和 Flutter 中选择,最后敲定到了 Flutter 上。确实你们都有这个考虑,仍是看本身的场景想取哪方面。
提问:我以前看闲鱼公众号上有 UI2code,想看看大家最新的进展是怎样的,UI2code 在闲鱼 APP 上页面实际使用的占比率大概是怎样的?
回答:咱们有个本身内部创业 APP,一个全新的 APP,是用 UI2Code 作的,效果就很好。我我的以为 UI2code 目前技术水平适合作全新的 APP 或者全新的页面。作老页面最大的问题是修改、合并的问题。因此 UI2code 对全新 APP 页面做用很大,由于那个所有是用这个生成的。目前这个技术更适用这样的场景。
提问:大家用 Weex 差很少是最先的,咱们的 APP 也有 Weex。我想问的是大家对 Flutter 兼容 Weex 有相关的技术吗?好比我以前有些业务是 Weex 作的,如今想在 Flutter 中去作,有相关的技术调研吗?
回答:这两个调用的话,其实它们两个是独立的,Weex 若是渲染到 Native 控件,就像标准 Native 跟 Flutter 是同样的。可是 Weex 若是最后降级成 Web View 就变成 Web View 跟 Flutter 的问题,它们其实也是兼容的,如今 Flutter 上用 Web View 也是没问题的,Web View 本质上也是 Native 的,因此最后都会落到 Native 和 Flutter 的关系问题。若是你想作控件极的关联,好比 Weex 写的控件跟 Flutter 写的控件作交互,它们两个能够在同一个页面出现,这样就会比较难,目前没法实现。此外,若是是想让 Flutter 像 Weex 同样有那么灵活的动态性,目前也没有太好的办法。
提问:Faas 那一层是否是至关于把 Server 端数据库的模型转换成前端 View Model 的过程?另一个问题,Flutter 客户端协同开发是有组件化方案?仍是有其余的?好比开发过程当中调试的方案是什么样的?Flutter 多业务线同时开发一个 APP,有不少独立的组件,在开发过程当中是怎样调试的?好比运行在闲鱼壳 APP 里跑起来,经过什么样的方式可以跑起来?
回答:Faas 是否是指把服务端 DB 数据透出到客户端的这个问题。大概能够这样理解,从业务开发分层角度看的话,有客户端、中间业务胶水层,领域层。胶水层就是把各个领域的接口拿过来组装,组装成页面要的数据,返回给客户端。FaaS 作的是这一层,胶水层。
提问(续):至关于领域那一端接口仍是 Server 来维护,最后输出更偏向于 Server 输出的数据,客户端须要配置东西的话须要在这层上再封装一层、组装一层客户端须要的形式?
回答:FaaS 这一层就是上面说的胶水层,之前没有 FaaS 时这一层是服务端同窗写的,如今由客户端同窗用 FaaS 来写。Faas 这一层能够理解为前端一般说的 BFF 这一层,但实际上会比 BFF 的内容更多些,FaaS 解决的是烟囱式的逻辑,FaaS 的横向间依赖比较少,一个请求进来,向下层拿数据把它组装返回。领域层的区别是它有不少领域与领域间的横向依赖,用的书据存储也很是多,模型复杂,就不适合 FaaS,目前尚未用在 FaaS 上。
提问(续):怎样判断 Faas 具体运行在 Server 端仍是客户端?
回答:其实 Faas 都是在 Server 端,只不过有一个基于 Action 的客户端框架,让客户端开发时,本地逻辑和 Faas 逻辑都像写一个函数同样。咱们的想法是在 Faas 函数上加一个注解,剩下就都是同样的了,是这样的方向。
提问:我常常关注闲鱼的一些技术文章,您刚才提出两个观点,noUI 和动态模板下发。在 Flutter 中写页面时,有参考 Flutter Design 网站,把全部的组件进行动态拖拽生成静态模板,Flutter 趋势是否能够发展成只关注数据集和业务逻辑,剩下都交给工具集或者工具去作,这样就能够很大一部分解放了 Flutter 写页面的生产力,闲鱼有没有这样的考虑?或者提供这样的编译工具给社区来使用?
回答:这个其实在内部是有用的,可是目前尚未开放出来。你讲的那种方式多是更多偏工具化的,用工具去生成这样一个页面,它的代码仍是 Flutter 的代码。这种方式闲鱼有交集的是 UI2code,确实是直接生成 Flutter 代码,倒也不是拿一个图片彻底一会儿就生成了,也有你说的拖动过程,要作些修改、属性简单的设置,而后生成 Flutte 页面代码,这块是能够的,可是这块也没有开源。
Flutter 沙龙回顾 | 跨平台技术趋势及字节跳动 Flutter 架构实践
Flutter 沙龙回顾 | 如何缩减接近 50% 的 Flutter 包体积
Flutter 沙龙回顾 | Custom Widgets in Flutter
上海沙龙回顾 | 字节跳动在Spark SQL上的核心优化实践
字节跳动技术沙龙是由字节跳动技术学院发起,字节跳动技术学院、掘金技术社区联合主办的技术交流活动。
字节跳动技术沙龙邀请来自字节跳动及业内互联网公司的技术专家,分享热门技术话题与一线实践经验,内容覆盖架构、大数据、前端、测试、运维、算法、系统等技术领域。
字节跳动技术沙龙旨在为技术领域人才提供一个开放、自由的交流学习平台,帮助技术人学习成长,不断进阶。
欢迎关注「字节跳动技术团队」