@@ -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