在 Dart 中,没有『多线程』的概念,在绝大多数的开发中,只会用到 UI 线程,也就是 Android 中所谓的『主线程』。

但是 Dart 给我们提供了异步编程的方式,来让我们『同步』地编写『异步』的代码。也即await/async关键字。在运行一些代码时,UI 线程还会继续渲染 Widget Tree,不会卡死。

那么单线程的 Dart 是如何实现这种看起来像『多线程』的机制的呢?这里我们要先介绍一个概念:Isolate。

Isolate

每一个 Isolate 都有自己的内存空间,以及一个单独的线程来运行 event loop。

绝大多数情况下,一个 Flutter 应用只需要一个 Isolate 就可以了,但如果你有庞大的计算需求,大到有可能会阻碍到 UI 线程掉帧,那你就得创建一个新的 Isolate 来进行这些计算。

创建新的 Isolate 包括但不限于以下方式:

  • Isolate.spawn()
  • compute()

创建完成的新 Isolate 有它自己独立的内存空间,即便是它的创建者也无法访问这部分空间。这也是『隔离』的意义所在。

实际上,要让多个 Isolate 合作,也是可以的,这需要用到它的 SendPort 和 ReceivePort。举例代码如下:

ReceivePort receivePort = ReceivePort();
// 创建新的 Isolate
await Isolate.spawn(complexCompute, receivePort.sendPort);
SendPort sendPort = await receivePort.first;
// 流的第一个元素被收到后监听会关闭,所以需要新打开一个ReceivePort以接收传入的消息
ReceivePort response = ReceivePort();

int toBeComputed = 1;

// 以数组的形式将参数和发送方传入,方便进行数据回传
sendPort.send([toBeComputed, response.sendPort]);
int result = await response.first;

setState(() {
    ...
});

complexCompute()方法必须是个顶级方法或者静态方法:

void complexCompute(SendPort sendPort) async {
  ReceivePort port = ReceivePort();
  sendPort.send(port.sendPort);

  await for (var param in port) {
    int toBeComputed = param[0];
    SendPort replyTo = param[1];

    toBeComputed++;

    replyTo.send(toBeComputed);
  }
}

当然,上面的计算也可以直接用 compute() 来解决:

int result = await compute(anotherComplexCompute, 1);

setState(( {
    ...
}));

同样地,anotherComplexCompute() 也必须是顶级方法:

int anotherComplexCompute(int value) {
    return value + 1;
}

Event Loop

上面讲完了 Isolate 机制,下面就该讲讲异步代码是如何被运行起来的。

Event Loop(事件循环)与 Android 中的 Looper 类似。