开启成长之旅!这是我参加「日新计划 12 月更文应战」的第1天,点击检查活动详情

公司的iOS项目是原生嵌Flutter类型,运用Flutter多引擎(Engine group)计划。与曾经项目运用的Flutter boost计划不同, 发现多引擎计划的attach功用十分不稳定,经常呈现无法attach以及Hot reload, Hot restart卡死的状况,十分影响开发效率。本文会简略剖析问题原因及提供一些解决计划。

Flutter attach的原理

Flutter经过将更新的源代码文件注入到正在运转的Dart 虚拟机(VM)来实现热重载。在虚拟机运用新的字段和函数更新类之后, Flutter 框架会自动从头构建 widget 树,以便快速检查更改的作用。

attach的进程就是一个衔接VM的进程,使用以Debug模式运转后,会启动一个VM服务,并且运用mDNS协议( mDNS/DNS-SD 是用于本地局域网服务发现的协议)广播。履行attach操作时,会经过mDNS协议去查找当时使用所匹配的VM服务,再经过WS协议进行衔接,这个时分就可能呈现如下几种状况,导致衔接失利:

  1. 找到的VM服务太多,需求挑选衔接哪一个。
  2. 调度问题,没有去连正确的 VM 服务。
  3. mDNS缓存没有刷新
  4. mDNS查找问题

1. 找到的VM服务太多,需求挑选衔接哪一个

当呈现这类问题时,一般会呈现这类log:

There are multiple observatory ports available.
Rerun this command with one of the following passed in as the appId:
  flutter attach --app-id com.xxx.test
  flutter attach --app-id com.xxx.test (2)

多引擎计划相对于单引擎计划及纯Flutter项目,很简单呈现这类问题,这种状况如提示所示,在项目的运转装备中指定app-id,再运转测验,具体操作如图所示:

Flutter多引擎无法Attach问题分析及热重载卡死问题处理

多引擎计划中,这个操作大概率不能解决attach问题,咱们也不能清晰知道自己运转的使用对应的是哪个app-id, 后面那个com.xxx.test (2)相对于第一个附加了(2),实际上都是对应同一个bundle identifier的使用,仅仅由于先来后到的原因,后到的被附加了(2),如果有更多,可能还会看到附加了(3)的等。所有咱们需求如图所示在后面加上-v参数来获取详细日志,以便于剖析问题发生的原因。

2. 调度问题,没有去连正确的VM服务

当咱们指定 app-id 后, 还是会经常呈现attach失利,这是什么原因呢?咱们能够经过指令: ns-sd -Z _dartobservatory._tcp 来获取当时VM服务列表,输出如下:

➜ ✗ dns-sd -Z _dartobservatory._tcp
Browsing for _dartobservatory._tcp
DATE: ---Sat 28 May 2022---
20:19:44.892  ...STARTING...
; To direct clients to browse a different domain, substitute that domain in place of '@'
lb._dns-sd._udp                                 PTR     @
; In the list of services below, the SRV records will typically reference dot-local Multicast DNS names.
; When transferring this zone file data to your unicast DNS server, you'll need to replace those dot-local
; names with the correct fully-qualified (unicast) domain name of the target host offering the service.
_dartobservatory._tcp                           PTR     com\.xxx\.test._dartobservatory._tcp
com\.xxx\.test._dartobservatory._tcp    SRV     0 0 57624 yanfang.local. ; Replace with unicast FQDN of target host
com\.xxx\.test._dartobservatory._tcp    TXT     "authCode=8DjE8_OckNk="
_dartobservatory._tcp                           PTR     com\.xxx\.test\032(2)._dartobservatory._tcp
com\.xxx\.test\032(2)._dartobservatory._tcp SRV     0 0 64646 yanfang.local. ; Replace with unicast FQDN of target host
com\.xxx\.test\032(2)._dartobservatory._tcp TXT     "authCode=J0BgTyk73m8="
_dartobservatory._tcp                           PTR     com\.xxx\.test\032(3)._dartobservatory._tcp
com\.xxx\.test\032(3)._dartobservatory._tcp SRV     0 0 61345 star.local. ; Replace with unicast FQDN of target host
com\.xxx\.test\032(3)._dartobservatory._tcp TXT     "authCode=E0fcBt3fdk2="

