为了账号安全,请及时绑定邮箱和手机立即绑定

开源|Fair 在 58 同城拍客 App 中的实践

标签:
Android Html5 iOS

 陈有余 58技术 2022-04-12 10:13

● 项目名称:Fair 2.0

● Github地址:https://github.com/wuba/fair

● 项目简介:Fair是为Flutter设计的动态化框架,可以通过Fair Compiler工具对Dart源文件的转化,使项目获得动态更新Widget的能力。Fair 2.0是为了解决 Fair 1.0版本的“逻辑动态化”能力不足。

本文旨在为大家提供 Fair 在实际项目中落地的完整案例,包含了使用 Fair 进行动态页面改造、复杂场景使用、接入过程中遇到的问题、Fair 接入前后的性能对比、热更新方案设计等等内容。


00 本文目录

https://img3.sycdn.imooc.com/62d53b400001512210800631.jpg

01 Fair接入和使用流程

1.1 接入 Fair

对于 Fair 的接入方式,推荐大家使用 源码依赖 的方式,下载地址如下:

git clone https://github.com/wuba/fair.git

在 pubspec.yaml 文件里添加依赖:

假如 Fair 源码和你的项目代码在同一个目录下

https://img3.sycdn.imooc.com/62d53b7d0001841e05940496.jpg

另外需要特别注意的是,Fair 内部通过 fair_version 库来做版本控制,所以大家需要根据自己本机的 Flutter SDK 版本来决定 fair_version 的版本。

以我的电脑为例子,我本地使用的 Flutter SDK 版本为 2.5.0,所以我强制指定 fair_version 的版本为 flutter_2_5_0:

https://img3.sycdn.imooc.com/62d53b97000190ab09480352.jpg

1.2 将 Widget 改造为可动态下发的 bundle

将一个 Widget 改造为可以动态下发的 bundle 需要三步。

第一步:改造 main 函数

需要去掉默认的 runApp(MyApp()),然后手动调用一下 WidgetsFlutterBinding.ensureInitialized(),最后用 FairApp 来包裹项目的根 Widget。

https://img2.sycdn.imooc.com/62d53bbb0001662210800618.jpg

第二步:添加 @FairPatch() 注解

在需要改造的 Widget 上添加 @FairPatch() 注解。

https://img3.sycdn.imooc.com/62d53bd50001af8a08300496.jpg


第三步:执行 build_runner 命令

执行了 build_runner 即可生成 bundle 资源

flutter pub run build_runner build

1.3 bundle 资源生成位置和组成

bundle 资源的位置,位于:project -> build -> fair 下。

https://img2.sycdn.imooc.com/62d53c0100011e1f08660554.jpg

一个 bundle 资源包含了两个文件:一个是 js 文件,一个是 json 文件。

  • js 文件里面包含的是动态页面的逻辑部分

  • json 文件包含的是动态页面的布局 DSL

1.4 使用 FairWidget 加载 bundle 资源

bundle 资源生成好以后,我们可以使用 FairWidget 组件对其进行加载。

在本地测试时,我们可以将 bundle 资源拷贝到 assets 目录下,然后将 assets 资源的地址通过 path 参数传递给 FairWidget:

https://img4.sycdn.imooc.com/62d53c240001055e10000352.jpg

运行效果如下:

https://img4.sycdn.imooc.com/62d53c3c0001c88c03200651.jpg

本地测试通过以后,可以将 bundle 资源托管到自己的服务器上,然后将 path 换成一个 bundle 资源的服务器下载地址即可:

https://img2.sycdn.imooc.com/62d53c530001e5a709160352.jpg

使用小结

到这里简单小结一下 Fair 的使用步骤:

https://img4.sycdn.imooc.com/62d53c6d0001a55610800233.jpg

对于一个简单的页面可以通过以上步骤进行改造,但是实际的项目中,情况往往比较复杂,所以,我们下面继续介绍在一些复杂的业务场景下,如何使用 Fair。

1.5 动态页面之间的参数传递

Fair 提供了两个注解,可以实现动态页面之间的参数传递:

  • @FairWell

  • @FairProps

这两个注解的区别是:

  • @FairWell 注解传递的参数不能参与逻辑运算,而 @FairProps 传递的参数可以参与逻辑运算。

  • @FairWell 支持 Dart 语言的所有数据类型,而 @FairProps 只支持传递一个 Map。

来看看示例代码。

@FairWell 的使用:

https://img4.sycdn.imooc.com/62d53ca90001a0d910160730.jpg

@FairProps 的使用:

https://img3.sycdn.imooc.com/62d53cc200014a3510160730.jpg

1.6 动态页面之间的跳转

推荐使用 命名路由 的方式进行跳转。

