Где изолят может быть определен в Flutter? Как запустить поток во Флаттере?

#multithreading #flutter #dart-isolates

Вопрос:

Я новичок в Flutter, поэтому вопрос может быть довольно очевидным, но я не могу найти ответа в Интернете.

У меня есть приложение Flutter с несколькими экранами, и я бы сказал, что на пятом экране у меня есть кнопка, которая должна вызвать некоторую тяжелую вычислительную работу (преобразование тысяч изображений). На том же экране есть индикатор выполнения, и он должен отображать ход выполнения.

Я озадачен тем, как это технически реализовать. Срабатывание происходит, очевидно, на onPressed кнопке.

  • если я просто вызову Future<void> функцию, то пользовательский интерфейс полностью зависнет на время обработки, что, очевидно, нежелательно
  • если я помещу свою функцию внутрь compute , при первом await же вызове я получу исключение Unhandled Exception: Exception: ServicesBinding.defaultBinaryMessenger was accessed before the binding was initialized. If you're running an application and need to access the binary messenger before runApp() has been called (for example, during plugin initialization), then you need to explicitly call the WidgetsFlutterBinding.ensureInitialized() first. , которое меня озадачивает, потому что я звонил WidgetsFlutterBinding.ensureInitialized() раньше runApp() . В любом случае этот метод не работает.
 compute(computationFunction, 'argument');
// ...
static void computationFunction(String argument) async {
  await firstStepFunction();
// ...
 
  • если я помещаю свою функцию в Isolate.spawn , я получаю исключение Unhandled Exception: Invalid argument(s): Isolate.spawn expects to be passed a static or top-level function , которое также меня озадачивает. Я попытался создать функцию static и переместил ее на верхний уровень этого пятого экранного модуля. Ничего не изменилось. Должен ли я начать Isolate с main функции? Во всех прекрасных примерах это делается именно так. Не могу ли я начать Isolate с середины нажатием кнопки.
 Isolate.spawn(computationFunction, receivePort.sendPort);
// ...
void computationFunction(SendPort sendPort) async {
  await firstStepFunction();
// ...
 

В Java я думаю, что простое new Thread(...).start() сделает эту работу.

Но как это сделать в Флаттере?


Обновить:

В своих экспериментах я заметил, что ни горячий перезапуск Flutter, ни горячая перезагрузка не работают корректно с изолятами. Вам действительно нужно снова запустить все приложение.

Мне удалось начать Isolate.spawn все правильно, если async / await ключевые слова удалены. Конечно, вызываемая функция должна иметь свою синхронную версию. Так что это не работает повсеместно.

 Isolate.spawn(computationFunction, receivePort.sendPort);
// ...
static void computationFunction(SendPort sendPort) { // async removed
  firstStepFunctionSync(); // the function is replaced with its synchronous version 
// ...
 

Я нашел пакет flutter_isolate , который позволяет запускать асинхронные функции:

 FlutterIsolate.spawn(computationFunction, argument);
// ...
void computationFunction(SendPort sendPort) async {
  await firstStepFunction();
// ...
 

Я постараюсь использовать flutter_isolate пакет в своем прототипе.

Ответ №1:

Вам следует прочитать https://dev.to/alphamikle/why-should-you-use-isolates-in-flutter-1k5o, и посмотрите на упаковку:изоляты.

В статье сравнивается использование основного потока, вычислений, собственно изоляции и пакета изолятов с преимуществами и недостатками каждого из них. Лучшая статья, которую я видел за долгое время.

Также имейте в виду, что потоки Java совместно используют данные, что приводит к возможным тупикам. Изоляты Dart не имеют общего доступа, используя «порты» для осторожного перемещения данных между изолятами, и нет необходимости в блокировке!

Ответ №2:

Ознакомьтесь с этим плагином, который обеспечивает простой способ работы с изолятами с рабочей абстракцией или с использованием параллельных методов и имеет хорошо объясненную документацию.

https://pub.dev/packages/easy_isolate

Использование простое, как

 void main() async {
  final worker = Worker();
  await worker.init(mainHandler, isolateHandler);
  
  worker.sendMessage(null);
}

void mainHandler(dynamic data, SendPort isolateSendPort) {
  isolateSendPort.send(null);
}

// Top-level function (or static)
void isolateHandler(dynamic data, SendPort mainSendPort, SendErrorFunction onSendError) {
  mainSendPort.send(null);
}
 

Или с использованием параллельных методов

 Future main() async {
  await Parallel.foreach(['test'], writeFile);
}

// Top-level function (or static)
void writeFile(String name) {
  File(Directory.systemTemp.path   '/$name').createSync();
}