飞镖未来阻塞主线程

时间:2020-10-25 03:17:32

标签: flutter dart future

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

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

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

在另一堂课中:

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

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

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

2 个答案:

答案 0 :(得分:1)

由于Dart使用事件循环,因此所有代码(同步和异步)将仅在相同的时间isolate(在其他语言中,将thread作为类推)运行。这样,当process方法出队并执行时,尽管是异步的,但仍会由于较长的执行时间而阻塞线程并导致丢弃帧。解决该问题的最佳方法是在新线程中生成另一个隔离,然后在那里进行计算。 Flutter为这种确切的用例提供了一种方便的方法,称为compute。它使用一个顶级函数(不是在类中,也不是匿名的)可以将原始类型参数(包括MapList)作为参数,并将在将来的某个时刻返回。有关compute的更多信息,请参见上面链接的文档。

如果您需要将多个参数传递给compute,则一种通用模式(不只是此用例)正在制定一种将类的字段序列化为Map<String, dynamic>的方法,并且从Map<String, dynamic>创建对象的工厂构造函数。通过反射,此过程会更容易,但是Flutter出于性能原因将其禁用。

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

答案 1 :(得分:0)

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

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));
      }
    }
  });
}