首先注册路由表:

https://img1.sycdn.imooc.com/62d53ce700013d7710800497.jpg

然后使用 Navigator.pushNamed 进行跳转:

https://img1.sycdn.imooc.com/62d53d02000123fc10800361.jpg

1.7 动态页面引用本地 Widget 组件:@FairBinding

对于一个本地的 Widget,如果直接在动态页面里面引用的话,Fair 无法完成 DSL 的生成,需要使用 @FairBinding 注解来对本地 Widget 组件进行标记。

https://img1.sycdn.imooc.com/62d53d250001dcdc08640784.jpg

标记完成后,可以运行 flutter pub run build_runner build 命令,运行成功后,会在项目的 src 目录下,生成本地 Widget 的组件映射表:AppGeneratedModule。

我们需要对 AppGeneratedModule 进行注册:

https://img4.sycdn.imooc.com/62d53d3f0001ef9110800271.jpg

然后就可以在动态页面里使用了:

https://img2.sycdn.imooc.com/62d53d5c0001822409820856.jpg

1.8 动态页面引用三方 sdk 的 Widget 组件:@FairBinding(package)

那很多人可能会有疑问了,假如我引用的是一个第三方 SDK 里的 Widget 呢?

我们依然使用的是 @FairBinding 注解,不过需要我们将三方 SDK 的 Widget 的 package 路径传递给 FairBinding:

https://img1.sycdn.imooc.com/62d53d820001ebde10800791.jpg

1.9 逻辑模版使用:FairDelegate

对于页面中一些固定的逻辑方法,我们可以将其抽离出来,放到 FairDelegate 里面,来降低 Dart 与 JS 的频繁通信,达到提升性能的目标。

甚至一些页面,只需要 UI 动态化,而不需要逻辑动态化,那么完全可以使用 FairDelegate 来实现。

使用 FairDelegate 方式很简单,首先自定义一个类,让它继承 FairDelegate:

https://img1.sycdn.imooc.com/62d53da60001ad9d10800847.jpg

然后,需要在 FairApp 里注册模板:

https://img1.sycdn.imooc.com/62d53dbe0001b3c110160496.jpg

1.10 使用 IFairPlugin 桥接常用第三方 SDK 的能力

我们在动态页面中,经常会用到一些第三方 SDK,比如网络请求、权限申请、拍照功能等等。

此时,我们需要使用 IFairPlugin 来桥接第三方 SDK 的能力,才能在动态页面中正常使用。

我们以权限申请库为例子。

首先,自定义一个类,继承 IFairPlugin:

https://img3.sycdn.imooc.com/62d53de2000111ea10800575.jpg

重写 getRegisterMethods(),在里面注册需要暴露给 JS 侧的方法。

同样的,定义好 Plugin 后,需要在 FairApp 里进行注册:

https://img3.sycdn.imooc.com/62d53dff00019a2109480766.jpg

然后就可以正常使用了。

02 Fair在58拍客中的落地Fair 上线后,我们也是在 58 拍客里进行了接入和使用。

我们完成了三个页面的改造:发布页面、视频列表页面、视频详情页面。

并且自己设计了热更新流程,下面,我们就来详细介绍一下。

2.1 58 拍客接入 Fair 前后 UI 对比

接入 Fair 前的页面:

https://img3.sycdn.imooc.com/62d53e3a0001f48910800800.jpg

接入 Fair 后的页面:

https://img4.sycdn.imooc.com/62d53e540001b0f510800800.jpg


Fair 能做到像素级别的还原,因此接入前后,页面几乎看不出任何差异。

2.2 更新方案设计

因为目前 Fair 的热更新平台正在开发中,还没有上线,因此,我们自己设计了一套热更新方案:

  • 方案1:同步更新

  • 方案2:静默更新

  • 方案3:预加载+同步更新

  • 方案4:预加载+静默更新

2.2.1 同步更新方案

采用同步更新方案时,每次进入页面会先请求版本号,如果有更新,则下载 bundle 资源(如已有缓存则不下载),最后展示。

流程图如下:

https://img3.sycdn.imooc.com/62d53e83000175e610801271.jpg

2.2.2 静默更新方案

采用静默更新方案,每次进入页面时,如有缓存则直接展示,并异步更新 bundle 资源。如无缓存,则使用同步更新。

所以,静默更新有 3 个特点:

https://img4.sycdn.imooc.com/62d53ea100016a0110800284.jpg

2.2.3 预加载

对于标记为预加载的资源,我们会在 APP 启动的时候就下载好 bundle 资源。

比如,我们改造 58 拍客的视频列表页,它位于首页的第二个 tab,对于这样的 tab 页,我们当然是希望它在 APP 启动时就进行预加载。

