Flutter在字节跳动的现状与工程实践

  • 推广 Flutter 技术

第三点就是针对于 Flutter 的推广,这个问题很容易理解,更多的开发者使用 Flutter,建设 Flutter 的资源就会越多,这样就会形成一个积极的循环。

建设规划


围绕着以上三点,我们做了一个“3 + 1”建设规划,就是应用框架、基础服务和引擎框架三大方面加上周边建设:

  • 引擎框架目标很明确,就是为了提高 Flutter 的基础能力,所以我们对其做了大量的性能优化,包括渲染卡顿、内存回收、启动速度等。在启动速度方面我们提升了将近百分之一百,与此同时体积也压缩了百分之五十,除了这些基础的性能优化外,我们还支持多窗口、端内复用等更多的能力;

  • 基础服务方面主要是针对研发流程,线上高可用和降级的灰度策略等,换句话说就是除了敲代码之外的研发流程;

  • 应用框架层我们对外说的比较少,应用框架层主要是为了打造一个开发者生态,或者称之为应用开发生态,更注重基础组件、工程方案、工程效率、最佳实践等,帮助业务更好地实施他们的工程;

  • 除了左面的三大点外,我们也很注重 Flutter 的周边建设,包括对 Flutter 技术体系的培养,推出培训课程、建立 Flutter 知识库等,同时我们也很注重开发者的交流,建立交流大群、信息同步群等等。

工程实践


接下来分享三个我们觉得通用性比较强的方案,看一下为什么我们会觉得它很强。

FlutterW 研发套件背景

为什么开发 FlutterW 研发套件呢?主要是源于三个问题:

  • 第一点,作为基础技术部,我们要服务的业务很多,我们希望他们使用 Flutter 的前提就是可以更简单地安装配置好 Flutter。但 Flutter 的安装配置很繁琐,同时不同技术背景的开发者配置起来也会有不同的感受,比如说专攻 H5 的同学配置这个环境就会很繁琐。Flutter 引擎是一个全局变量,它需要安装在每一个同学的系统里面,而整个业务团队内每个人所使用的引擎版本不尽相同,就会导致因环境问题而产生异常现象,花大量时间处理这个问题显然是很不划算的;

  • 第二点,Flutter 作为一个跨平台语言,无论是开发、编译还是调试,它的整个链路都特别长。简单来说就是:一旦应用中出现了一个问题,它可能发生在 Dart 上,可能发生在 Native 上,还有可能发生在这个业务同学的电脑环境配置上,导致问题排查起来十分不方便;

  • 第三点,当我们有方案需要实施的时候,很难把方案立即部署到具体的业务团队的设备上,在实施方案的时候也具备一定的难度。

功能介绍


基于以上三点,我们需要一个能把用户的整个开发流程包裹起来的研发套件。比如说安装配置 Flutter、使用 Flutter 进行开发、发布 Flutter 的产物、调试 Flutter 的问题等一系列的开发流程。目前 FlutterW 主要具备四大方面的能力,如下图:

第一个是环境方面,它需要把开发环境处理好,具体来说就是帮助开发者快速部署好 Flutter,还要保证环境的基本属性一致、引擎版本一致;需要有一个独立的沙盒,某一个项目的配置不应该在一台设备上相互影响;还需要有一个依赖配置表。

同时 FlutterW 还需要具备一些工程方案的能力,可以让我们的业务通过它进行简单的实现,为什么不用文档呢?因为在各式各样的业务方团队面前,文档的效率并不高,所以我们让这个套件作为一个载体让业务团队在实施方案的时候更轻松、更快并且失误率更小。

第三个是左下方的橙色部分,也就是 Support 能力。业务方往往会遇到各种问题,找到我们的时候,我们该如何解决?跨团队、跨部门的合作已属不易,更何况还有很多是跨地区的。由此我们给 FlutterW 加了一个功能:采集用户的错误信息,直接调用我们的解决方案并部署。

最后的绿色部分是 Dryrun 功能,就是用来体验 Flutter 的功能。我们在公司内部做推广的时候发现,很多团队都有体验 Flutter 的意向,但是 Flutter 的安装势必会污染本地的电脑环境,导致他们放弃体验。FlutterW 的能力就是可以直接运行 Flutter,不需要配置。

总结下来 FlutterW 的功能中最核心的只有两点:

  • 便准化工程环境

  • 自动化工程能力

标准化工程环境


