Ответ
При запуске Flutter-приложения создается три основных изолята (Isolate), каждый из которых работает в отдельном потоке и имеет свою память:
- UI Isolate (Основной изолят): Запускает функцию
main(), обрабатывает события (тапы, жесты), выполняет код Dart-приложения и строит слои (layer tree) для рендеринга. Только этот изолят имеет доступ к Flutter UI. - GPU Isolate: Отвечает за преобразование layer tree в команды для графического процессора (GPU) через Skia (или Impeller).
- Platform Isolate (ранее "IO Isolate"): Обрабатывает асинхронные ввод-вывод, файловые операции и коммуникацию с нативной платформой (Android/iOS) через платформенные каналы (Platform Channels).
Пример создания своего изолята для тяжелых вычислений, чтобы не блокировать UI:
// Функция, которая будет запущена в новом изоляте
void _heavyTask(SendPort mainSendPort) {
final result = performComplexCalculation();
// Отправляем результат обратно в основной изолят
mainSendPort.send(result);
}
// В основном UI-изоляте
Future<void> startBackgroundTask() async {
final receivePort = ReceivePort();
await Isolate.spawn(_heavyTask, receivePort.sendPort);
// Слушаем ответ от нового изолята
receivePort.listen((data) {
print('Результат из изолята: $data');
receivePort.close();
});
}
Блокировка UI Isolate (например, долгим синхронным вычислением) приводит к "зависанию" интерфейса (jank).
Ответ 18+ 🔞
Слушай, я тут разбирался, как эта штука Flutter под капотом работает, и, блядь, сам от себя охуел от её архитектуры. Ну то есть, она ж не просто так рисует твои кнопочки, там целый ёперный театр из изолятов разворачивается, когда ты приложение запускаешь.
Представь себе, что у тебя не телефон, а стройка. И на ней сразу три независимые бригады работают, каждая в своём замкнутом контуре, чтобы друг другу не мешали. Вот это и есть изоляты. Их всегда три штуки стартует, и у каждого — своя память, свой поток, свои задачи. Общаются они только почтовыми голубями, то бишь портами, иначе — никак.
Первая бригада, главная — UI Isolate. Это, можно сказать, прораб. Он с утра приходит, функцию main() запускает, и дальше весь твой код на Dart крутит. Все тапы, свайпы, анимации — это он обрабатывает. И самое главное — только у этого товарища есть пропуск на строящийся объект, то есть право трогать интерфейс Flutter. Он из твоих виджетов строит дерево слоёв (layer tree) и говорит: «Вот, художникам, рисуйте это». Если его загрузить какой-нибудь ерундой вроде пересчёта числа Пи до миллиардного знака, то всё, приехали — интерфейс встанет колом, анимация будет дёргаться. Короче, доверия ебать ноль к его свободному времени.
Вторая бригада — GPU Isolate. Это уже наши художники-оформители. Они берут у прораба (UI Isolate) те самые слои, которые он подготовил, и говорят: «О, окей». А дальше начинается магия: они через графическую библиотеку Skia (или новую Impeller) превращают эти абстрактные слои в конкретные команды для видеокарты телефона. «Закрась вот этот пиксель таким цветом, нарисуй тут скругление». Вся тяжёлая работа по рендерингу — на них, но UI они не блокируют.
Ну и третья команда — Platform Isolate (раньше звался IO Isolate). Это связные и снабженцы. Всё, что связано с внешним миром, — их работа. Запись в файл, чтение с диска, сетевые запросы (хотя они часто свои изоляты порождают) и, что критично, общение с нативной платформой (Android/iOS) через те самые Platform Channels. Хочешь камеру запустить или получить доступ к контактам — твой UI Isolate стучится к этим ребятам, а они уже бегут договариваться с «туземцами»-нативом.
Так, а что делать, если тебе в приложении реально нужно что-то тяжелое посчитать? Ну там, видео обработать или сложную математику сделать? Подозрение ебать чувствую, что если это в UI Isolate запихнуть, будет тебе хиросима с интерфейсом.
Вот тут-то ты и можешь нанять четвёртую, временную бригаду — свой собственный изолят. Создаёшь его, грузишь туда свою тяжёлую функцию, и пусть он там ебушки-воробушки считает, сколько влезет. А когда закончит — отправит результат почтовым голубем обратно в главный изолят. Смотри, как это выглядит в коде:
// Функция, которая будет запущена в новом изоляте. Сидит там одна, как сыч.
void _heavyTask(SendPort mainSendPort) {
final result = performComplexCalculation(); // Твоя пиздопроебибная математика
// Отправляем результат обратно в основной изолят
mainSendPort.send(result);
}
// А это код в основном UI-изоляте
Future<void> startBackgroundTask() async {
final receivePort = ReceivePort(); // Делаем почтовый ящик для приёма писем
// Наняли нового работника, дали ему ящик для ответа
await Isolate.spawn(_heavyTask, receivePort.sendPort);
// Сидим, слушаем почтовый ящик
receivePort.listen((data) {
print('Результат из изолята: $data'); // Опа, а вот и ответ!
receivePort.close(); // Ящик отработал, закрываем
});
}
Суть в чём? Пока этот _heavyTask в своём углу бздит и считает, твой интерфейс продолжает отзывчиво работать, анимации плавные. Потому что UI Isolate не заблокирован. Он просто ждёт весточку. А если бы он сам этим занимался, то был бы полупидор — и считать не может нормально, и интерфейс забросил. Запомни главное правило: блокировка UI Isolate — это прямой путь к говно-анимациям и фризам. Не делай так. Выноси всю тяжесть в отдельные изоляты, и будет тебе счастье.