Back to blog
Article

Background location tracking on cheap Android without killing the battery

Background location tracking on cheap Android without killing the battery
S

StriveBit

4 min readMobile Development

Background location tracking that does not destroy the battery or the user's trust

A field-reporting app we maintain for a logistics client in Ghaziabad tracks delivery staff throughout their shift. The devices are mostly Redmi 9 Activs and Realme C25s — phones with 5000 mAh batteries but processors that throttle aggressively under sustained background work.

The first version we shipped used `@react-native-community/geolocation` with a foreground service that requested updates every 5 seconds. Battery drain was around 18% per hour. Staff were plugging in at lunch and still running dry by 4 PM. We had to rethink the whole approach.

The core problem is not the GPS chip. It is the wakeups. Every location request wakes the CPU, fires the JS bridge, serialises a JSON payload, and writes to AsyncStorage or posts to the server. On a cheap Android, each cycle costs roughly 0.4% battery. At 5-second intervals, that adds up fast.

We moved to `react-native-background-actions` for the foreground service, but the real win was changing how often we ask for a fix. Android's `FusedLocationProviderClient` with `setInterval(60000)` and `setFastestInterval(15000)` gives us a balance: one minute between normal updates, but it will return a fix sooner if another app on the device has already requested one. We get the benefit of coalesced requests without paying for them.

val request = LocationRequest.Builder(Priority.PRIORITY_BALANCED_POWER_ACCURACY)
    .setIntervalMillis(60000)
    .setMinUpdateIntervalMillis(15000)
    .setMaxUpdateDelayMillis(120000)
    .build()

`PRIORITY_BALANCED_POWER_ACCURACY` uses Wi-Fi and cell tower triangulation instead of GPS when the accuracy requirement is loose. For tracking a delivery vehicle on a known route, 50-meter accuracy is enough. We switch to `PRIORITY_HIGH_ACCURACY` only when the app is in the foreground and the user is actively looking at a map.

The JS side batches fixes in memory and flushes every 2 minutes or when the batch hits 20 entries, whichever comes first. We use a simple array in a ref and a `setInterval` that posts to the backend. If the post fails, the batch stays and retries on the next interval. This reduces network wakeups from 60 per hour to roughly 3.

const batchRef = useRef([]);

useEffect(() => {
  const flush = async () => {
    if (batchRef.current.length === 0) return;
    const payload = batchRef.current.splice(0, batchRef.current.length);
    try {
      await api.post('/location/batch', { points: payload });
    } catch {
      batchRef.current.unshift(...payload);
    }
  };
  const timer = setInterval(flush, 120000);
  return () => clearInterval(timer);
}, []);

Battery drain dropped to 6% per hour. Staff now finish a 9-hour shift with 40% remaining on most days.

The trust part is harder than the battery part. Background location is the single most scrutinised permission on the Play Store. The rejection we got on the fourth submission taught us to be explicit about what the user sees. The foreground notification must say what the app is doing and why. We use a persistent notification: "[App name] is tracking your location for your active delivery shift." Tapping it opens the app. Swiping it away stops tracking and marks the shift as paused.

We also added a visible indicator in the app itself. When tracking is active, a small dot appears next to the shift timer. When the user goes off-shift, we call `stopForegroundService` immediately. We do not keep tracking "just in case." If a staff member forgets to clock out, the backend infers shift end from the last received fix plus a 10-minute silence window and closes the shift automatically.

One thing we decided not to do is geofencing. The client asked for arrival alerts when a delivery executive reaches a drop point. Geofence registration on Android is reliable, but on these cheap devices with aggressive battery optimisation, the geofence transition events get delayed by 3-5 minutes. That is worse than just polling at 1-minute intervals and computing arrival on the server. We poll and compute. The tradeoff is a small amount of wasted location data, but the alerts are consistent.

The approach we settled on is not sophisticated. It is a foreground service with a 60-second interval, batched uploads, and a notification that tells the truth. The battery numbers are real and measured on actual devices in the field. That is what matters.

Back to all articles

Ready to build something great?

We help ambitious teams build software that lasts. If you're interested in working with us or want to discuss your project, let's connect.

Get in touch