关于flutter:Flutter技术在哈啰两轮的升级之路

44次阅读

共计 6321 个字符,预计需要花费 16 分钟才能阅读完成。

上一篇,咱们分享了 Flutter 在两轮的利用推广,本次分享的主题是 Flutter 在两轮的降级之路,次要分为两局部。一是咱们在 Flutter 落地之后,因为业务的倒退,导致咱们须要对 Flutter 进行降级。二是降级之后咱们遇到了一些问题,这里列举了一个比拟典型的案例——FlutterEngine 的自定义。

背景介绍

降级起因

两轮从 2019 年来开始应用 flutter,算是利用比拟早的团队。随后 flutter 降级了 2.x 版本,这个版本有个比拟重要的更新就是空平安(nullsafety),因为在 1.x 的版本,如果一个对象为 null 之后,再去调用它的任何办法,都会抛异样,在页面上的体现就是页面假死,也不会再刷新了。所以就须要研发人员关注下本人应用变量时是否曾经为空了,2.0 会强加这个查看,在编译阶段让咱们确定好次变量是否可空以及是否懒加载,这样在咱们应用变量时,就会有零碎揭示咱们,相当于帮咱们做了这个工作,缩小出错率。其实很多其余语言也都有这样的检测机制。再加上一些性能的提优,于是有了降级的诉求。

Flutter 版本敲定

这里比照了一下 1.x 版本和 2.x 版本的次要区别。2.x 版本优化了利用启动的提早,调用 Dart VM 的 GC 策略也做了一些改良,低端 Android 设施的初始帧出线间隔时间最多缩小了约 300ms。

此外,空对象音讯是咱们在开发中最常见的 exception,所以空平安是很有必要的。pub.dev 曾经公布了超过 1000 个空平安 package,包含来自 Dart、Flutter、Firebase 和 Material 团队公布的数百个 package。

降级施行

基础设施

基础设施降级次要波及到两方面,打包机 sdk 降级和集体研发侧版本管理工具。因为 SDK 降级在 2.8.1 版本删除了一些被弃用的 Api,会造成咱们编译报错。可参考以下批改:

  • 切换本地环境为 2.8.1 版本
  • 运行工程查看报错
  • 批改对应报错库的 api
  • 本地运行胜利后,挪动端 CI/CD 零碎发库提测
  • 测试同学全量回归,灰度

降级节奏

整体的降级节奏包含:根底降级启动、各个业务线研发启动、业务线 null_safety 革新 1 - n 次、全量回归、灰度公布和异样观测。

本次 2.0 降级工作量根本都在 null_safety 语法革新上,因为工作量大波及范围广所以无奈在短期内实现语法革新。所以本次革新将按照业务性能划分,尽量收拢每次语法革新带来的影响范畴,从而引起测试侧屡次全量测试的人力节约。

Flutter 空平安

健全的空平安已在 Dart 2.12 和 Flutter 2.0 及更高版本中可用。Dart 3 及当前的版本将只反对健全的空平安。

有了空平安,上面代码中所有的变量都是非空的:

依赖项查看

应用官网工具进行类型推断,最好确保依赖的所有包都曾经降级为 null safe 版本。所以在革新时应该从底层向上逐渐降级。

在控制台执行如下命令:
dart pub outdated —mode=null-safety
能够查看到以后我的项目工程依赖包是否是 null safe。

对于其中不符合要求的外部库,须要手动降级后打包,生成 null safe 版本,倡议版本号比之前最新版减少一个大版本;对于三方库能够在三方库官方站点或者中文站点查找合乎 null safe 的最新版本号,并批改我的项目工程的 yaml 文件。

全副依赖批改实现后,执行命令拉取最新依赖:

flutter pub get
flutter pub upgrade

操作实现之后能够应用第一步的命令查看是否依赖都校对完了。

如果提醒所有依赖都申明反对 null safe,或者冀望跳过依赖查看,就能够进行后续操作。

执行胜利之后,会生成 url,点击可在浏览器中应用迁徙工具:

左侧是须要迁徙的文件目录,官网工具会对选中文件进行类型推断并主动增加如下标记:

  • ! (类型不可为空)
  • ? (类型可为空)
  • late (初始化延时)
  • late final (一次性初始化延时)
  • required (参数标记)

