Skip to content

[Bug]: LocationPuck leaks one fused-location provider (thread + native memory) per MapView destroy on Android #4225

@llamington

Description

@llamington

Mapbox Version

11.20.1 (default)

React Native Version

0.81.5

Platform

Android

@rnmapbox/maps version

10.3.1

Standalone component to reproduce

import React, { useEffect, useState } from 'react';
import { MapView, Camera, LocationPuck } from '@rnmapbox/maps';

const REMOUNT_INTERVAL_MS = 3000;

function BugReportExample() {
  const [mapVisible, setMapVisible] = useState(true);

  // Simulates navigating to and from a screen containing a map:
  // each cycle mounts a fresh MapView + LocationPuck, then unmounts it.
  useEffect(() => {
    const interval = setInterval(() => {
      setMapVisible((visible) => !visible);
    }, REMOUNT_INTERVAL_MS);
    return () => clearInterval(interval);
  }, []);

  if (!mapVisible) {
    return null;
  }

  return (
    <MapView style={{ flex: 1 }}>
      <Camera centerCoordinate={[-74.00597, 40.71427]} zoomLevel={14} />
      <LocationPuck />
    </MapView>
  );
}

Location permission must be granted (the puck's location provider is only created once permissions allow it). While the component remounts, count the Play Services fused-location handler threads:

PID=$(adb shell pidof <your.package>)
adb shell "for t in /proc/$PID/task/*/comm; do cat \$t; done" | grep -c fusedLocation

Observed behavior and steps to reproduce

Every mount/unmount cycle of a MapView containing a LocationPuck permanently leaks one FusedLocationProviderClient (visible as a fusedLocationCl… handler thread) plus its native allocations.

Measured in a production app (Expo SDK 54, new architecture, release build, no expo-dev-client), repeatedly opening and backing out of a screen containing a map — exactly 10 navigation cycles per measurement window, thread census + dumpsys meminfo between windows:

Baseline (1 live map) After 10 open/back cycles After 20
fusedLocationCl… threads 3 13 23 (+1 per mount)
MapboxRenderThread 1 1 1 (map teardown itself is clean)

Control: removing only <LocationPuck /> from the same screen and repeating the identical 30-cycle protocol keeps the fused-location thread count constant at 1 and removes the linear component of native-heap growth. The render thread and Java heap are clean in both configurations — the leak is specific to the puck's location provider.

Device: Samsung SM-P613, Android 14 (API 34), Google Play Services 26.19.34.

Expected behavior

Destroying a MapView (or unmounting the LocationPuck) releases the location component's provider: the fused-location client count stays constant across mount/unmount cycles, matching the (correct) behavior of MapboxRenderThread.

Notes / preliminary analysis

The leak is Android-specific in our testing and isolates cleanly to LocationPuck: the identical navigation protocol with the puck removed (and nothing else changed) keeps the fused-location client count constant.

Additional links and references

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions