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

上一篇,咱们分享了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的大交融夯实根底。

(本文作者:叶旸)

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理