Сколько изолятов создает Flutter при запуске приложения?

Ответ

При запуске Flutter-приложения создается три основных изолята (Isolate), каждый из которых работает в отдельном потоке и имеет свою память:

  1. UI Isolate (Основной изолят): Запускает функцию main(), обрабатывает события (тапы, жесты), выполняет код Dart-приложения и строит слои (layer tree) для рендеринга. Только этот изолят имеет доступ к Flutter UI.
  2. GPU Isolate: Отвечает за преобразование layer tree в команды для графического процессора (GPU) через Skia (или Impeller).
  3. 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 — это прямой путь к говно-анимациям и фризам. Не делай так. Выноси всю тяжесть в отдельные изоляты, и будет тебе счастье.