公司的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卡死的情况会缓解,但是依然会有一定概率出现,如果您有更好的方案,欢迎评论区告知。