Implementation of Background Geolocation Tracking in a Mobile Application
Background tracking is where most teams get their first complaints from users: "kills battery" or "stops working after an hour." Both symptoms are consequences of choosing the wrong strategy, not background tracking itself.
Why It's Harder Than It Seems
Since iOS 13, the system strictly limits background execution. Even with "Background Modes → Location updates" enabled, the system can suspend the app in "suspended" mode, and geolocation updates stop arriving. Only Significant Location Change or geofence crossing triggers resumption — if the app managed to enter "background task" before going background.
Android has its own picture. Since version 8.0, JobScheduler and Doze Mode limit background operations to once per hour on "inactive" devices. Android 10 introduced ACCESS_BACKGROUND_LOCATION as a separate permission. On MIUI 12+, Samsung One UI 3+, OPPO ColorOS — manufacturers add their own battery killers that stop the process faster systematically.
Correct Architecture for iOS
Three strategies — choose by task:
1. Standard Location Updates — precise updates every N meters. Requires Foreground Service (app basically active) or Background Location capability. High accuracy, high battery drain. Suitable for active navigation.
2. Significant Location Change Monitoring — startMonitoringSignificantLocationChanges(). Updates on ~500m movement or tower switch. Very efficient. Works even in "terminated" state — system launches app in background. Suitable for delivery tracking without second-precision accuracy requirement.
3. Visits Monitoring — startMonitoringVisits(). Detects arrival/departure based on stop analysis. Minimal battery drain. For movement analytics, not navigation.
For real courier tracking, combine: with active app — Standard Updates with desiredAccuracy = kCLLocationAccuracyBestForNavigation, on background — switch to Significant Location Change. Switch in applicationDidEnterBackground / applicationWillEnterForeground.
func applicationDidEnterBackground(_ application: UIApplication) {
locationManager.stopUpdatingLocation()
locationManager.startMonitoringSignificantLocationChanges()
}
func applicationWillEnterForeground(_ application: UIApplication) {
locationManager.stopMonitoringSignificantLocationChanges()
locationManager.startUpdatingLocation()
}
beginBackgroundTask(withName:expirationHandler:) gives 30 seconds to complete current operation when going background — use to send final points to server.
Correct Architecture for Android
Foreground Service with notification is the only reliable way. Without it on Android 8+, tracking stops within minutes.
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 // every 10 seconds
)
.setMinUpdateDistanceMeters(20f)
.build()
fusedLocationClient.requestLocationUpdates(request, locationCallback, null)
return START_STICKY
}
}
START_STICKY guarantees system restart of service on kill. For devices with aggressive battery killer (Xiaomi/Samsung), add BroadcastReceiver on BOOT_COMPLETED and MY_PACKAGE_REPLACED for autostart.
Buffer coordinates locally in Room — WorkManager with CONNECTED constraint sends them when internet available.
Flutter: Two Approaches
background_locator_2 — open package, uses platform-specific background mechanisms. Configuration via BackgroundLocator.registerLocationUpdate with LocationSettings. Works via separate FlutterEngine in isolated Dart environment.
flutter_background_geolocation (Transistor Software, paid) — more reliable variant with ready-made iOS/Android battery limitation handling, geofences, automatic on/off by schedule.
For production apps with reliability requirements, choose paid package — $120 license pays off in first week of support savings.
Testing Background Tracking
Most common bug — works on dev device, doesn't work for some users. Reason: test device not in Doze Mode, developer doesn't have MIUI. Checklist:
- Test with screen off for 30+ minutes
- Test with "Battery Optimization" enabled
- Test on Xiaomi with MIUI 12+, Samsung with One UI 4+
- Test on airplane mode toggle
- Check service restarts after reboot
Timeline: four to eight days — architecture, implementation on needed platforms, edge case handling, testing on real devices with different firmware.







