Dart 未来阻塞主线程

Dart future blocking main thread

我正在开发一款捕捉和处理图像的应用程序。代码的简化版本是:

build() {
   return FloatingActionButton(
        onPressed: processImage,
        child: Icon(
          Icons.camera_alt,
          color: color,
        ),
      ); 
}

processImage(Camera image) async {
   await image.process();   
}

在另一个 class:

Future<image> process() {
  return Future(() {
    for (int x = 0; x < width; x++) {
      for (int y = 0; y < height; y++) {
        //process image
      }
    }
  });
}

但是当 process() 为 运行 时,UI 冻结。

为什么会这样?那个函数不是在后台传递给 Future 构造函数 运行 吗?

您可以将间隙插入到事件循环中。
简单方法:

Future<image> process2() {
  return Future(() async {
    for (var x = 0; x < width; x++) {
      for (var y = 0; y < height; y++) {        
        // process       
      }

      if (x % 100 == 0) {
        await Future.delayed(Duration(seconds: 100));
      }
    }
  });
}

由于 Dart 使用事件循环,所有代码(同步和异步)都将简单地 运行 在同一个 isolate 上(想想其他语言中的 thread 作为类比),只是在不同的时间点。因此,当您的 process 方法出列并执行时,它会阻塞线程并导致帧由于较长的执行时间而被丢弃,尽管它是异步的。该问题的最佳解决方案是在新线程中生成另一个 isolate,并在那里进行计算。 Flutter 为这个确切的用例提供了一个方便的方法,称为 compute。它需要一个 top-level 函数(不在 class 中,也不是匿名函数),它可以有一个基本类型参​​数(包括 MapList)作为参数,并且将 return 在未来的某个时候。有关 compute 的更多信息,请参阅上面链接的文档。

如果您有多个参数需要传递给 compute,一个常见的模式(在这个用例之外)是制作一个将 class' 字段序列化为 Map<String, dynamic>,以及一个从 Map<String, dynamic> 创建对象的工厂构造函数。这个过程使用反射会更容易,但由于性能原因,Flutter 禁用了它。

有关 Flutter 文档中 compute 的完整示例,请参阅此处:https://flutter.dev/docs/cookbook/networking/background-parsing