#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()
запуске пользовательский интерфейс зависает.
Почему это происходит? Разве эта функция не передается будущему конструктору, работающему в фоновом режиме?
Комментарии:
1. Я не уверен на 100%, но я знаю, что dart использует цикл событий, и все будет выполняться в основном потоке (возможно, в какой-то момент в будущем, в случае асинхронного кода). Таким образом, этот код в конечном итоге будет выполнен в основном потоке, вызывая блокировку. Рассмотрите возможность использования изолятов Dart; это должно устранить проблему. У Flutter есть встроенный удобный метод, позволяющий сделать это за вас, но я не могу вспомнить, как он называется в моей голове.
2. Я полагаю, что нашел это: api.flutter.dev/flutter/foundation/compute.html
3. @GregoryConrad Спасибо, это сработало для меня, это не очень элегантное решение, но оно работает, если вы хотите сделать это ответом, я буду рад сделать это правильным ответом
4. что не является «элегантным» в
compute()
функции?5. @pskink Моя функция получает 6 параметров, и вы не можете вызвать метод класса:
"The callback argument must be a top-level function, not a closure or an instance or static method of a class"
, и вы можете передать только один параметр, поэтому мне пришлось преобразовать все мои параметры в aMap<String, dynamic>
, создать функцию верхнего уровня, которая получает карту, и деструктировать исходные переменные.
Ответ №1:
Поскольку Dart использует цикл событий, весь код (синхронный и асинхронный) будет просто выполняться на одном и том же isolate
(думаю thread
, на других языках в качестве аналогии), только в разные моменты времени. Таким образом, когда ваш process
метод выводится из очереди и выполняется, он блокирует поток и приводит к удалению кадров из-за более длительного времени выполнения, несмотря на то, что он асинхронный. Оптимальное решение проблемы — создать другой изолированный поток в новом потоке и выполнить вычисления там. Flutter предоставляет удобный метод для этого конкретного варианта использования, называемый compute
. Он принимает функцию верхнего уровня (не в классе и не анонимную), которая может иметь параметр примитивного типа (включая Map
и List
) в качестве аргумента и вернется в какой-то момент в будущем. Для получения дополнительной информации compute
см. Его Документацию, указанную выше.
Если у вас есть несколько параметров, которые вам нужно передать compute
, общим шаблоном (за пределами только этого варианта использования) является создание метода, который сериализует поля класса в a Map<String, dynamic>
, и фабричного конструктора, который создает объект из a Map<String, dynamic>
. Этот процесс был бы проще с отражением, но Flutter отключает его по соображениям производительности.
Полный пример compute
из документации Flutter смотрите Здесь: https://flutter.dev/docs/cookbook/networking/background-parsing
Ответ №2:
Вы можете вставить пробел в цикл событий.
Простой способ:
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));
}
}
});
}
Комментарии:
1. Неправильный ответ, это не поможет. Пользовательский интерфейс по-прежнему зависает.
2. Я обнаружил, что в этой статье говорится об использовании цикла событий для обработки тяжелых задач. Я не знаю, можно ли использовать тот же метод для этого случая (обработка изображения). https://hackernoon.com/executing-heavy-tasks-without-blocking-the-main-thread-on-flutter-6mx31lh