Реализация фоновой загрузки/скачивания файлов в мобильном приложении
Разница между обычной и фоновой загрузкой — принципиальная. Обычная работает, пока пользователь смотрит на экран. Фоновая продолжается после сворачивания приложения, блокировки экрана и даже после принудительного завершения процесса системой. Реализация фоновой передачи файлов — одна из тех задач, где «почти работает» и «работает правильно» разделяют несколько тонких деталей.
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 vs DownloadManager vs ForegroundService
Три подхода, у каждого свои сценарии:
| Подход | Подходит для | Минусы |
|---|---|---|
DownloadManager |
Скачивание публичных URL | Только download, нет fine-grained контроля |
WorkManager |
Загрузка/скачивание с логикой повтора | Ограничение 10 минут на задачу |
ForegroundService |
Долгие передачи > 10 мин | Требует уведомление, пользователь может убить |
Для upload больших файлов на Android 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()
Прогресс для фоновых задач
Уведомление с прогрессом — через WorkManager setProgressAsync + наблюдение через WorkManager.getInstance().getWorkInfoByIdLiveData(workId) в UI. На iOS системный URLSession показывает прогресс в Centre управления для скачиваний автоматически. Для кастомного прогресса — URLSessionDownloadDelegate.urlSession(_:downloadTask:didWriteData:).
Flutter: flutter_downloader решает фоновую загрузку для обеих платформ через нативные реализации. WorkManagerPlugin — для custom upload задач. Для React Native — react-native-background-upload (iOS) и кастомный HeadlessTask + WorkManager (Android).
Resumable uploads
Если файл большой (видео 500 МБ+), стоит реализовать resumable upload: файл делится на чанки (например, 5 МБ), каждый чанк загружается отдельно, сервер собирает файл. При обрыве — продолжаем с последнего успешного чанка. Для AWS S3 — multipart upload API, для GCS — resumable upload sessions, для собственного бэкенда — tus протокол (TUSKit на iOS, tus-android-client).
Что входит в работу
Определяем подходящий механизм под платформу и требования, реализуем прогресс в уведомлениях, обработку ошибок с retry, интеграцию с UI. Если нужен chunked/resumable upload — проектируем протокол совместно с бэкендом.
Срок: 4–8 дней с учётом тестирования на реальных устройствах в условиях обрыва сети.