以一个标准的 Flutter 工程为例,由于每个用户的电脑内的环境都不一样,那么在你关注这个工程本身的同时还需要关注用户本地的环境配置。我们不可能永远知道业务团队同学在使用什么样的配置,每次处理问题都进行沟通的话,就会在无形之中多出了太多的成本。Flutter 把环境直接配置在了项目内部,通过 FlutterW 安装的项目,它的内部除了代码资源外,还会有一个依赖配置表,里面会将此项目的一些信息,诸如基础的依赖、SDK 版本、打包工具等,都描述清楚,然后根据这个配置表来自动拉取相应的资源从而形成一个沙盒环境,在这个环境内进行独立开发。

自动化工程能力


第二个是自动化工程能力。如上图,当业务团队需要反馈问题的时候,FlutterW 会收集更多信息提交给我们团队,我们基础技术部收到信息后会将问题还原并提出解决方案,这时候我们可以将解决方案部署到 FlutterW 的云端服务器,FlutterW 会自动更新这些方案从而为业务团队解决问题。通过 FlutterW 这个媒介,我们可以尽量地拿到用户信息并将解决方案实时部署。

以上两个是 FlutterW 最核心的两个能力,简单总结来说就是:

  • 标准化工程环境,保证项目成员环境⼀致,问题可回溯。

  • ⾃动化工程方案能力,协助排查问题、解决问题,实施⽅案。

  • 提供低成本体验 Flutter 能力,降低 Flutter 的推广难度。

  • 配合研发流程打包发布 Flutter 产物。

容器化工程方案背景


这里的容器化工程方案更多的是指 API 的容器化。Flutter 是一个跨平台语言,跨平台框架,身为一名开发者听起来实在是很美好——做一个 App 就可以各个端去跑。但当开发者真正着手开发一个跨平台应用的时候,就会发现需要了解的实在是太多了:Dart 是不可避免的,还有 Java、OC、平台相关语言… 因为我们终究还是要依赖平台的能力,这样要求还是比较高的。正因为一般的开发很难兼顾好各个平台,所以我们很多业务开发会同时配置一个 iOS 和一个 Android,当平台出现问题时分别跟进,但这样很浪费人力。那有没有办法让我们的业务只关注在 Dart 上呢?不用再去管这些所谓的平台能力呢?显然是可以的。

多端、多业务交叉部署


我们引入了一层标准化的 API,用来规范化各个平台提供的所有能力。平台实现这样一套标准能力后,Dart 业务的开发方就不需要再烦心于各个平台的问题,面向这一层 API 进行编程就可以了。也正是引入这一层,使我们可以达到真正的多端、多业务交叉部署。

Flutter 作为一个渲染框架,自身就保证了运行环境的渲染能力的统一,而容器化 API 又保证了各个平台、各个端的基础能力的统一,有了渲染能力和基础能力的统一,业务方就可以真真正正的用一套代码跑在各个端上了。

业务隔离部署


这其实是对于开发者的开发体验的一个优化。既然是面向 API 编程,那么就不需要关注最终运行在哪里了。对于开发来说,在开发时运行一个 H5 版本岂不是更方便、更轻巧?最起码不用再连接一部手机。在实际交付的时候,再打包成产物,交付到实际的设备上。

体系

那么容器化 API 是一个什么样的体系呢?

如上图,整个容器化 API 最重要的部分是上方的红色部分,我们需要固定 API 层并将其标准化,所以它有一个独立的 API 版本管理。但仅仅提供一层 API 也是不够的,以字节跳动为例,公司内部的 App 特别多,每一个应用都要重复实现这些接口的实现并不科学,所以我们为它提供了一套默认实现——而且这个方案可以支持他们随便移出这些实现,修改成自己的实现或者做一些其他的扩展。

经历了这么多的考量,最终得出了这个容器化 API 方案的最终产品,大概总结下来就是:

  • 容器化标准化了各端的基础能⼒;

  • 分离开发关注点,降低开发难度;

  • 业务开发、部署环境可分离,可以⾯向 Web 端提供更快更好的开发体验,最终部署在 Native 获得更好的⽤户使⽤体验。

ByteRedux 状态管理方案背景


Flutter 是一种典型的响应式 UI 框架,这种框架的特点就是 ui = f(state),重要是状态而不是 UI。只要我们能管理好一个应用的状态,那我们整个应用的页面或者行为就始终受控。但 Flutter 这种框架,它状态分散在各个组件当中,各个页面之间要互动、流转,从而导致这个操作十分烦琐,很难维护。

选择

