> Как реализовать работу в фоне во Flutter для отправки данных при возобновлении связи (Flutter, Android)

Уровень: senior · Роль: mobile · Категория: Технические вопросы

Компании: PashaPay

Стек: Flutter, Android

> Пример ответа

Короткий ответ

Для фоновой отправки данных при возобновлении связи во Flutter используйте комбинацию connectivity_plus для мониторинга сети и workmanager или flutter_background_service для выполнения задачи в фоне. Ключевой момент - не полагаться только на lifecycle-методы, так как ОС может убить процесс. На Android надёжнее использовать WorkManager с периодическим запуском и проверкой состояния сети, либо BroadcastReceiver на CONNECTIVITY_CHANGE через нативный код, если нужна мгновенная реакция.

Подробное объяснение

Задача сводится к трём аспектам: детекция восстановления связи, выполнение кода в фоне и гарантия доставки данных. Во Flutter нет прямого доступа к системным событиям сети, поэтому используются плагины.

Основные подходы:

  1. connectivity_plus + WidgetsBindingObserver - работает только пока приложение активно или в фоне, но не после убийства процесса. Подходит для простых сценариев, но не гарантирует выполнение при свёрнутом приложении на Android.

  2. workmanager - обёртка над Android WorkManager и iOS BGTaskScheduler. Позволяет регистрировать периодические задачи (минимум 15 минут на Android) или отложенные задачи с условием NetworkType.CONNECTED. Это самый надёжный способ для отправки данных: система сама запустит задачу при появлении сети, даже если приложение убито.

  3. flutter_background_service - запускает долгоживущий изолят, но требует осторожности: Android ограничивает фоновые процессы, и такой подход быстрее приводит к батарейным проблемам. Используйте только если нужен постоянный мониторинг.

  4. Нативный BroadcastReceiver на CONNECTIVITY_CHANGE - самый мгновенный отклик, но требует написания Kotlin/Java кода и передачи данных через MethodChannel или EventChannel.

Для отправки данных при возобновлении связи оптимальная связка: workmanager с условием NetworkType.CONNECTED + локальное хранилище (shared_preferences, sqflite, hive) для очереди неотправленных данных. Приложение пишет данные в очередь, а фоновая задача забирает их и отправляет на сервер.

На практике

  1. Создайте очередь данных в локальном хранилище. Каждая запись - JSON с payload и метаданными.
  2. Зарегистрируйте фоновую задачу через Workmanager().registerPeriodicTask или registerOneOffTask с NetworkType.CONNECTED.
  3. В callback задачи читайте очередь, отправляйте данные, удаляйте успешно отправленные.
  4. Для мгновенной реакции при активном приложении подпишитесь на стрим Connectivity().onConnectivityChanged и вызывайте ту же функцию отправки.
  5. Обязательно обработайте ошибки сети и повторы - задача должна быть идемпотентной.

На Android 12+ добавьте разрешения INTERNET, ACCESS_NETWORK_STATE, RECEIVE_BOOT_COMPLETED (для перезапуска после перезагрузки). Для workmanager укажите android:enableWorkManager в манифесте.

Пример кода

// main.dart
void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Workmanager().initialize(callbackDispatcher);
  Workmanager().registerPeriodicTask(
    'sync-task',
    'syncData',
    frequency: Duration(minutes: 15),
    constraints: Constraints(networkType: NetworkType.CONNECTED),
  );
  runApp(MyApp());
}

// Фоновая задача
void callbackDispatcher() {
  Workmanager().executeTask((task, inputData) async {
    if (task == 'syncData') {
      await sendPendingData();
    }
    return true;
  });
}

Future<void> sendPendingData() async {
  final queue = await LocalQueue().getPending();
  for (final item in queue) {
    try {
      await ApiClient().send(item.payload);
      await LocalQueue().remove(item.id);
    } catch (e) {
      // оставляем в очереди, попробуем в следующий раз
    }
  }
}

// В UI - мгновенная отправка при появлении сети
Connectivity().onConnectivityChanged.listen((result) {
  if (result != ConnectivityResult.none) {
    sendPendingData();
  }
});

Как отвечать на собеседовании

Начните с уточнения требований: нужно ли отправлять после убийства приложения, какой допустимый интервал задержки, какой объём данных. Это покажет системное мышление. Затем опишите два уровня: активный (стрим connectivity) и фоновый (workmanager). Подчеркните trade-off: workmanager не мгновенный, но надёжный; BroadcastReceiver - мгновенный, но сложнее. Упомяните, что на iOS подходы отличаются (BGTaskScheduler, ограничения по времени). Если спросят про гарантию доставки - говорите про очередь и идемпотентность, а не про "просто отправку".

Что проверяет интервьюер

  • Понимание lifecycle Android и ограничений фоновых процессов.
  • Умение выбирать инструмент под задачу, а не использовать первый попавшийся плагин.
  • Знание, как работает WorkManager: условия, периодичность, гарантии выполнения.
  • Умение проектировать отказоустойчивую отправку: очередь, ретраи, обработка ошибок.
  • Осознание различий между платформами (Android vs iOS).

Типичные ошибки

  • Использование только connectivity_plus в initState - после убийства приложения ничего не отправится.
  • Запуск flutter_background_service без необходимости - быстрый расход батареи и возможный бан от Google Play.
  • Отправка данных прямо в callback без очереди - потеря данных при сбое сети.
  • Игнорирование NetworkType.CONNECTED в constraints - задача будет запускаться без сети и падать.
  • Необработка ошибок в фоновой задаче - возврат false из executeTask приведёт к повторным попыткам и лишнему расходу ресурсов.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью