Реалізація фонової записи геолокації в мобільному додатку
На Xiaomi з MIUI 14 foreground service убит через 8 хвилин після вимкнення екрану. Трек оборвався. Користувач думає, що додаток працює — сповіщення в статусбарі є, іконка є. Але FusedLocationProviderClient перестав отримувати оновлення, тому що процес убит батарейним менеджером MIUI.
Це найпоширеніша причина сломаного геотрекінгу на Android — й у неї немає універсального рішення. Є набір заходів, які разом дають прийнятний результат.
Android: як вижити в агресивних лаунчерах
Foreground Service — необхідний мінімум. Без нього трекінг не живе ніде. Сервіс запускається з startForeground(id, notification), тип FOREGROUND_SERVICE_TYPE_LOCATION (обов'язковий з Android 10). Сповіщення повинно показувати поточний статус — «ідеся запис» або поточну швидкість.
Автозапуск на MIUI. com.miui.securitycenter → «Автозапуск» — при першому запуску показуємо Intent, спрямований користувача в налаштування. Це єдиний спосіб вижити на Xiaomi. Аналогічно для Huawei: com.huawei.systemmanager → «Управління батареєю» → «Запустити вручну». Список intent'ів по виробникам — у бібліотеці AutoStarter (Android).
WakeLock — не допомагає одиночно. PARTIAL_WAKE_LOCK утримує CPU, але не захищає процес від kill на рівні MIUI/EMUI. Використовуємо в парі з foreground service.
Конфігурація LocationRequest. Для трекінгу людини пішки: interval = 10_000 мс, fastestInterval = 5_000 мс, priority = Priority.PRIORITY_HIGH_ACCURACY. Для транспортного трекінгу: interval = 3_000 мс. Для фонової записи маршруту без поспіху: interval = 30_000 мс з Priority.PRIORITY_BALANCED_POWER_ACCURACY — витрата батареї в 3 рази менша.
WorkManager як watchdog. Запускаємо PeriodicWorkRequest кожні 15 хвилин (мінімальний інтервал WorkManager). Якщо foreground service не працює — watchdog його перезапускає. Не ідеальне рішення, але додає надійність.
iOS: простіше, але зі своїми обмеженнями
На iOS CLLocationManager з allowsBackgroundLocationUpdates = true + background mode location в entitlements працює надійно. iOS не убиває location services в фоні. Але є нюанси:
pausesLocationUpdatesAutomatically = false обов'язково. Інакше iOS сама вирішить приостановити оновлення «для економії батареї», коли користувач довго стоїть на місці.
desiredAccuracy. kCLLocationAccuracyBest дає 5–10 метрів, але висасує батарею. kCLLocationAccuracyNearestTenMeters — достатньо для більшості сценаріїв трекінгу. kCLLocationAccuracyHundredMeters з distanceFilter = 50 — для простої записи «де був».
Significant Location Changes. startMonitoringSignificantLocationChanges() — це не трекінг, а «був в іншому районі міста». Спрацьовує при зміні соти (~300–500 метрів). Підходить для логування відвіданих місць, не для безперервного маршруту.
App termination. Якщо користувач смахнув додаток з свайпера — трекінг припиняється. iOS не піднімає додаток автоматично через location. Рішення: при отриманні applicationWillTerminate показуємо попередження «закриття додатку зупинить запис маршруту».
Типові помилки реалізації
| Помилка | Наслідок | Рішення |
|---|---|---|
| Запис кожної точки в мережу окремим HTTP-запитом | Висока витрата батареї, часті помилки мережі | Батч-буфер в пам'яті, відправка кожні 30 сек |
| Зберігання треку лише в пам'яті | Втрата даних при kill процесу | Персистентна черга в SQLite |
PRIORITY_HIGH_ACCURACY без необхідності |
Батарея садиться за 4–5 годин | Збалансувати точність під сценарій |
Не запитувати SCHEDULE_EXACT_ALARM (Android 12+) |
WorkManager watchdog працює неточно | Додати permission й використовувати AlarmManager |
Батч-відправка координат
Кожна точка GPS — це 3 числа + timestamp. Окремий HTTP-запит на кожну точку — розточительство. Буфер в пам'яті (або SQLite якщо потрібна надійність) з відправкою кожні N секунд або M точок:
// Android: накопичуємо в ViewModel, відправляємо батчем
private val locationBuffer = mutableListOf<LocationPoint>()
fun onLocationUpdate(location: Location) {
locationBuffer.add(location.toPoint())
if (locationBuffer.size >= BATCH_SIZE || isTimeToFlush()) {
sendBatch(locationBuffer.toList())
locationBuffer.clear()
}
}
На iOS аналогічно через @Published var buffer: [CLLocation] в ObservableObject.
Висновок
Надійна фонова геолокація — це не одна строка коду. Це foreground service + правильний LocationRequest + батч-буфер + watchdog + користувацький онбординг з дозволами на автозапуск. На iOS простіше, на Android — більше edge-кейсів під конкретних виробників.
Реалізація під один сценарій (пішеходний трекінг, автомобільний, польові роботи) займає 3–5 робочих днів. Якщо потрібна кросплатформена реалізація з урахуванням специфіки всіх Android-лаунчерів — близько тижня-двох.







