应用程序的流畅与否,主要取决于能否及时对用户操作作出响应。目前(2022-06-29)主流移动设备大都设计为以 60 fps 的速度刷新屏幕,在 60fps 的屏幕上,事件需在大约 16ms 内被执行。如果超过这个时间,画面将会出现卡顿。

核心奥义

  • 非密集计算型耗时任务(CPU 负载较低),例如等待网络通信中的响应,应在 Main Isolate 上使用异步处理完成。
  • 密集计算型耗时任务(CPU 负载较高),为不影响帧率,应启动新的 Isolate 执行并行处理。
  • 使用其他 Isolate 运行任务的切换成本大约为 10~20ms 或更多(成本高于 Javascript 的轻量级线程)。

TODO:滥用 Isolate 会使代码复杂化,若只需执行某个耗时任务并获取其返回值,应使用 compute 函数。

TODO:任务耗时超过 16ms(在 60fps 终端的情况下),可判定为耗时任务。

TODO:为什么要使用 Isolate?为什么耗时任务会影响帧率?这两个问题在本文番外章节中有讲道。

TODO:常见的密集计算型耗时任务包括矩阵乘法、密码学相关(如签名、散列、密钥生成)、图像/音频/视频处理、序列化/反序列化、离线机器学习模型计算、压缩(如 zlib)、正则表达式等。

番外

Dart 是一种单线程语言,一次执行一个操作,一个接一个地执行。这意味着只要一个操作正在执行,就不能被其他 Dart 代码中断。 在幕后,每个 Isolate 都依靠自己的事件循环 (Event Loop) 一个接一个的从队列中获取并执行任务。

事件循环

一个 Isolate 中包含两个队列, 微任务队列 (MicroTask)事件队列 (Event),微任务队列中的任务优先执行,仅当微任务队列为空时,事件循环才会执行事件队列中的任务。

事件循环

默认情况下所有任务都在 Main Isolate 上完成,包括 Flutter 的 UI 更新。如果遇到耗时任务,由于是单线程,包括 Flutter UI 更新在内的所有任务的都会被阻塞,这将导致帧率急剧下降。通常,只需将耗时任务放到新 Isolate 中即可避免这种问题的发生。

*TODO:多个 Isolate 之间不会共享内存,只能通过来回传递消息 (ReceivePort和SendPort) 实现相互通信。

TODO:Dart 单线程事件循环的执行模型与 Node.js 类似。

TODO:目前而言(2022-06-29),Flutter 平台通道通信仅受主隔离支持。这个主要隔离对应于应用程序启动时创建的隔离。

参考资料