面板两头能够看到具体改变,右侧能够看到以后文件改变的起因。

对于不须要迁徙或者大型文件须要分步迁徙的场景,能够只勾选须要迁徙的文件目录,官网迁徙工具会主动为不须要迁徙的文件顶层增加 null unsafe Dart 的版本正文。

引入反对 null safe 的第三方库

在更改本地代码时,个别都须要先降级依赖的第三方库,然而在官网第三方库下载地址 https://pub.flutter-io.cn 或者中文站点 https://pub.flutter-io.cn 中查问到依赖库的最新版本。

常见谬误及批改形式

  • 谬误 1:The non-nullable variable ‘preferences’ must be initialized.

起因:非空的动态变量或者顶层变量在申明时没有初始化,诊断器就会报此谬误。因为 flutter 对于没有初始化的变量会主动赋值为 null,而该变量并没有申明可空。

解决办法:

  • 谬误 2:The method ‘*‘ can’t be unconditionally invoked because the receiver can be ‘null’.

起因:可能为空的变量间接应用 ’.’ 调用办法,诊断器就会报此谬误。

解决办法:

  • 谬误 3:Map<int, list> 简单类型批改

解决办法:

  • 谬误 4:The default ‘List’ constructor isn’t available when null safety is enabled.

解决办法:

FlutterEngine 的 Crash 问题

在上线之后,安稳运行一段时间,咱们也继续察看监控数据,发现在 native 有大量新类型 crash。

[FlutterEngine destroyContext] 相干的 crash

解体起因是在用户敞开利用的时候会告诉到 flutter 引擎进行重置操作 (调用办法看【FlutterEngine destroyContext】)。重置的时候因为官网问题导致 engine 被屡次重置销毁。在第二次的重置的时候(OnPlatformViewSetSemanticsEnabled 这个办法里边获取引擎指针的时候解体了)

发现问题后,咱们去翻看了官网 issue,发现官网曾经修复该问题,不过是在后续版本上修复的,联合咱们本身状况(此 crash 产生是在后盾)咱们外部研究了几种解决方案:

  • 解决方案 1

官网曾经修复了该问题,并曾经合并代码到 master。

修复的 pull request:https://github.com/flutter/engine/pull/30835/commits

bufix 的形容:
https://github.com/flutter/flutter/issues/95844

合并到引擎 commit 记录:https://github.com/flutter/engine/commit/fb3ee7f2b5e6e537a2a83c9fe2cf733cd9c6ec06

在高版本,2.10.2(2.10.3+) 以上此类问题已修复。

2.8.1Cherrypicks:
https://github.com/flutter/engine/pull/30355

但目前无奈间接降级到 flutter 版本到 2.10.2 以上,临时的解决办法是本地 cherry pick 代码,编译打包 flutter engine,替换 2.8.1 的 flutter engine。

危险最小然而须要评估改 pull request 的 cherry pick 老本,须要根底技术支持打包计划文档,须要摸索引擎打包交融到 flutter 批改打包,再到 flux 打 flutter 包流程交融。

  • 解决方案 2

flutter 引擎从 2.8.1 降级到 2.10.4

  • 解决方案 3

能够 hook destroyContext 在判断利用状态是否调用 destroyContext,但这个有肯定危险可能会影响其余的逻辑。

  • 解决方案 4

梳理引擎切换的场景,通过业务上防止减小 crash 产生。

最终咱们抉择了激进的计划肯定制 FlutterEngine。

环境筹备

git
Python
Xcode
depot_tools 创立目录 /Users/xx/Desktop/flutter-engine
depot_tools 装置(须要全局 VPN 否则很慢)

cd /Users/xx/Desktop/flutter-engine
git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git

配置环境 (~/.zshrc 或者~/.bashrc)

export PATH=/Users/xx/Desktop/flutter-engine/depot_tools:$PATH

这里有个配置文件须要留神下:
gclient
配置在 /Users/xxx/Desktop/flutter-engine 目录 engine 文件夹创立.gclient 文件

同步依赖

cd /Users/xxx/Desktop/flutter-engine/engine
gclient sync

编译产物

cd /Users/xxx/Desktop/flutter-engine/engine/src
# --simulator 就是模拟器
# --ios-cpu=arm 就是 armv7,不指定默认就是 arm64
flutter/tools/gn --ios —unoptimized

