Background Geolocation Tracking Implementation in Mobile App
On Xiaomi with MIUI 14, foreground service killed after 8 minutes of screen off. Track broke. User thinks app is working — notification in status bar exists, icon exists. But FusedLocationProviderClient stopped receiving updates because process killed by MIUI battery manager.
This is the most common reason for broken geotacking on Android — and has no universal solution. There's a set of measures that together give acceptable result.
Android: How to Survive Aggressive Launchers
Foreground Service — necessary minimum. Without it tracking doesn't survive anywhere. Service launched with startForeground(id, notification), type FOREGROUND_SERVICE_TYPE_LOCATION (mandatory from Android 10). Notification should show current status — "recording" or current speed.
Auto-launch on MIUI. com.miui.securitycenter → "Auto-launch" — on first app launch show Intent directing user to settings. Only way to survive on Xiaomi. Similarly for Huawei: com.huawei.systemmanager → "Battery management" → "Launch manually". List of intents by manufacturer — in AutoStarter library (Android).
WakeLock — doesn't help alone. PARTIAL_WAKE_LOCK holds CPU but doesn't protect process from kill at MIUI/EMUI level. Use in pair with foreground service.
LocationRequest configuration. For pedestrian tracking: interval = 10_000 ms, fastestInterval = 5_000 ms, priority = Priority.PRIORITY_HIGH_ACCURACY. For vehicle tracking: interval = 3_000 ms. For background route recording without rush: interval = 30_000 ms with Priority.PRIORITY_BALANCED_POWER_ACCURACY — battery consumption 3x less.
WorkManager as watchdog. Run PeriodicWorkRequest every 15 minutes (WorkManager minimum interval). If foreground service not working — watchdog restarts it. Not ideal but adds reliability.
iOS: Simpler but with Own Limitations
On iOS CLLocationManager with allowsBackgroundLocationUpdates = true + background mode location in entitlements works reliably. iOS doesn't kill location services in background. But nuances exist:
pausesLocationUpdatesAutomatically = false mandatory. Otherwise iOS itself decides to pause updates "for battery conservation" when user stands still long.
desiredAccuracy. kCLLocationAccuracyBest gives 5–10 meters but drains battery. kCLLocationAccuracyNearestTenMeters — sufficient for most tracking scenarios. kCLLocationAccuracyHundredMeters with distanceFilter = 50 — for simple "where was" recording.
Significant Location Changes. startMonitoringSignificantLocationChanges() — not tracking but "was in different city district." Triggers on cell change (~300–500 meters). Suitable for logging visited places, not continuous route.
App termination. If user swipes app from switcher — tracking stops. iOS won't bring up app automatically via location. Solution: on applicationWillTerminate show warning "closing app will stop recording."
Typical Implementation Errors
| Error | Consequence | Solution |
|---|---|---|
| Record each point to network as separate HTTP request | High battery consumption, frequent network errors | Batch buffer in memory, send every 30 sec |
| Store track only in memory | Data loss on process kill | Persistent queue in SQLite |
PRIORITY_HIGH_ACCURACY unnecessarily |
Battery dies in 4–5 hours | Balance accuracy to scenario |
Not request SCHEDULE_EXACT_ALARM (Android 12+) |
WorkManager watchdog imprecise | Add permission and use AlarmManager |
Batch Coordinate Sending
Each GPS point — 3 numbers + timestamp. Separate HTTP request per point — wasteful. Memory buffer (or SQLite if reliability needed) with sending every N seconds or M points:
// Android: accumulate in ViewModel, send in batch
private val locationBuffer = mutableListOf<LocationPoint>()
fun onLocationUpdate(location: Location) {
locationBuffer.add(location.toPoint())
if (locationBuffer.size >= BATCH_SIZE || isTimeToFlush()) {
sendBatch(locationBuffer.toList())
locationBuffer.clear()
}
}
On iOS similarly via @Published var buffer: [CLLocation] in ObservableObject.
Summary
Reliable background geolocation — not one line of code. This is foreground service + correct LocationRequest + batch buffer + watchdog + user onboarding with auto-launch permissions. On iOS simpler, on Android — more edge cases per manufacturer.
Implementation for one scenario (pedestrian tracking, vehicle, field work) takes 3–5 workdays. Cross-platform with all Android launcher specifics — about one to two weeks.







