Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Изоляты (Isolates) в Dart/Flutter
Изолят — это независимый поток выполнения в Dart, который имеет собственную кучу памяти и не разделяет переменные с другими изолятами. Это решение для параллелизма в Dart, позволяющее выполнять тяжелые операции без блокировки главного потока.
Зачем нужны изоляты
// ПРОБЛЕМА: блокирует главный поток UI
void heavyComputation() {
// Это заморозит UI на несколько секунд
int sum = 0;
for (int i = 0; i < 1000000000; i++) {
sum += i;
}
print("Sum: $sum");
}
// РЕШЕНИЕ: использовать изолят
Future<int> heavyComputationInIsolate() async {
return await compute(heavyComputation, null);
}
void heavyComputation(dynamic _) {
int sum = 0;
for (int i = 0; i < 1000000000; i++) {
sum += i;
}
print("Sum: $sum");
}
Простой способ: compute()
compute() — это встроенная функция Flutter для выполнения функции в новом изоляте.
import 'package:flutter/foundation.dart';
// Функция, которая будет запущена в изоляте
int calculateFibonacci(int n) {
if (n <= 1) return n;
return calculateFibonacci(n - 1) + calculateFibonacci(n - 2);
}
// Функция для обработки JSON (тяжелая операция)
List<User> parseJsonList(String jsonString) {
final List<dynamic> json = jsonDecode(jsonString);
return json.map((item) => User.fromJson(item)).toList();
}
class MyWidget extends StatefulWidget {
@override
State<MyWidget> createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
late Future<int> _fib;
late Future<List<User>> _users;
@override
void initState() {
super.initState();
// Запуск вычисления в изоляте
_fib = compute(calculateFibonacci, 40);
// Парсинг больших данных в изоляте
_users = compute(parseJsonList, largeJsonString);
}
@override
Widget build(BuildContext context) {
return FutureBuilder<int>(
future: _fib,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return CircularProgressIndicator();
} else if (snapshot.hasError) {
return Text("Error: ${snapshot.error}");
} else {
return Text("Fibonacci(40) = ${snapshot.data}");
}
},
);
}
}
Полный контроль: Isolate.spawn()
import 'dart:isolate';
// Функция, которая будет запущена в новом изоляте
void isolateEntryPoint(SendPort sendPort) {
// Получаем ReceivePort для общения
final receivePort = ReceivePort();
sendPort.send(receivePort.sendPort);
// Слушаем сообщения
receivePort.listen((message) {
if (message is String && message == "fibonacci") {
int result = calculateFibonacci(40);
sendPort.send(result);
} else if (message == "stop") {
receivePort.close();
}
});
}
class IsolateManager {
late Isolate _isolate;
late ReceivePort _receivePort;
late SendPort _sendPort;
Future<void> initialize() async {
// Создаём ReceivePort в главном изоляте
_receivePort = ReceivePort();
// Создаём новый изолят
_isolate = await Isolate.spawn(
isolateEntryPoint,
_receivePort.sendPort,
);
// Получаем SendPort из изолята
_sendPort = await _receivePort.first;
}
Future<int> calculateFibonacci() async {
// Создаём новый ReceivePort для получения результата
final resultPort = ReceivePort();
// Отправляем команду
_sendPort.send("fibonacci");
// Ждём результат
final result = await resultPort.first;
resultPort.close();
return result;
}
void dispose() {
_sendPort.send("stop");
_isolate.kill();
_receivePort.close();
}
}
Практический пример: обработка больших файлов
import 'dart:isolate';
// Функция в изоляте
Future<List<String>> processLargeFile(String filePath) async {
final file = File(filePath);
final lines = await file.readAsLines();
// Какая-то тяжелая обработка
return lines
.where((line) => line.isNotEmpty)
.map((line) => line.toUpperCase())
.toList();
}
// Использование с compute
class FileProcessor {
Future<List<String>> processFile(String path) async {
return await compute(processLargeFile, path);
}
}
Обмен данными между изолятами
import 'dart:isolate';
void workerIsolate(SendPort mainSendPort) {
final receivePort = ReceivePort();
mainSendPort.send(receivePort.sendPort);
receivePort.listen((dynamic message) {
if (message is Map) {
final numbers = message["numbers"] as List<int>;
final sum = numbers.reduce((a, b) => a + b);
mainSendPort.send({"result": sum});
}
});
}
class IsolateWorker {
late SendPort _workerSendPort;
late ReceivePort _mainReceivePort;
late Isolate _isolate;
Future<void> init() async {
_mainReceivePort = ReceivePort();
_isolate = await Isolate.spawn(workerIsolate, _mainReceivePort.sendPort);
_workerSendPort = await _mainReceivePort.first as SendPort;
}
Future<int> sumNumbers(List<int> numbers) async {
final responsePort = ReceivePort();
_workerSendPort.send({
"numbers": numbers,
"responsePort": responsePort.sendPort,
});
final response = await responsePort.first as Map;
return response["result"] as int;
}
void dispose() {
_isolate.kill();
_mainReceivePort.close();
}
}
void main() async {
final worker = IsolateWorker();
await worker.init();
final result = await worker.sumNumbers([1, 2, 3, 4, 5]);
print("Sum: $result"); // Sum: 15
worker.dispose();
}
Изоляты в фоновых задачах
class BackgroundService {
static Future<void> backgroundTask(dynamic message) async {
// Тяжелая фоновая работа
print("Running background task...");
await Future.delayed(Duration(seconds: 5));
print("Background task completed");
}
}
// Использование в Flutter
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
void _startBackgroundWork() async {
// Запускаем тяжелую работу в фоновом изоляте
await compute(BackgroundService.backgroundTask, null);
print("Background work done");
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ElevatedButton(
onPressed: _startBackgroundWork,
child: Text("Start Background Work"),
),
),
);
}
}
Лучшие практики
✅ Используй compute() для простых операций:
final result = await compute(myFunction, input);
✅ Используй Isolate.spawn() для сложных операций с двусторонней коммуникацией:
final isolate = await Isolate.spawn(entryPoint, sendPort);
✅ Закрывай ReceivePort когда больше не нужен:
receivePort.close();
✅ Убивай изолят когда закончил:
isolate.kill();
❌ Избегай передачи больших объектов между изолятами (копируются, медленно)
❌ Не забывай про обработку ошибок в изолятах
Различие между compute() и Isolate.spawn()
| Аспект | compute() | Isolate.spawn() |
|---|---|---|
| Сложность | Простой API | Больше контроля |
| Двусторонняя связь | Нет | Да |
| Время жизни | Автоматическое управление | Ручное управление |
| Использование | Одноразовые операции | Долгоживущие рабочие |
| Для начинающих | Рекомендуется | Для опытных |
Заключение
Изоляты — это мощный инструмент для создания отзывчивых Flutter приложений. Для большинства случаев используй compute(), но когда нужна сложная многоправная коммуникация, используй Isolate.spawn(). Правильное использование изолятов делает приложение быстрым и отзывчивым.