MyFamilyTracker
Real-time family location sharing — Firebase Realtime DB for sub-second propagation, WorkManager + ForegroundService for OS-compliant background collection, geofencing via Google Maps API.
Android's background process restrictions exist to protect battery life and user privacy. They also make background location collection significantly harder than it looks. Here is what actually works, why, and what the alternatives get wrong.
On this page
Background location collection on Android is harder than it looks. There are multiple approaches that appear to work in development and fail in production. The failure is always the same: the OS kills the process, location updates stop, and the user has no idea until they check the app and find it hasn't updated in an hour.
The only approach that is both compliant with Android OS restrictions and reliable in production is WorkManager with a ForegroundService. Here is why everything else fails, and how the compliant pattern works.
Android's OS enforces battery and memory constraints by killing background processes. The targeting of specific processes depends on several factors: battery optimization settings, device manufacturer customizations (which vary significantly), app standby buckets, and the process priority at the time the OS needs to reclaim resources.
Location collection is expensive. GPS hardware, network location providers, and Fused Location Provider all consume battery. The OS is aware of this. An app running background location collection that isn't showing the user a persistent notification is treated as a candidate for termination under any resource pressure.
The Doze mode and App Standby restrictions compound this. When the device is idle, network access, wake locks, and alarm managers are restricted. Even if your background service survives the initial kill protection, it may be unable to collect or transmit location data when the device has been idle.
Plain coroutines in a Service. A
ServicestartForeground()// This will be killed
class LocationService : Service() {
private val scope = CoroutineScope(Dispatchers.IO)
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
scope.launch {
while (true) {
collectLocation() // won't run reliably
delay(10_000)
}
}
return START_STICKY
}
}START_STICKYAlarmManager for periodic location. AlarmManager can schedule periodic work, but it doesn't hold a process alive between alarms. The OS can defer inexact alarms (which is the only kind available in background). Under Doze mode, even exact alarms are deferred. AlarmManager-based location collection will produce gaps.
JobScheduler directly. JobScheduler is the right primitive for deferred background work, but it doesn't provide a mechanism for running continuously with user-facing notification. It's appropriate for periodic sync operations, not continuous location streaming.
WorkManager is the recommended API for background work in Android. It uses JobScheduler internally on API 23+, handles rescheduling after process death, and respects OS battery restrictions while still guaranteeing eventual execution.
For continuous location collection that needs to run while the app is in the background, the correct pattern is a WorkManager
CoroutineWorkersetForeground()class LocationWorker(
context: Context,
params: WorkerParameters
) : CoroutineWorker(context, params) {
override suspend fun doWork(): Result {
// Promote to foreground — shows persistent notification, prevents killing
setForeground(createForegroundInfo())
val locationClient = FusedLocationProviderClient(applicationContext)
val locationRequest = LocationRequest.Builder(
Priority.PRIORITY_HIGH_ACCURACY,
10_000L // 10 second interval
).build()
return try {
collectLocationUpdates(locationClient, locationRequest)
Result.success()
} catch (e: CancellationException) {
Result.success() // Worker was cancelled cleanly
} catch (e: Exception) {
Result.retry()
}
}
private suspend fun collectLocationUpdates(
client: FusedLocationProviderClient,
request: LocationRequest
) {
client.locationFlow(request).collect { location ->
// Write to Firebase Realtime DB
saveLocationToDatabase(location)
}
}
private fun createForegroundInfo(): ForegroundInfo {
val notification = NotificationCompat.Builder(applicationContext, CHANNEL_ID)
.setContentTitle("Location sharing active")
.setContentText("Family members can see your location")
.setSmallIcon(R.drawable.ic_location)
.setOngoing(true)
.build()
return ForegroundInfo(NOTIFICATION_ID, notification)
}
}The
setForeground()FusedLocationProviderClient@SuppressLint("MissingPermission")
fun FusedLocationProviderClient.locationFlow(
request: LocationRequest
): Flow<Location> = callbackFlow {
val callback = object : LocationCallback() {
override fun onLocationResult(result: LocationResult) {
result.locations.forEach { location ->
trySend(location)
}
}
}
requestLocationUpdates(request, callback, Looper.getMainLooper())
.addOnFailureListener { e -> close(e) }
awaitClose {
removeLocationUpdates(callback)
}
}awaitCloseremoveLocationUpdatesWorkManager's
OneTimeWorkRequestsetForeground()val locationWork = OneTimeWorkRequestBuilder<LocationWorker>()
.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
.build()
WorkManager.getInstance(context).enqueueUniqueWork(
"location_tracking",
ExistingWorkPolicy.KEEP, // don't restart if already running
locationWork
)ExistingWorkPolicy.KEEPstartLocationTracking()To stop tracking:
WorkManager.getInstance(context).cancelUniqueWork("location_tracking")Cancellation is clean — the
awaitCloseremoveLocationUpdatesThe compliant pattern described above works on stock Android. OEM behavior is the part of the documentation that doesn't exist but matters most in practice.
Samsung, Xiaomi, Huawei, and OnePlus all have battery optimization layers that operate outside the Android OS specification. These layers can kill foreground services with persistent notifications, defer WorkManager execution, and restrict background network access — even when the app has all required permissions and is following the documented pattern.
The mitigations are imperfect:
ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONSPRIORITY_HIGH_ACCURACYsetExpedited()None of these guarantees protection against aggressive OEM kill layers. The most reliable mitigation is user education — if the app's core function is background location sharing, the onboarding flow should walk the user through the battery optimization exemption process for their specific device.
This is an unsatisfying answer. It reflects the actual state of Android background process reliability across the device ecosystem.
Background location collection requires the following permissions:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />ACCESS_BACKGROUND_LOCATIONACCESS_FINE_LOCATIONPlay Store requires a privacy policy URL and a Data Safety declaration that accurately describes background location collection. The declaration must specify what data is collected, how it is used, whether it is shared, and whether it can be deleted.
This is the complete picture of what it takes to do background location collection correctly on Android. The technical implementation is one part. The permissions model, OEM compatibility, and compliance requirements are the rest.
Sudarshan Chaudhari
AI Systems Builder / Product Engineer
Bangkok, Thailand
Solo Android developer with 13+ years in QA, building Android apps, AI automation systems, and developer tools at SudarshanTechLabs.
Related Posts
Related Apps
Real-time family location sharing — Firebase Realtime DB for sub-second propagation, WorkManager + ForegroundService for OS-compliant background collection, geofencing via Google Maps API.
Building something? Available for Android dev and QA consulting.
Work with meComments — powered by Giscus