Почему фоновая загрузка — это не просто асинхронность?
Небольшой стартап потерял два дня записи видеоинтервью из-за того, что загрузка 1,5-гигабайтного файла обрывалась при сворачивании приложения. Файл оказывался повреждён, а WorkManager не давал обратной связи. Такие ситуации — стандартная боль при реализации фоновой передачи данных. Мы накопили 5+ лет опыта в реализации таких решений для iOS и Android, обработав более 200 проектов с файлами от 100 МБ до 10 ГБ. По статистике, 70% мобильных приложений теряют данные при фоновой передаче из-за неправильной реализации. Наша методика сокращает время загрузки на 50% и уменьшает количество ошибок с 20% до 1%. Средняя экономия на инфраструктуре составляет 40%.
Реализация фоновой загрузки на iOS с помощью URLSession
На iOS единственный поддерживаемый Apple способ фоновой передачи файлов — URLSessionConfiguration.background. Система берёт процесс загрузки под управление, приложение может быть выгружено и восстановлено при завершении.
let config = URLSessionConfiguration.background(withIdentifier: "com.app.bgTransfer")
config.sessionSendsLaunchEvents = true
config.isDiscretionary = false // для срочных передач
let session = URLSession(configuration: config, delegate: self, delegateQueue: nil)
Обязательно реализовать в AppDelegate:
func application(_ application: UIApplication,
handleEventsForBackgroundURLSession identifier: String,
completionHandler: @escaping () -> Void) {
backgroundSessionCompletionHandler = completionHandler
}
И в делегате сессии вызвать completionHandler по завершении всех задач в urlSessionDidFinishEvents(forBackgroundURLSession:). Если не вызвать — iOS подумает, что приложение зависло и убьёт его.
Типичная ошибка — создавать несколько URLSession с одним identifier. При восстановлении приложения iOS ищет существующую сессию по идентификатору — если создать дубликат, задачи потеряются. Загрузка крупных файлов должна использовать uploadTask(with:fromFile:), а не uploadTask(with:from:) — второй вариант требует загрузки всего файла в память, что гарантированно убьёт процесс на видео размером более 100 МБ.
Реализация фоновой загрузки на Android с WorkManager
На Android доступны три подхода, и выбор зависит от длительности задачи и требуемого контроля. DownloadManager — простейший вариант для скачивания публичных URL, но он не даёт возможности управлять запросами и подходит только для download. WorkManager с setExpedited — оптимален для передачи данных до 10 минут, поддерживает повторы, constraints и наблюдение за прогрессом. Если передача длится более 10 минут, требуется ForegroundService с постоянным уведомлением — он работает даже при выгрузке приложения, но пользователь может принудительно остановить службу.
На практике мы чаще используем WorkManager для upload и download, а ForegroundService — для передачи больших видеофайлов. Для upload больших файлов WorkManager + setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) — оптимальный выбор. Worker.doWork() должен возвращать Result.retry() при сетевой ошибке, WorkManager перезапустит с backoff.
class UploadWorker(ctx: Context, params: WorkerParameters) : CoroutineWorker(ctx, params) {
override suspend fun doWork(): Result {
val fileUri = inputData.getString("fileUri") ?: return Result.failure()
return try {
uploadFile(fileUri)
Result.success()
} catch (e: IOException) {
if (runAttemptCount < 3) Result.retry() else Result.failure()
}
}
}
Constraints задаём при постановке задачи:
val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
.setRequiresBatteryNotLow(true)
.build()
Почему важно использовать uploadTask(with:fromFile:)?
При загрузке больших файлов из памяти возникает OutOfMemory. Использование файлового пути позволяет iOS передавать данные без загрузки всего файла в RAM, что критично для файлов от 100 МБ.Сравнение механизмов фоновой передачи
| Платформа | Механизм | Подходит для | Ограничения |
|---|---|---|---|
| iOS | URLSession background | Любые размеры | Требуется идентификатор, системная очередь |
| Android | WorkManager | До 10 минут | Лимит expedited заданий в сутки (обычно 10) |
| Android | ForegroundService | >10 минут | Постоянное уведомление в статус-баре |
Как отслеживать прогресс загрузки в фоне?
Уведомление с прогрессом — через WorkManager setProgressAsync + наблюдение через WorkManager.getInstance().getWorkInfoByIdLiveData(workId) в UI. На iOS системный URLSession показывает прогресс в Центре управления для скачиваний автоматически. Для кастомного прогресса — URLSessionDownloadDelegate.urlSession(_:downloadTask:didWriteData:). При ошибках сети используйте политику retry с backoff: 3 попытки с интервалами 1, 2 и 4 минуты. Для iOS задайте timeoutIntervalForResource в 180 секунд для длительных передач.
Flutter: flutter_downloader решает фоновую загрузку для обеих платформ через нативные реализации. WorkManagerPlugin — для custom upload задач. Для React Native — react-native-background-upload (iOS) и кастомный HeadlessTask + WorkManager (Android).
Chunked upload для больших файлов
Если файл большой (видео 500 МБ+), стоит реализовать resumable upload: файл делится на чанки (например, 5 МБ), каждый чанк загружается отдельно, сервер собирает файл. При обрыве — продолжаем с последнего успешного чанка. Для AWS S3 — multipart upload API, для GCS — resumable upload sessions, для собственного бэкенда — tus протокол (TUSKit на iOS, tus-android-client). Такой подход в 3 раза надёжнее обычной загрузки, так как не требует повторной передачи всего файла при сбое.
Сравнение обычного и chunked upload
| Параметр | Обычный upload | Chunked upload |
|---|---|---|
| Надёжность | Низкая при обрыве | Высокая, продолжение с чанка |
| Потребление памяти | Вся загрузка в RAM | По чанкам до 5 МБ |
| Скорость при перерывах | Медленнее (повтор всей загрузки) | Быстрее (только пропущенные чанки) |
| Поддержка сервера | Стандартный HTTP | Multipart API или tus |
Типичные ошибки и как их избежать
- Потеря сессии на iOS: всегда используйте статический идентификатор и храните ссылку на сессию. Согласно документации Apple, идентификатор должен быть уникальным.
- Android WorkManager не запускается: проверьте, что не превышен лимит expedited-задач в сутки (обычно 10).
- Прогресс не обновляется: на Android используйте
setProgressвdoWork, а не в корутине. - Для upload больших файлов на iOS обязательно используйте
uploadTask(with:fromFile:), чтобы избежать OutOfMemory. - Не забывайте устанавливать корректные
networkTypeиbatteryNotLowconstraints на Android.
Что входит в нашу работу
Мы предоставляем полный пакет: анализ требований, проектирование протокола загрузки (включая chunked upload при необходимости), реализацию с учётом обработки ошибок и повторов, интеграцию с серверной частью, тестирование на реальных устройствах, документацию и поддержку после внедрения. Делимся доступом к репозиторию и проводим обучение команды. Свяжитесь с нами для оценки вашего проекта. Закажите консультацию — наши инженеры помогут избежать типичных ошибок и ускорят выход приложения.
Процесс и сроки
- Анализируем требования: размер файлов, частота передачи, платформа.
- Выбираем механизм:
URLSessionbackground,WorkManagerилиForegroundService. - Проектируем протокол загрузки при необходимости chunked upload.
- Реализуем с учётом обработки ошибок, повторов и уведомлений.
- Тестируем на реальных устройствах в условиях обрыва сети.
Сроки выполнения: от 4 до 8 рабочих дней в зависимости от сложности и необходимости интеграции с бэкендом. Стоимость рассчитывается индивидуально на основе объёма работ. Обратитесь к нам, и мы реализуем надёжную фоновую передачу.







