Flutter Isolate的使用
在 Flutter(Dart)里,Isolate 是唯一的多“线程”手段,但它不是传统线程,而是一个没有共享内存、靠消息通信的并发单元。
把 Isolate 用对,才能把耗时任务从 UI 线程彻底剥离,保证 60 fps 不丢帧。下面把“原理 → 通信 → 常用 API → 实战 → 坑点”一次讲清。
一、Isolate 到底是什么?
- 单线程 + 事件循环模型
Dart 程序启动后默认只跑一条线程(Root Isolate),所有 Widget 构建、渲染、手势都在这条 UI 线程里顺序执行。 - 没有共享内存
每个 Isolate 拥有独立堆,彼此物理隔离,所以不会出现锁、竞争条件,也不会阻塞 UI。 - 通信方式只有 Port(SendPort / ReceivePort),消息异步、二进制序列化。
二、两种开箱即用的封装
场景 | 手写 Isolate | 官方封装 compute() | Isolate.run (Dart 2.19+) |
---|---|---|---|
自动管理生命周期 | × | √ | √ |
支持泛型返回值 | √ | √ | √ |
可多次通信 | √ | ×(一次性) | ×(一次性) |
代码量 | 多 | 少 | 最少 |
三、手写 Isolate 的 4 步模板(可复用)
// ① 主 Isolate:创建 ReceivePort 并 spawn
Future<SendPort> _initIsolate() async {final receivePort = ReceivePort();await Isolate.spawn<_IsolateInit>(_entryPoint, receivePort.sendPort);return await receivePort.first; // 等待子 Isolate 把 SendPort 回传
}// ② 子 Isolate 入口:先给自己建 ReceivePort,再把 SendPort 回传
void _entryPoint(SendPort mainSendPort) {final receivePort = ReceivePort();mainSendPort.send(receivePort.sendPort); // 回传receivePort.listen((msg) { // 正式处理业务final n = msg[0] as int;final replyPort = msg[1] as SendPort;final sum = List.generate(n, (i) => i).reduce((a, b) => a + b);replyPort.send(sum); // 结果回传});
}// ③ 对外提供工具函数
Future<int> heavySum(int n) async {final sendPort = await _initIsolate(); // 缓存后可复用final answerPort = ReceivePort();sendPort.send([n, answerPort.sendPort]);return await answerPort.first;
}
上面 _initIsolate()
的结果可以全局缓存,避免每次新建 Isolate(一次性 5~10 ms 延迟)。
四、compute() 与 Isolate.run 快速调用
// compute 版本
int fib(int n) => n <= 2 ? 1 : fib(n - 1) + fib(n - 2);
final r = await compute(fib, 40); // 自动建 Isolate 并返回结果// Isolate.run 版本(更简洁)
final r2 = await Isolate.run(() => fib(40));
注意:
- 传给
compute/run
的函数必须是顶层函数或 static 方法(因为需要序列化到另一堆)。 - 内部不要引用 BuildContext 等 UI 对象,否则会抛“非法参数”异常。
五、双向持续通信(Event 场景)
如果子 Isolate 需要持续给主 Isolate 发事件(例如下载进度、传感器流),用 Stream + ReceivePort 即可:
Stream<int> startCounter() {final receivePort = ReceivePort();Isolate.spawn(_counterEntry, receivePort.sendPort);return receivePort.cast<int>(); // 返回广播流
}void _counterEntry(SendPort port) {final timer = Stream.periodic(const Duration(seconds: 1), (i) => i);timer.take(10).listen((i) => port.send(i));
}
主 Isolate 直接 listen
这个 Stream 就能收到秒级心跳。
六、性能与调试要点
- 新建一次 Isolate 约 5~10 ms,不要高频重建;可用 IsolateNameServer 做全局复用。
- 消息过大(>64 k)会自动走外部内存,单次 >10 MB 会明显卡顿,建议分块或传文件路径。
- 在 DevTools 的 Timeline 面板里能看到 “Isolate Spawn” 事件,方便定位创建耗时。
- Android 若出现 “Isolate exited unexpectedly”,检查是否调用了
Platform.isAndroid
等 VM 不支持的 API。
七、一句话总结
- 简单一次性任务 → 用
compute()
/Isolate.run()
; - 长驻、可复用、双向通道 → 手写
Isolate.spawn
+ReceivePort/SendPort
; - 不要把 UI 对象、BuildContext、插件 API 带到 Isolate 里;
- 监控消息体积与创建频率,就能在 Flutter 里零成本享受“真·多线程”算力。