Flutter iOS真机首次安装正常,后续启动网络异常重启4次恢复
Flutter iOS真机网络请求异常:资源泄漏导致的文件句柄耗尽问题
问题现象
- 数据密集型Flutter App(包含API接口请求、地图瓦片加载)在Android、iOS模拟器、Windows平台运行正常,但**iOS真机(Debug/Release模式)**出现以下异常:
- 首次安装启动时无问题,数据加载、地图瓦片渲染流畅;
- 后续启动频繁出现数据缺失、地图瓦片加载失败;
- 调试器捕获大量两类错误:
Error: SocketException: Failed host lookup: 'site.com' (OS Error: nodename nor servname provided, or not known, errno = 8) Error: SocketException: Connection failed (OS Error: Too many open files, errno = 24), address = site.com, port = 443 - 同一域名下的10次左右API/JSON请求无失败,仅数百次地图瓦片请求报错;App顶层网络检测显示目标站点可正常访问。
已执行的排查操作
- 测试了直接安装、TestFlight安装两种分发方式,问题均复现;
- 覆盖iPhone 11、iPhone 15两款设备;
- 所有系统软件、Flutter依赖包已更新至最新版本;
- 切换使用过
http、dio两种网络请求包; - 尝试在
AppLifecycleState.detached状态下调用exit(0)强制退出; - 排查IPv4/IPv6兼容性:切换至LTE网络曾短暂恢复正常,但5G网络仍存在问题;仅连续重启App 4次可重置状态,恢复正常运行。
核心原因分析
1. 首次正常、后续异常的本质
这是iOS平台下Dart网络层的资源泄漏导致的:
- 首次启动时,App的文件句柄、网络套接字资源处于初始状态,未达到系统限制(iOS单进程默认文件句柄上限约1024);
- 后续启动时,前一次App退出后,大量地图瓦片的并发请求未被正确释放,残留的套接句柄累积占用资源,最终触发
Too many open files错误。当文件句柄耗尽时,DNS解析请求也无法获取资源,进而引发nodename nor servname provided的域名解析失败。
2. 连续重启4次恢复的逻辑
iOS对App的资源回收采用渐进式清理策略:
- 前几次重启时,系统可能仍保留了App的部分残留资源;
- 连续重启4次后,系统判定该App存在异常资源泄漏,会强制清理所有与该App关联的套接字、文件句柄、缓存资源,让App重新获取完整的资源配额,因此恢复正常。
3. 与Dart网络包的关联
目前Dart的http包存在已知的类似资源泄漏问题,在iOS真机环境下,高频并发的小请求(如地图瓦片)会导致套接字无法及时回收,累积后触发资源耗尽。
解决方案
限制并发请求数量
地图瓦片属于高频并发请求,需限制同时发起的请求数(建议设置为10-20),避免瞬间耗尽文件句柄:- 使用
dio可通过自定义拦截器或设置Transformer控制并发; - 使用
http可结合async库的Semaphore实现并发限流。
- 使用
主动释放网络资源
- 对于
http包,确保每个请求的Response都被正确关闭,可使用await using语法自动管理资源:await using((client) async { final response = await client.get(Uri.parse('https://site.com/tile')); // 处理响应 }, HttpClient.new); - 对于地图组件(如flutter_map、google_maps_flutter),在App退居后台时,主动调用组件的资源清理方法,取消未完成的瓦片请求。
- 对于
优化App退出逻辑
不要仅依赖exit(0),在AppLifecycleState.paused或detached状态下,主动取消所有未完成的网络请求:- 使用
dio时,调用CancelToken.cancel()取消请求; - 关闭所有
HttpClient或Dio实例,确保资源被释放。
- 使用
完善iOS网络配置
在Info.plist中添加NSAppTransportSecurity配置,确保IPv6兼容性,避免DNS解析异常:<key>NSAppTransportSecurity</key> <dict> <key>NSExceptionDomains</key> <dict> <key>site.com</key> <dict> <key>NSIncludesSubdomains</key> <true/> <key>NSAllowsArbitraryLoads</key> <true/> </dict> </dict> </dict>切换至原生网络请求
若Dart层的资源泄漏问题无法解决,可通过MethodChannel调用iOS原生的URLSession实现网络请求,原生层的资源管理机制更成熟,能有效避免此类泄漏。
内容的提问来源于stack exchange,提问作者user1770223
相关产品推荐
相关产品推荐

