公司的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
协议进行连接,这个时候就可能出现如下几种情况,导致连接失败:
- 找到的
VM
服务太多,需要选择连接哪一个。 - 调度问题,没有去连正确的 VM 服务。
mDNS
缓存没有刷新mDNS
查找问题
1. 找到的VM
服务太多,需要选择连接哪一个
当出现这类问题时,一般会出现这类log:
1 | There are multiple observatory ports available. |
多引擎
方案相对于单引擎
方案及纯Flutter
项目,很容易出现这类问题,这种情况如提示所示,在项目的运行配置中指定app-id
,再运行尝试,具体操作如图所示:
在多引擎
方案中,这个操作大概率不能解决attach
问题,我们也不能明确知道自己运行的应用对应的是哪个app-id
, 后面那个com.xxx.test (2)
相对于第一个附加了(2)
,实际上都是对应同一个bundle identifier
的应用,只是由于先来后到的原因,后到的被附加了(2)
,如果有更多,可能还会看到附加了(3)
的等。所有我们需要如图所示在后面加上-v
参数来获取详细日志,以便于分析问题产生的原因。
2. 调度问题,没有去连正确的VM
服务
当我们指定 app-id
后, 还是会经常出现attach
失败,这是什么原因呢?我们可以通过命令: ns-sd -Z _dartobservatory._tcp
来获取当前VM
服务列表,输出如下:
1 | ➜ ✗ dns-sd -Z _dartobservatory._tcp |
在输出中可以看到VM
服务的app-id
, 端口
, 设备名
。 如第一条,app-id
是 com.xxx.test
, 端口
是:57624
, 设备名
是:yanfang.local
。
输出中有3个服务,分别是:com\.xxx\.test._dartobservatory._tcp
和 com\.xxx\.test\032(2)._dartobservatory._tcp
,其实这两个都是bundle identifier
为 com.xxx.test
应用所对应的VM
服务,由于先来后到的原因,后到的就被加了一个(2)
以做区分,在上述log内容中,我们甚至可能会看到同事的设备名称,这是由于mDNS
协议是查找整个局域网内的VM
服务,当你和同事在调试同一个项目时,mDNS
会把你和同事同样app-id
的VM
服务一起找出来,那么哪个是你当前调试应用对应的VM
服务呢? 虽然你指定了app-id
,但是你先运行app-id
是com.xxx.test
,后运行你就变成了com.xxx.test (2)
。这种情况下有两种方案来解决这个问题:
- 在
attach
时拔掉网线,断开网络连接,让mDNS
找不到局域网内其它设备的VM
服务 - 每次运行前执行
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样例日下:
1 | [+1902 ms] Checking for available port on com.xxx.test._dartobservatory._tcp.local |
在上述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 reload
和Hot restart
时切换页面。 - 避免同时执行多次
Hot reload
,Hot reload
。 Hot reload
无效时,尝试执行Hot reload
恢复状态。- 代码中有死循环,也会导致
Hot reload
,Hot reload
卡死
做了如上操作后,Hot reload
卡死的情况会缓解,但是依然会有一定概率出现,如果您有更好的方案,欢迎评论区告知。