在输出中能够看到VM服务的app-id, 端口设备名。 如第一条,app-idcom.xxx.test, 端口是:57624, 设备名是:yanfang.local

输出中有3个服务,分别是:com\.xxx\.test._dartobservatory._tcpcom\.xxx\.test\032(2)._dartobservatory._tcp,其实这两个都是bundle identifiercom.xxx.test 使用所对应的VM服务,由于先来后到的原因,后到的就被加了一个(2)以做区别,在上述log内容中,咱们甚至可能会看到搭档的设备名称,这是由于mDNS协议是查找整个局域网内的VM服务,当你和搭档在调试同一个项目时,mDNS会把你和搭档相同app-idVM服务一起找出来,那么哪个是你当时调试使用对应的VM服务呢? 尽管你指定了app-id,可是你先运转app-idcom.xxx.test,后运转你就变成了com.xxx.test (2)。这种状况下有两种计划来解决这个问题:

  1. attach时拔掉网线,断开网络衔接,让mDNS找不到局域网内其它设备的VM服务
  2. 每次运转前履行 ns-sd -Z _dartobservatory._tcp 检查当时的VM服务,依据上面的设备名和端口挑选正确的app-id进行装备。

3. 存在mDNS缓存,没有刷新

有时分,咱们调用ns-sd -Z _dartobservatory._tcp指令后会发现,同一台设备中,存在多个同名的VM服务,可是明明一台设备不可能一起运转两个相同bundle identifier的使用。我置疑可能是Flutter多引擎下存在bug,导致缓存没有刷新引起的。具体没有深究,欢迎评论区点拨纠正。

在这类状况下就只能依赖于-v参数,检查attach时的log, 看是去测验衔接的哪一个VM服务。 log样例日下:

[+1902 ms] Checking for available port on com.xxx.test._dartobservatory._tcp.local
[   +3 ms] Checking for authentication code for com.xxx.test._dartobservatory._tcp.local
[  +67 ms] Connecting to service protocol: http://127.0.0.1:56954/SNvhcI0TrgU=/
[  +17 ms] Exception attempting to connect to the VM Service: SocketException: OS Error: Connection refused, errno = 61, address = 127.0.0.1, port = 60679
[        ] This was attempt #1. Will retry in 0:00:00.100000.

在上述log中能够看到测验衔接的VM服务端口和app-id,一般配合ns-sd -Z _dartobservatory._tcp指令来判断正确的app-id,然后指定app-id再运转即可。

4. mDNS查找问题

Flutter库房的Issue46705有对这类问题进行说明,处理方式有:

  • 封闭个人热点
  • Mac -> 设置 -> 网络 -> iPhone USB -> 不勾选“除非需求,否则请停用”
  • 重试

在实践中,经过上述4个流程,能够确保必定attach成功。

Hot reload问题

Flutter多引擎计划相对其它计划来讲,含辛茹苦attach成功后,还会有更高概率呈现Hot reload卡死的状况,实际运用起来十分不稳定,咱们能够经过如下事项缓解:

  • 封闭自动Hot reload和保存代码触发Hot reload功用。
  • 不在Hot reloadHot restart时切换页面。
  • 避免一起履行多次Hot reloadHot reload
  • Hot reload无效时,测验履行Hot reload恢复状态。
  • 代码中有死循环,也会导致Hot reloadHot reload 卡死

做了如上操作后,Hot reload卡死的状况会缓解,可是依然会有必定概率呈现,如果您有更好的计划,欢迎评论区告知。

参考资料

  • 官方文档-热重载
  • 解读Flutter中热重载原理
  • iOS mDNS meta issue

by 星的天空