Android Geofences That Survive Reboot & App Updates
GeofencingClient keeps your geofences in Google Play services — and that store is wiped on device reboot, app update, and when location is toggled off/on. Nothing warns you. You have to re-register your geofences yourself on BOOT_COMPLETED, on MY_PACKAGE_REPLACED, and on app launch — from a list you persisted locally.
Your Android geofences work perfectly in testing. Then a user reboots their phone — and every geofence is gone, silently, until the next time they open your app. This is expected Android behavior, and it catches almost everyone. Here's precisely when Android drops geofences and how to make them stick.
Where Android keeps geofences (and why they vanish)
When you call GeofencingClient.addGeofences(), the geofences live inside Google Play services, not your app process. That's what lets them fire when your app is closed. But that store isn't permanent — the OS clears it on several everyday events, and it does not restore them or notify you.
When Android silently drops your geofences
- Device reboot — the most common one.
- Your app is updated (a new APK install replaces the old one).
- Location services turned off, then on.
- Google Play services updates or is cleared.
- Force Stop by the user, and sometimes aggressive battery-optimization kills on certain OEMs.
The fix: re-register on the right events
The pattern is simple once you know the triggers: keep your geofence definitions in local storage, and re-add them to GeofencingClient whenever one of the "drop" events happens.
- Add a
BroadcastReceiverforandroid.intent.action.BOOT_COMPLETED(needs theRECEIVE_BOOT_COMPLETEDpermission). - Also handle
android.intent.action.MY_PACKAGE_REPLACEDfor app updates. - Re-register on app launch too, as a belt-and-suspenders backstop.
- Persist the geofence list locally (e.g. a small store) so you can re-add them offline, without waiting on a network call.
A minimal Kotlin sketch
class BootReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val a = intent.action
if (a == Intent.ACTION_BOOT_COMPLETED ||
a == Intent.ACTION_MY_PACKAGE_REPLACED) {
// Re-add the geofences we persisted locally — no network needed.
val zones = FenceStore.load(context)
val client = LocationServices.getGeofencingClient(context)
client.addGeofences(buildRequest(zones), geofencePendingIntent(context))
}
}
}
<!-- AndroidManifest.xml -->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<receiver android:name=".BootReceiver" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
</intent-filter>
</receiver>
Delivering events when the app is dead
Geofence transitions are delivered to a PendingIntent, so your BroadcastReceiver fires even when your app process isn't running. To act on it — show a notification, record the event — do that work in the receiver from locally-cached content. For the upload, hand it to WorkManager with a NetworkType.CONNECTED constraint rather than calling the network inline: the receiver's ~10-second window can't guarantee an HTTP request finishes, but a WorkManager job runs when connectivity returns and survives Doze and reboot. You do not need a persistent foreground service for geofencing.
ACCESS_BACKGROUND_LOCATION on Android 10+ (and POST_NOTIFICATIONS on 13+); tune setNotificationResponsiveness() and a loitering delay for dwell; debounce boundary flapping from GPS jitter; and test on OEMs with aggressive battery management (Xiaomi, Huawei, Samsung), where users may need to allow "auto-start".
The short version
Android geofences are not durable — they're wiped on reboot, app update, and location toggles. Persist your zones locally and re-register them on BOOT_COMPLETED, MY_PACKAGE_REPLACED, and app start. Do that and your geofencing survives the real world. (The iOS side has its own trap — the 20-region limit.)
Skip the boilerplate
GeofenceKit re-registers on reboot and app update, uploads events in the background (WorkManager on Android, background URLSession on iOS) so delivery survives app-kill, Doze, and reboot, and fires notifications from cached content — as one SDK for Android, iOS, React Native, and Flutter. You manage zones in a dashboard; the SDK keeps them alive on-device.
Start free — get an API key