所以我们要尝试研究之前前端的一些经验,目前业界的主流就是 Redux,那它有什么优势呢?

如上图,左侧的图更像我们平时的一些应用开发,各个元素间相互调用,各个对象间也经过各种循环来调用,一旦项目相对庞大,就会没有人敢随便动这个项目,项目就会逐渐失去可维护性。反观 Redux,它提供了一个角色,把所有的状态收归在其内部,除了参与元素外,主要是对 UI 的监听,监听那些状态有着什么样的改变。同时,我们也更容易知道它内部会发生哪些变化。

简单看一下 Redux 的原理,上文提到的管理角色叫做 Store,Store 内持有了整个应用的所有 State,同时它也持有着所有的 Reducer——用来更新这些状态的。正因为数据和更新方法都在控制范围内,整个应用就拥有较高的可控性。

大致流程就是:我们的页面元素 View 发送一个事件通知 Store,在 Store 内部会根据这个事件进行处理并遍历所有的 Reducer,哪一个 Reducer 对这个事件感兴趣就会更新的状态,同时 View 元素就会自动改变。重点在于这是一个单向的数据流和单一数据源。

缺陷

当我们真正使用 Redux 的时候,就发现 Redux 有一些不足之处:

  1. 入门成本偏高,但维护成本显著降低

  2. 全局只有⼀个 Store 是个大问题

  • 牵一发动全身,全局计算导致性能问题

  • 无法多人协作开发

  1. 状态是静态定义的
  • 数据结构复杂化

  • 状态缺乏生命周期

由此,我们也尝试比较了一下目前市面上其他的状态管理方案:

可以看得出来,各个解决方案都有其优缺点,但结合我们自身应用的规模化考虑还是觉得 Redux 这种强管理、数据状态可控的模式更适合我们。

方案介绍

那么之前的问题关键在于哪里呢?

  • 关键问题:Store“超重”,Store 需要负责具体 UI 互动细节,又要负责管理全局状态两个角色;

  • 核心思路:引入更高⼀级的概念取代 Store 成为单⼀数据源,管理全局。

回想我们的状态本身,状态分布于页面上,而且状态之间往往会有一些归属关系,例如有的页面在登陆后才会存在,有的信息在登陆后才会有。由此我们选择了一个树状结构,也就是我们的自研方案——ByteRedux。

高性能、低成本学习


我们引入了一个 StoreTree 来承担这个单一数据源这个角色,引入这个角色以后有什么好处呢?第一个就是高性能、低学习成本。为了避免引入开发时候的误区,我们选择了让 Store 来代理这棵树让 Store 和单一数据源完成整个交互。

举一个例子,我们的 UI 发送一个 Action 到这个最左边的 Store 的时候,这个 Store 不会直接在内部处理而是转发到根节点,从而投派给 StoreTree 这个角色,让 StoreTree 管理这个 Action,帮你去寻址到你想发送的 Store,这时它内部才会进行计算并更新内部的状态。这个过程对于我们普通开发者来说,和原本的 Redux 用法基本上没区别,并不需要额外的学习成本。高性能又是怎么理解呢?如下图,这个 UI 元素能影响到的仅有它的目标 Store,其余四个节点的状态完全不会受干预,也不会导致其他无关的界面的任何的 ReBuilt。

架构师筑基包括哪些内容

我花了将近半个月时间将:深入 Java 泛型.、注解深入浅出、并发编程.、数据传输与序列化、Java 虚拟机原理、反射与类加载、高效 IO、Kotlin项目实战等等Android架构师筑基必备技能整合成了一套系统知识笔记PDF,相信看完这份文档,你将会对这些Android架构师筑基必备技能有着更深入、更系统的理解。

由于文档内容过多,为了避免影响到大家的阅读体验,在此只以截图展示部分内容

注:资料与上面思维导图一起看会更容易学习哦!每个点每个细节分支,都有对应的目录内容与知识点!



这份资料就包含了所有Android初级架构师所需的所有知识!
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!
知识笔记PDF,相信看完这份文档,你将会对这些Android架构师筑基必备技能有着更深入、更系统的理解。

由于文档内容过多,为了避免影响到大家的阅读体验,在此只以截图展示部分内容

注:资料与上面思维导图一起看会更容易学习哦!每个点每个细节分支,都有对应的目录内容与知识点!

[外链图片转存中…(img-s6GhDBOZ-1714706486684)]
[外链图片转存中…(img-CWtypDVj-1714706486686)]
这份资料就包含了所有Android初级架构师所需的所有知识!
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值