在 src/out/ios_debug_unopt 目录下,会生成一个 Xcode 我的项目,用于 debug 时候应用引擎源代码。

src/out/host_debug_unopt,我在 debug 引擎时候发现须要外面的 dart-sdk,所以这个也须要编译一下。如果没有批改 dart 层代码就不须要执行这一步。

执行 ninja,生成 framework 产物。

ninja -C out/ios_debug_unopt && ninja -C out/host_debug_unopt

编译过程很漫长,能够在 ninja 前面加上参数 -j 2,防止编译占用过多的电脑资源,影响你开发。

编译实现之后,就要创立最初的 Flutter.xcframework。

官网也有提供 python 脚本工具,src/flutter/sky/tools 目录下的:create_ios_framework.py 和 create_macos_gen_snapshots.py

创立 Flutter.xcframework:release 版本加 –dsym 参数会生成 dysm 符号表

cd  ~/Desktop/flutter-engine/engine/src/flutter/sky/tools

# debug 版本 Flutter.xcframework
python create_ios_framework.py --dst  ~/Desktop/flutter-engine/engine/src/out/vd/ios --arm64-out-dir  ~/Desktop/flutter-engine/engine/src/out/ios_debug --armv7-out-dir  ~/Desktop/flutter-engine/engine/src/out/ios_debug_arm --simulator-out-dir  ~/Desktop/flutter-engine/engine/src/out/ios_debug_sim

# profile 版本 Flutter.xcframework
python create_ios_framework.py --dst  ~/Desktop/flutter-engine/engine/src/out/vd/ios-profile --arm64-out-dir  ~/Desktop/flutter-engine/engine/src/out/ios_profile --armv7-out-dir  ~/Desktop/flutter-engine/engine/src/out/ios_profile_arm --simulator-out-dir  ~/Desktop/flutter-engine/engine/src/out/ios_debug_sim

# release 版本 Flutter.xcframework
python create_ios_framework.py --dst  ~/Desktop/flutter-engine/engine/src/out/vd/ios-release --arm64-out-dir  ~/Desktop/flutter-engine/engine/src/out/ios_release --armv7-out-dir  ~/Desktop/flutter-engine/engine/src/out/ios_release_arm --simulator-out-dir  ~/Desktop/flutter-engine/engine/src/out/ios_debug_sim --dsym

# 每个版本都加上了模拟器的局部,便于应用方模拟器调试 

创立 gen_snapshots

cd  ~/Desktop/flutter-engine/engine/src/flutter/sky/tools

# debug 版本 gen_snapshot_arm64 和 gen_snapshot_armv7
python create_macos_gen_snapshots.py --dst  ~/Desktop/flutter-engine/engine/src/out/vd/ios --arm64-out-dir  ~/Desktop/flutter-engine/engine/src/out/ios_debug --armv7-out-dir  ~/Desktop/flutter-engine/engine/src/out/ios_debug_arm

# profile 版本 gen_snapshot_arm64 和 gen_snapshot_armv7
python create_macos_gen_snapshots.py --dst  ~/Desktop/flutter-engine/engine/src/out/vd/ios-profile --arm64-out-dir  ~/Desktop/flutter-engine/engine/src/out/ios_profile --armv7-out-dir  ~/Desktop/flutter-engine/engine/src/out/ios_profile_arm

# release 版本 gen_snapshot_arm64 和 gen_snapshot_armv7
python create_macos_gen_snapshots.py --dst  ~/Desktop/flutter-engine/engine/src/out/vd/ios-release --arm64-out-dir  ~/Desktop/flutter-engine/engine/src/out/ios_release --armv7-out-dir  ~/Desktop/flutter-engine/engine/src/out/ios_release_arm

# 模拟器是 JIT 的,不须要 gen_snapshot

将来瞻望

俗话说,工欲善其事必先利其器。目前两轮 Flutter 降级后安稳运行,对业务迭代疾速倒退保驾护航。将来,两轮在大前端甚至能够持续摸索 flutter 的新特效,如 flutter-web,为大前端在小程序,taro,h5,app 的大交融夯实根底。

(本文作者:叶旸)

正文完
 0