Skip to content

Commit b9db1ff

Browse files
committed
fix: resolve race condition on Android in location listener causing NullPointerException
1 parent 72069ca commit b9db1ff

File tree

1 file changed

+22
-8
lines changed

1 file changed

+22
-8
lines changed

android/src/main/java/com/google/android/react/navsdk/NavModule.java

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ public class NavModule extends NativeNavModuleSpec
7474
private NavViewManager mNavViewManager;
7575
private final CopyOnWriteArrayList<NavigationReadyListener> mNavigationReadyListeners =
7676
new CopyOnWriteArrayList<>();
77-
private boolean mIsListeningRoadSnappedLocation = false;
77+
private volatile boolean mIsListeningRoadSnappedLocation = false;
7878
private LocationListener mLocationListener;
7979
private Navigator.ArrivalListener mArrivalListener;
8080
private Navigator.RouteChangedListener mRouteChangedListener;
@@ -167,8 +167,6 @@ public void cleanup(final Promise promise) {
167167
}
168168

169169
mIsListeningRoadSnappedLocation = false;
170-
removeLocationListener();
171-
removeNavigationListeners();
172170
mWaypoints.clear();
173171

174172
for (NavigationReadyListener listener : mNavigationReadyListeners) {
@@ -178,6 +176,11 @@ public void cleanup(final Promise promise) {
178176
final Navigator navigator = mNavigator;
179177
UiThreadUtil.runOnUiThread(
180178
() -> {
179+
// Remove listeners on UI thread to serialize with callback dispatch.
180+
// This reduces the chance of triggering a race condition in the Navigation SDK
181+
// where callbacks may still be in-flight during removal.
182+
removeLocationListener();
183+
removeNavigationListeners();
181184
navigator.clearDestinations();
182185
navigator.stopGuidance();
183186
navigator.getSimulator().unsetUserLocation();
@@ -933,20 +936,31 @@ public void resetTermsAccepted(final Promise promise) {
933936

934937
@Override
935938
public void startUpdatingLocation(final Promise promise) {
936-
registerLocationListener();
937939
mIsListeningRoadSnappedLocation = true;
938-
promise.resolve(null);
940+
// Register listener on UI thread to serialize with callback dispatch and allow
941+
// safe remove-and-recreate.
942+
UiThreadUtil.runOnUiThread(
943+
() -> {
944+
registerLocationListener();
945+
promise.resolve(null);
946+
});
939947
}
940948

941949
@Override
942950
public void stopUpdatingLocation(final Promise promise) {
943951
mIsListeningRoadSnappedLocation = false;
944-
removeLocationListener();
945-
promise.resolve(null);
952+
// Remove the listener on UI thread to serialize with callback dispatch.
953+
// This avoids the race condition in the Navigation SDK.
954+
UiThreadUtil.runOnUiThread(
955+
() -> {
956+
removeLocationListener();
957+
promise.resolve(null);
958+
});
946959
}
947960

948961
private void registerLocationListener() {
949-
// Unregister existing location listener if available.
962+
// Remove existing listener first, then recreate. This is safe when called
963+
// from UI thread as it serializes with callback dispatch.
950964
removeLocationListener();
951965

952966
if (mRoadSnappedLocationProvider != null) {

0 commit comments

Comments
 (0)