diff --git a/app/src/main/java/com/hawhamburg/micr0bu/ui/screens/V2xLiveMapView.kt b/app/src/main/java/com/hawhamburg/micr0bu/ui/screens/V2xLiveMapView.kt index 91e3630..305d34f 100644 --- a/app/src/main/java/com/hawhamburg/micr0bu/ui/screens/V2xLiveMapView.kt +++ b/app/src/main/java/com/hawhamburg/micr0bu/ui/screens/V2xLiveMapView.kt @@ -85,7 +85,7 @@ fun V2xLiveMapView( lifecycleOwner.lifecycle.addObserver(observer) onDispose { lifecycleOwner.lifecycle.removeObserver(observer) - mapViewRef.value?.onDetach() + // onDetach() deliberately NOT called here - see AndroidView's onRelease below. } } @@ -173,6 +173,20 @@ fun V2xLiveMapView( mv.controller.animateTo(ownGeoPoint) mv.invalidate() }, + // osmdroid's onDetach() permanently tears the MapView down: afterwards its + // MapViewRepository holds a null MapView, so constructing a Marker against it throws + // NullPointerException from deep inside InfoWindow's constructor. + // + // This used to run in the DisposableEffect's onDispose, which is NOT safe: that effect + // is keyed on the lifecycle owner and disposes independently of this AndroidView, so + // the update block above could still run against an already-detached MapView and + // rebuild its markers. It crashed the app on 2026-08-17 once DENMs started arriving, + // because every incoming message recomposes this view and there are far more updates + // to land in that window than there used to be. + // + // onRelease is the callback that actually means "this View is gone": Compose + // guarantees no further update after it. + onRelease = { it.onDetach() }, modifier = Modifier.fillMaxSize(), ) }