Реалізація фонового відстеження геолокації в мобільному додатку
Ми стикалися з проектами, де фоновий трекінг «з'їдав» заряд за 2 години, а на Xiaomi додаток просто не отримувало координати після переходу в фон. Обидві проблеми — наслідок неправильної архітектури, а не самого факту фонового виконання. У цій статті розберемо, як ми будуємо надійний трекінг з прийнятною витратою батареї на iOS та Android.
Проблеми фонового трекінгу
iOS з версії 13 жорстко обмежує фонове виконання. Навіть з увімкненою Background Modes → Location updates система може призупинити додаток у режимі «suspended», і оновлення геолокації перестануть надходити. Тригером для відновлення служить лише значне переміщення (Significant Location Change) або перетин геозони — якщо додаток не встиг увійти в «background task» до переходу в фон.
Android — своя картина. З версії 8.0 JobScheduler та Doze Mode обмежують фонові операції до одного разу на годину при «неактивній» зарядці. На Android 10 з'явилося ACCESS_BACKGROUND_LOCATION як окремий дозвіл. На MIUI 12+, Samsung One UI 3+, OPPO ColorOS — ще й виробники додають власні battery killers, які вбивають процес раніше систематично.
Яку стратегію обрати для iOS?
Три підходи — обираємо за задачею. Порівняння в таблиці:
| Стратегія | Точність | Витрата батареї | Працює в terminated | Застосування |
|---|---|---|---|---|
| Standard Location Updates | Висока (метр) | Висока | Ні | Навігація в реальному часі |
| Significant Location Change | ~500 м | Низька | Так | Трекінг доставки, не критичний до секунд |
| Visits Monitoring | На основі зупинок | Мінімальна | Так | Аналітика переміщень |
Для реального кур'єрського трекінгу комбінуємо: при активному додатку — Standard Updates з desiredAccuracy = kCLLocationAccuracyBestForNavigation, при переході в фон — перемикаємося на Significant Location Change. Перемикання в applicationDidEnterBackground / applicationWillEnterForeground.
func applicationDidEnterBackground(_ application: UIApplication) { locationManager.stopUpdatingLocation() locationManager.startMonitoringSignificantLocationChanges() } func applicationWillEnterForeground(_ application: UIApplication) { locationManager.stopMonitoringSignificantLocationChanges() locationManager.startUpdatingLocation() } beginBackgroundTask(withName:expirationHandler:) дає 30 секунд на завершення поточної операції при переході в фон — використовуємо для відправлення останніх точок на сервер. Завдяки цій комбінації вдається знизити витрату трафіку на 40% порівняно з безперервним трекінгом.
Як налаштувати Android Foreground Service?
Foreground Service з повідомленням — єдиний надійний спосіб. Без нього на Android 8+ трекінг зупиниться протягом кількох хвилин.
class LocationTrackingService : Service() { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { val notification = buildTrackingNotification() startForeground(NOTIF_ID, notification) val request = LocationRequest.Builder( Priority.PRIORITY_BALANCED_POWER_ACCURACY, 10_000L // кожні 10 секунд ) .setMinUpdateDistanceMeters(20f) .build() fusedLocationClient.requestLocationUpdates(request, locationCallback, null) return START_STICKY } } START_STICKY гарантує перезапуск сервісу системою при вбивстві. Для пристроїв з агресивним battery killer (Xiaomi/Samsung) додаємо BroadcastReceiver на BOOT_COMPLETED та MY_PACKAGE_REPLACED для автозапуску.
Координати буферизуємо в Room локально — WorkManager з CONNECTED constraint відправляє їх при наявності мережі. Така архітектура економить до 50% заряду порівняно з постійною відправкою даних.
Порівняння стратегій для Android
| Стратегія | Працює в Doze | Батарея | Надійність |
|---|---|---|---|
| Foreground Service | Так | Середня | Висока |
| JobScheduler | Ні (рідкі вікна) | Низька | Низька |
| Passive (AlarmManager) | Ні | Мінімальна | Дуже низька |
Foreground Service — єдиний варіант для продакшну. JobScheduler підходить лише для разових перевірок, але не для безперервного трекінгу.
Flutter: два підходи
background_locator_2 — відкритий пакет, використовує platform-specific фонові механізми. Налаштування через BackgroundLocator.registerLocationUpdate з LocationSettings. Працює через окремий FlutterEngine в ізольованому Dart-оточенні. Однак на деяких прошивках (MIUI) може відключатися без повідомлення.
flutter_background_geolocation (Transistor Software, платний) — більш надійний варіант з готовою обробкою iOS/Android батарейних обмежень, геозонами, автоматичним увімкненням/вимкненням трекінгу за розкладом. Для продакшн-додатків з високими вимогами до надійності обираємо платний пакет — його вартість компенсується за перший тиждень підтримки за рахунок економії на батарейних скаргах.
Що входить у роботу
- Аудит поточної архітектури трекінгу (якщо проект не з нуля)
- Проектування стратегії під конкретні сценарії використання
- Реалізація на iOS (Swift), Android (Kotlin) або Flutter
- Обробка edge cases: перехід у фон, вбивство процесу, reboot, низький заряд
- Інтеграція з сервером (REST/GraphQL, буферизація, відправка)
- Тестування на реальних пристроях: iPhone 12 проти Xiaomi Mi 11, Samsung Galaxy
- Документація по архітектурі та деплой
Проконсультуйтеся з нашим інженером щодо вашого сценарію — ми допоможемо обрати оптимальний стек і стратегію.
Терміни та вартість
Термін реалізації — від 4 до 8 днів. Вартість розраховується індивідуально залежно від кількості платформ, складності інтеграцій та вимог до точності. Ми працюємо на ринку більше 5 років, реалізували понад 20 проектів з фоновою геолокацією для логістики, фітнесу та соціальних мереж. Замовте аудит поточної архітектури трекінгу — напишіть нам, щоб обговорити деталі.
Тестування фонового трекінгу
Найчастіший баг — працює на дев-пристрої, не працює у частини користувачів. Причина: тест-пристрій не в Doze Mode, у розробника немає MIUI. Чекліст для перевірки:
- Тест з вимкненим екраном на 30+ хвилин
- Тест з увімкненою «Оптимізацією батареї»
- Тест на Xiaomi з MIUI 12+, Samsung з One UI 4+
- Тест при переході в авіарежим і назад
- Перевірка, що сервіс перезапускається після reboot
Додаткові матеріали: Background Location Updates на iOS, Foreground Services на Android.







