Skip to content

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

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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions