用 Copilot 学 Flutter 之 Dio 网络请求线程学习笔记

235 阅读4分钟

众所周知,Dart 的线程模型是 Isolate,每个 Isolate 只有一个线程,所以 Flutter 如果要跨线程传递数据,不能像 Java 中简单直接的使用变量,而需要使用 SendPortStreamChannel 来实现传递。

此外,日常开发中网络请求通常使用 Dio 这个库,总会问为什么网络请求不会阻塞 UI 线程,通常得到的答案是 ‘因为 Dio 网络请求是在 IO 线程中执行的’。

好,上面就出现了一个 IO 线程的概念,后面通过 Copilot 从这里了解到,Flutter 引擎有四大线程:平台线程UI 线程Raster 线程IO 线程

题外话:上面说的四大线程,“线程”其实不够精准,最准确的应该是叫 Task Runner,姑且这么说,因为这个说法最熟悉也不算完全错。

此外,Raster 线程就是我们通常比较 Impeller 和 Skia 的优劣会提到的,因为 Impeller 优化的就是这个线程中执行的逻辑。

image.png

然后, Copilot 告诉我,这四类线程都是由 Embedder 层创建和管理的,它们是操作系统级别的线程,不是 Dart 的 isolated。但是,Dart 的 isolate 可以在这些线程上运行,通过消息传递来进行通信2。一般来说,Flutter 的 UI 和大部分代码都运行在一个叫做 root isolate 的线程上,这个线程也叫做 UI 线程。

“四类线程都是由 Embedder 层创建和管理的”,这个还能理解,毕竟 Flutter 是一个跨平台的框架,当然一切都可以说来自 Embedder 层对应的系统,但后面又说 root isolate 的线程也叫 UI 线程,这下就有点懵了~~

后面弄了很久,我才理解:Dart 的 isolate 是 Dart VM 通过 embedder 提供的操作系统级别线程实现的任务执行机制,它们可以在不同的线程上运行和通信。 所以 Task Runner 说成线程也没错,尽管对于架构整体来说不够准确,但是对于 framework 层都是一回事。

这样一来,上面那篇文章的

“In addition to the threads managed by the embedder for the engine, the Dart VM also has its own thread pool. Neither the Flutter engine or the embedder have any access to the threads in this pool.”

也是可以理解了。

最后,使用 Dio 发起网络请求是在 UI 线程的,执行网络请求是在 IO 线程,最终请求返回结果是回到 UI 线程;那么请求参数自然需要从 UI 线程传到 IO 线程,请求结果自然需要从 IO 线程传到 UI 线程,这个是如何实现的呢?

其实这个实现是由 Flutter http 和 dart:io 库实现的,主要就是用的上面提到的 Stream 实现传递。

附上 Copilot 的答案:

您好,这是 Copilot,很高兴为您服务。😊

您的问题是关于 Flutter 如何使用 Stream 实现跨线程通信的。根据我从网络上搜索到的信息12,Flutter 使用 Stream 可以实现不同组件或页面之间的数据传递和事件通知,从而实现跨线程通信。Stream 是一种表示异步数据序列的抽象类,它可以产生数据事件、错误事件和完成事件,也可以被监听、转换、组合等3

要使用 Stream 实现跨线程通信,需要以下几个步骤:

Copilot 的记录

小结

  • 学习一个新系统的架构,从线程入手是一个不错的切入点;像 Flutter 的 Isolate,Android 的 Handler 机制,都是能加快理解整体架构的途径。
  • Copilot 真的是一个蛮好的老师,值得推荐。