https://img2.sycdn.imooc.com/62d53ece00017cba10800851.jpg

预加载的方案一般不会单独使用,会选择与同步更新和静默更新一起使用。

总结

https://img3.sycdn.imooc.com/62d53efc00019fff10800538.jpg

2.3 如何保证 bundle 资源安全性?

如果我们在下载 bundle 资源的过程中,因为一些未知原因导致资源未下载完整,或者说下载了一个被恶意替换的资源,此时,去加载这样一个不合法资源的话,会发生一些未知的错误,增加 APP 的风险。

https://img1.sycdn.imooc.com/62d53f250001f60c06300658.jpg

那么如何解决呢?

我们的解决方案是采用验证 校验和 的方式。

我们将 bundle 资源上传到服务器时,会通过 MD5 或 SHA-256 计算出 bundle 资源的 校验和(Checksum),并由热更新接口下发。

https://img1.sycdn.imooc.com/62d53f410001af6010800533.jpg

客户端完成 bundle 下载后,重新计算一遍得出校验和,如果与服务端下发一致,则为完整且合法的,可以执行加载。

特别注意:上图中展示的校验和是明文的形式,实际开发中,需要大家对其进行加密后再下发。这样做是为了进一步提升数据的安全性。

比如拍客里面,是对其进行了非对称加密后下发,客户端解密后再使用。

2.4 兜底策略(防止未知错误)

在拍客里,我们加入了一个兜底的策略,即:由 Server 端控制加载 bundle 资源还是 Flutter 原生的 Widget。

伪代码如下:

https://img2.sycdn.imooc.com/62d53f67000148d810800689.jpg

这样做的目的是,当线上发生一些未知的错误而无法纠正时,能够及时控制。

2.5 Fair 落地过程中遇到的问题

第一个问题:图片模糊

https://img3.sycdn.imooc.com/62d53f8b0001ac7510800400.jpg

如上图所示,我们在完成改造后,加载页面发现图片变得非常模糊。

之所以会有这个问题是因为,Fair 将 DSL 转化为 Widget 时,默认取的是 assets 目录下的 1x 图,所以导致了在一些高分辨率手机下出现模糊。

解决方案是,1x 图不要使用低分辨率图片,且 Image 需要设置 width、height 和 fit 属性。

第二个问题是:colors 属性缺失

我们在代码里设置了一个颜色值:Colors.red[50]:

https://img1.sycdn.imooc.com/62d53fb600012fbe10800446.jpg

但是生成 bundle 资源后,发现 DSL 里并没有对于的 color 属性:

https://img4.sycdn.imooc.com/62d53fd00001f2f610800529.jpg

出现这个问题的原因是,Fair 暂时不能支持解析 Colors.xx[xx] 这种设置颜色的深度的语法。

推荐使用十六进制颜色值 Color(0xAARRGGBB)的写法。

03 接入Fair前后性能对比

首先同步一下开发环境和测试环境:

开发环境:

  • macOS Big Sur 11.2(Apple M1)

  • Flutter SDK 2.5.0

测试设备:

  • HUAWEI nova 7,Android 11

  • iOS iPhoneXSMax,iOS13.3

测试方式:

  • 非压测,模拟用户正常使用

  • 模拟了12个事件

测试页面:

https://img2.sycdn.imooc.com/62d5400a0001b0f510800800.jpg

3.1 页面流畅度

通过模拟 12 个操作事件,如滑动、点击、跳转来进行测试。

3.2 内存表现(增量数据)

接入 Fair 后,Android 端的内存占用 增加了 22MB,iOS 端增加了 17.9MB。

https://img1.sycdn.imooc.com/62d5403f0001a39910800593.jpg

3.3 启动时间(增量数据)

接入 Fair 后,Android 端的启动时间 增加了 0.03 秒,iOS 端增加了 0.1 秒。

04 Fair开发体验总结

我们在 58 拍客里接入和使用 Fair 后,对 Fair 有 3 个比较大的感受:

https://img4.sycdn.imooc.com/62d770f500013a8010800233.jpg

第一个感受是:保留Flutter原生开发习惯

使用 Fair 不需要学习新的技术栈,也不用适应新的语法习惯,只需要按照 Flutter 原生的写法开发即可。

第二个感受是:改造简单

将 Widget 改造为动态页面,只需要加上几个注解即可,方便、实用。

参考数据:58 拍客接入 Fair + 3 个页面改造排期:2 天

第三个感受是:bundle 资源大小控制合理

58 拍客改造了 3 个页面,这 3 个页面总的 bundle 资源大小才 8KB。

最后,欢迎大家使用 Fair,也欢迎大家为我们点亮 Star

git clone https://github.com/wuba/fair.git

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消