Is there an existing issue for this?
Description of the bug
On Android, NavModule.cleanup() removes listeners, stops guidance, clears destinations, and nulls out the module's mNavigator reference, but never invokes the native Navigator#cleanup(). Since the Navigator is a singleton held by NavigationApi, dropping the module's reference doesn't destroy it: its internal location listener keeps running for the lifetime of the app process.
Observable effect after cleanup() resolves successfully from JS:
- A sustained ~1Hz GPS request remains registered with the fused location provider (visible via
adb shell dumpsys location).
- The system location-in-use indicator stays on whenever the app is in the foreground — on any screen, even outside the
NavigationProvider.
- The request re-registers on every return to foreground, and only dies when the process is killed.
This causes unnecessary battery drain and signals location usage to the user after the navigation session has ended.
Expected behavior: after cleanup() resolves, the Navigator's location request should be released and the location indicator should turn off.
Google's instance cleanup best practices recommend calling Navigator#cleanup when the navigation session is completed.
Proposed fix: adding the missing call at the end of the teardown block in NavModule.cleanup() resolves it. The method already captures a local final Navigator navigator reference before nulling out mNavigator, so the call fits the existing pattern:
navigator.getSimulator().unsetUserLocation();
navigator.cleanup(); // <-- missing call
promise.resolve(true);
Verified on a physical Android device: with this change the FLP request is released immediately when cleanup() resolves and the location indicator turns off. Subsequent init() → set destination → startGuidance() → cleanup() cycles work correctly, including backgrounding/foregrounding between cycles.
Possibly related history: #34 addressed guidance continuing after unmount, and #566 fixed navinfo forwarding surviving cleanup(). This appears to be the same class of incomplete-teardown issue, but affecting the Navigator's own location provider. If Navigator#cleanup() was omitted intentionally (e.g. to work around a known race condition on re-initialization), happy to hear the context.
iOS Platform
Not verified
Android Platform
Affected
React Native version
0.83.10
React version
19.2.0
Package version
0.16.3
Native SDK versions
React Native Doctor Output
npx react-native doctor reports only environment-detection issues unrelated
to this bug (it doesn't find Android Studio/SDK paths on Fedora, though
ANDROID_HOME, adb, JDK and Gradle all resolve correctly and builds run fine).
Output of npx expo-env-info:
System:
OS: Linux (Fedora 43)
Node: 24.14.1
npm: 11.11.0
npmPackages:
expo: 55.0.28
expo-router: 55.0.17
react: 19.2.0
react-native: 0.83.10
npmGlobalPackages:
eas-cli: 21.4.0
Expo Workflow: bare (prebuild)
@googlemaps/react-native-navigation-sdk: 0.16.3
Test device: Android [16] ([moto g75 5G]), physical device
Steps to reproduce
- In a screen wrapped by
NavigationProvider, call navigationController.init() (accepting the Terms and Conditions dialog).
- Call
navigationController.cleanup() — the promise resolves successfully.
- Run
adb shell dumpsys location and inspect the fused location provider section: a sustained ~1Hz GPS request is still being delivered, and the system location-in-use indicator remains visible while the app is in the foreground.
- Navigate to any other screen, including screens outside the
NavigationProvider — the indicator stays on.
- Send the app to background (the request stops) and return to foreground — the request re-registers and the indicator comes back. It only stops for good when the app process is killed.
Expected vs Actual Behavior
Expected: after cleanup() resolves, the Navigator should be destroyed and its location request released — the fused location provider request should stop and the system location-in-use indicator should turn off.
Actual: cleanup() resolves with true, but the Navigator singleton is never destroyed (only the module's reference is nulled out). A ~1Hz GPS request stays registered with the fused location provider for the lifetime of the app process, keeping the location indicator on across the whole app whenever it is in the foreground.
Logs: no errors or exceptions are thrown on either side — cleanup() resolves successfully and logcat shows nothing unusual, which is what makes this hard to notice. The only visible evidence is in adb shell dumpsys location, where the GPS provider keeps delivering to the fused location provider at ~1Hz after cleanup:
08-19 11:24:19.784: received GPS locations[1]
08-19 11:24:19.785: delivered locations[1] to <uid>/com.google.android.gms[fused_location_provider]/<id>
08-19 11:24:20.786: received GPS locations[1]
08-19 11:24:20.788: delivered locations[1] to <uid>/com.google.android.gms[fused_location_provider]/<id>
These deliveries continue indefinitely while the app is foregrounded, stop when the app is backgrounded, and resume on returning to foreground. After adding navigator.cleanup() to NavModule.cleanup(), they stop immediately when cleanup resolves.
Code Sample
Minimal reproduction — no destination, no guidance, no NavigationView needed:
import { View, Button } from 'react-native';
import { NavigationProvider, useNavigation } from '@googlemaps/react-native-navigation-sdk';
function Repro() {
const { navigationController } = useNavigation();
return (
<View style={{ flex: 1, justifyContent: 'center', gap: 20, padding: 40 }}>
<Button title="INIT + GUIDANCE" onPress={async () => {
await navigationController.init();
await navigationController.setDestinations([
{ title: 'Test', position: { lat: TU_LAT, lng: TU_LNG } },
]);
await navigationController.startGuidance();
console.log('[repro] guidance started');
}} />
<Button title="CLEANUP" onPress={async () => {
await navigationController.cleanup();
console.log('[repro] cleanup OK');
}} />
</View>
);
}
export default function ReproScreen() {
return (
<NavigationProvider termsAndConditionsDialogOptions={{ title: 'Repro' }}>
<Repro />
</NavigationProvider>
);
}
Press "init" (accept the Terms and Conditions dialog), then press "cleanup". Both resolve successfully, but the location request persists.
Additional Context
No response
Is there an existing issue for this?
Description of the bug
On Android,
NavModule.cleanup()removes listeners, stops guidance, clears destinations, and nulls out the module'smNavigatorreference, but never invokes the nativeNavigator#cleanup(). Since the Navigator is a singleton held byNavigationApi, dropping the module's reference doesn't destroy it: its internal location listener keeps running for the lifetime of the app process.Observable effect after
cleanup()resolves successfully from JS:adb shell dumpsys location).NavigationProvider.This causes unnecessary battery drain and signals location usage to the user after the navigation session has ended.
Expected behavior: after
cleanup()resolves, the Navigator's location request should be released and the location indicator should turn off.Google's instance cleanup best practices recommend calling
Navigator#cleanupwhen the navigation session is completed.Proposed fix: adding the missing call at the end of the teardown block in
NavModule.cleanup()resolves it. The method already captures a localfinal Navigator navigatorreference before nulling outmNavigator, so the call fits the existing pattern:Verified on a physical Android device: with this change the FLP request is released immediately when
cleanup()resolves and the location indicator turns off. Subsequentinit()→ set destination →startGuidance()→cleanup()cycles work correctly, including backgrounding/foregrounding between cycles.Possibly related history: #34 addressed guidance continuing after unmount, and #566 fixed navinfo forwarding surviving
cleanup(). This appears to be the same class of incomplete-teardown issue, but affecting the Navigator's own location provider. IfNavigator#cleanup()was omitted intentionally (e.g. to work around a known race condition on re-initialization), happy to hear the context.iOS Platform
Not verified
Android Platform
Affected
React Native version
0.83.10
React version
19.2.0
Package version
0.16.3
Native SDK versions
React Native Doctor Output
npx react-native doctorreports only environment-detection issues unrelatedto this bug (it doesn't find Android Studio/SDK paths on Fedora, though
ANDROID_HOME, adb, JDK and Gradle all resolve correctly and builds run fine).
Output of
npx expo-env-info:System:
OS: Linux (Fedora 43)
Node: 24.14.1
npm: 11.11.0
npmPackages:
expo: 55.0.28
expo-router: 55.0.17
react: 19.2.0
react-native: 0.83.10
npmGlobalPackages:
eas-cli: 21.4.0
Expo Workflow: bare (prebuild)
@googlemaps/react-native-navigation-sdk: 0.16.3
Test device: Android [16] ([moto g75 5G]), physical device
Steps to reproduce
NavigationProvider, callnavigationController.init()(accepting the Terms and Conditions dialog).navigationController.cleanup()— the promise resolves successfully.adb shell dumpsys locationand inspect the fused location provider section: a sustained ~1Hz GPS request is still being delivered, and the system location-in-use indicator remains visible while the app is in the foreground.NavigationProvider— the indicator stays on.Expected vs Actual Behavior
Expected: after
cleanup()resolves, the Navigator should be destroyed and its location request released — the fused location provider request should stop and the system location-in-use indicator should turn off.Actual:
cleanup()resolves withtrue, but the Navigator singleton is never destroyed (only the module's reference is nulled out). A ~1Hz GPS request stays registered with the fused location provider for the lifetime of the app process, keeping the location indicator on across the whole app whenever it is in the foreground.Logs: no errors or exceptions are thrown on either side —
cleanup()resolves successfully and logcat shows nothing unusual, which is what makes this hard to notice. The only visible evidence is inadb shell dumpsys location, where the GPS provider keeps delivering to the fused location provider at ~1Hz after cleanup:These deliveries continue indefinitely while the app is foregrounded, stop when the app is backgrounded, and resume on returning to foreground. After adding
navigator.cleanup()toNavModule.cleanup(), they stop immediately when cleanup resolves.Code Sample
Minimal reproduction — no destination, no guidance, no NavigationView needed:
Press "init" (accept the Terms and Conditions dialog), then press "cleanup". Both resolve successfully, but the location request persists.
Additional Context
No response