From eb6150260b622346ee21467cf77d31401dfb3431 Mon Sep 17 00:00:00 2001 From: Ashin Walpola Date: Thu, 20 Aug 2026 16:31:41 +0200 Subject: [PATCH] Fix NPE crash on the V2X map when messages arrive during teardown osmdroid's MapView.onDetach() permanently tears the view down: afterwards its MapViewRepository holds a null MapView, so constructing a Marker against it throws NullPointerException from inside InfoWindow's constructor. It was being called from a DisposableEffect keyed on the lifecycle owner, which disposes independently of the AndroidView that owns the map. The update block could therefore still run against an already-detached MapView and rebuild its markers: java.lang.NullPointerException: Attempt to invoke virtual method 'MapViewRepository MapView.getRepository()' on a null object reference at org.osmdroid.views.overlay.Marker.(Marker.java:116) at V2xLiveMapViewKt...(V2xLiveMapView.kt:119) The crash is dated 2026-08-17 18:19, ten minutes after DENM reception went live on the device. The defect was always there, but every incoming message recomposes this view, so going from occasional updates to one per second made the window easy to land in - and SPATEM at ~2 Hz makes it easier still. Moves the teardown to AndroidView's onRelease, which is the callback that means "this View is gone" and after which Compose guarantees no further update. --- .../micr0bu/ui/screens/V2xLiveMapView.kt | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) 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(), ) }