Реализация фоновой записи геолокации в мобильном приложении
На Xiaomi с MIUI 14 foreground service убит через 8 минут после выключения экрана. Трек оборвался. Пользователь думает, что приложение работает — уведомление в статусбаре есть, иконка есть. Но FusedLocationProviderClient перестал получать обновления, потому что процесс убит батарейным менеджером MIUI. Мы сталкивались с этой проблемой в каждом втором проекте по геотрекингу. Наша команда накопила опыт обхода ограничений вендоров и готова реализовать надёжное решение под ключ. Получите консультацию по вашему сценарию — оценим проект за один рабочий день.
Это самая распространённая причина сломанного геотрекинга на Android — и у неё нет универсального решения. Есть набор мер, которые вместе дают приемлемый результат. Наши клиенты экономят до 40% времени на отладке, используя проверенные конфигурации.
Почему трекинг обрывается на Android?
Foreground Service — необходимый минимум. Без него трекинг не живёт нигде. Сервис запускается с startForeground(id, notification), тип FOREGROUND_SERVICE_TYPE_LOCATION (обязателен с Android 10). Уведомление должно показывать текущий статус — «идёт запись» или текущую скорость. Мы гарантируем, что с правильно настроенным foreground service трекинг работает стабильно на 80% устройств. Для детального ознакомления обратитесь к документации по Foreground Service.
Автозапуск на 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 раза меньше расход батареи. Оптимизация интервала позволяет продлить время работы до 12 часов с одного заряда.
Подробная конфигурация LocationRequest
Для пешего трекинга используйте:
LocationRequest.create() .setInterval(10000) .setFastestInterval(5000) .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY) Для автомобильного трекинга:
LocationRequest.create() .setInterval(3000) .setFastestInterval(2000) .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY) WorkManager как watchdog — запускаем PeriodicWorkRequest каждые 15 минут (минимальный интервал WorkManager). Если foreground service не работает — watchdog его перезапускает. Это добавляет надёжности: по нашим тестам, процент успешных сессий возрастает с 70% до 95%. К тому же, такое решение обходится дешевле постоянного мониторинга.
Как решить проблему автозапуска?
Единственный способ — онбординг пользователя. При первом запуске приложения показываем диалог с инструкцией и intent'ом в настройки. Без этого даже идеальный код не спасёт. Мы включаем такой онбординг во все проекты по умолчанию.
Что делать, если iOS тоже подводит?
На iOS CLLocationManager с allowsBackgroundLocationUpdates = true + background mode location в entitlements работает надёжно. iOS не убивает location services в фоне. Но есть нюансы:
pausesLocationUpdatesAutomatically = false обязательно. Иначе iOS сама решит приостановить обновления «для экономии батареи», когда пользователь долго стоит на месте.
desiredAccuracy: kCLLocationAccuracyBest даёт 5–10 метров, но высасывает батарею. kCLLocationAccuracyNearestTenMeters — достаточно для большинства сценариев трекинга. kCLLocationAccuracyHundredMeters с distanceFilter = 50 — для простой записи «где был». По сравнению с Android, iOS требует в 2 раза меньше настроек для стабильной работы.
Significant Location Changes: startMonitoringSignificantLocationChanges() — это не трекинг, а «был в другом районе города». Срабатывает при смене соты (~300–500 метров). Подходит для логирования посещённых мест, не для непрерывного маршрута.
App termination: если пользователь смахнул приложение из свайпера — трекинг прекращается. iOS не поднимет приложение автоматически через location. Решение: при получении applicationWillTerminate показываем предупреждение «закрытие приложения остановит запись маршрута».
Рекомендуемые настройки для разных сценариев
| Сценарий | Интервал (ms) | Приоритет | Расход батареи |
|---|---|---|---|
| Пешеходный трекинг | 10000 | HIGH_ACCURACY | Умеренный (~8%/ч) |
| Автомобильный трекинг | 3000 | HIGH_ACCURACY | Высокий (~15%/ч) |
| Фоновый мониторинг | 30000 | BALANCED | Низкий (~3%/ч) |
Сравнение настроек для Android и iOS
| Параметр | Android | iOS |
|---|---|---|
| Управление сервисом | Foreground service + WorkManager | Background modes + allowsBackgroundLocationUpdates |
| Минимальные требования для фона | FOREGROUND_SERVICE_TYPE_LOCATION, разрешения |
Background Modes: Location updates, NSLocationAlwaysAndWhenInUseUsageDescription |
| Оптимизация батареи | PRIORITY_BALANCED_POWER_ACCURACY, batch-буфер |
kCLLocationAccuracyHundredMeters, distanceFilter |
| Надёжность при убийстве | WorkManager watchdog, автозапуск | Только если приложение не закрыто свайпом |
| Расход батареи за час трекинга | ~8% (при балансных настройках) | ~5% |
Батч-отправка координат: каждая точка GPS — это 3 числа + timestamp. Отдельный HTTP-запрос на каждую точку — расточительство. Буфер в памяти (или SQLite если нужна надёжность) с отправкой каждые N секунд или M точек. Использование батч-буфера снижает сетевую нагрузку в 5 раз по сравнению с поточечной отправкой.
// 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.
Как мы реализуем фоновую геолокацию под ключ?
Наш процесс включает пять этапов:
- Анализ сценариев использования и выбор стека (Swift/Kotlin/Flutter).
- Настройка foreground service и батч-буфера.
- Интеграция watchdog и онбординга для Android.
- Тестирование на 10+ реальных устройствах (Xiaomi, Huawei, Samsung, Pixel, iPhone).
- Деплой в App Store и Google Play с документацией.
Типичные ошибки реализации
Основные проблемы: запись каждой точки в сеть отдельным HTTP-запросом (высокий расход батареи, частые ошибки сети) — решается батч-буфером в памяти с отправкой каждые 30 секунд. Хранение трека только в памяти ведёт к потере данных при kill процесса — необходима персистентная очередь в SQLite. Использование PRIORITY_HIGH_ACCURACY без необходимости сажает батарею за 4–5 часов — балансируйте точность под сценарий. На Android 12+ не забудьте запросить SCHEDULE_EXACT_ALARM, иначе WorkManager watchdog работает неточно — добавьте permission и используйте AlarmManager.
Что входит в работу
- Детальный анализ сценариев и конфигурация под целевые устройства.
- Реализация foreground service, LocationRequest, батч-буфера, watchdog.
- Онбординг пользователя с интентом на автозапуск.
- Тестирование на 10+ моделях.
- Документация по эксплуатации и код-ревью.
- Гарантийная поддержка 1 месяц после деплоя.
Итог
Надёжная фоновая геолокация — это не одна строка кода. Это foreground service + правильный LocationRequest + батч-буфер + watchdog + пользовательский онбординг с разрешениями на автозапуск. На iOS проще, на Android — больше edge-кейсов под конкретных производителей. Мы — команда сертифицированных разработчиков с опытом более 5 лет и 30+ проектами в этой области. Свяжитесь с нами, чтобы обсудить ваш проект — оценим сроки и стоимость бесплатно. Получите консультацию по вашему сценарию уже сегодня.







