Skip to content

Commit 11f79ae

Browse files
authored
fix: resolve race condition in location listener causing NullPointerException (#569)
1 parent 92cecf8 commit 11f79ae

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();
@@ -939,20 +942,31 @@ public void resetTermsAccepted(final Promise promise) {
939942

940943
@Override
941944
public void startUpdatingLocation(final Promise promise) {
942-
registerLocationListener();
943945
mIsListeningRoadSnappedLocation = true;
944-
promise.resolve(null);
946+
// Register listener on UI thread to serialize with callback dispatch and allow
947+
// safe remove-and-recreate.
948+
UiThreadUtil.runOnUiThread(
949+
() -> {
950+
registerLocationListener();
951+
promise.resolve(null);
952+
});
945953
}
946954

947955
@Override
948956
public void stopUpdatingLocation(final Promise promise) {
949957
mIsListeningRoadSnappedLocation = false;
950-
removeLocationListener();
951-
promise.resolve(null);
958+
// Remove the listener on UI thread to serialize with callback dispatch.
959+
// This avoids the race condition in the Navigation SDK.
960+
UiThreadUtil.runOnUiThread(
961+
() -> {
962+
removeLocationListener();
963+
promise.resolve(null);
964+
});
952965
}
953966

954967
private void registerLocationListener() {
955-
// Unregister existing location listener if available.
968+
// Remove existing listener first, then recreate. This is safe when called
969+
// from UI thread as it serializes with callback dispatch.
956970
removeLocationListener();
957971

958972
if (mRoadSnappedLocationProvider != null) {

0 commit comments

Comments
 (0)