From 7d0f555414d46176f9cc8d5829cb7113c7aa51e6 Mon Sep 17 00:00:00 2001 From: Masashi Katsumata Date: Sat, 7 Nov 2026 13:16:50 -0800 Subject: [PATCH 1/2] Destroy the MapView when the map leaves the composition Nothing destroyed the MapView. Its render thread kept it, and through the camera-idle listener the old controller with every overlay, so each provider switch added a whole map to the heap. It is now destroyed through MapViewBase's onDestroyMapView. It is not stopped first: it was never started, and MapView.onStop() would deactivate the shared FileSource. --- src/main/java/com/mapconductor/maplibre/MapLibreMapView.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/com/mapconductor/maplibre/MapLibreMapView.kt b/src/main/java/com/mapconductor/maplibre/MapLibreMapView.kt index 89af5ea..3e1e3db 100644 --- a/src/main/java/com/mapconductor/maplibre/MapLibreMapView.kt +++ b/src/main/java/com/mapconductor/maplibre/MapLibreMapView.kt @@ -202,6 +202,11 @@ fun MapLibreMapView( // For now, assuming content relates to overlay definitions. style = style, onStyleDiagnostics = onStyleDiagnostics, + // Never started (onStart is not called), so it is not stopped either -- + // MapView.onStop() would deactivate the shared FileSource the next map + // still uses. Destroying it ends the render thread, whose camera + // listeners otherwise keep the controller and every overlay alive. + onDestroyMapView = { mapView -> mapView.onDestroy() }, content = content, // This might need adjustment based on how overlays are handled ) } From 4b667f74a166dbd01161995f8ed698e285e89b9d Mon Sep 17 00:00:00 2001 From: Masashi Katsumata Date: Sat, 7 Nov 2026 16:20:26 -0800 Subject: [PATCH 2/2] Read the controller through the state base, which MapViewBase clears The state kept its own controller field, set on attach and never cleared, so a state the app keeps held the destroyed controller and every overlay of the old map. It now reads the base's attached controller, which MapViewBase detaches when the map is torn down. The polygon conductor's destroy() clears its polygon and outline managers, as core's PolygonController now does. --- .../java/com/mapconductor/maplibre/MapLibreViewStateImpl.kt | 6 ++++-- .../maplibre/polygon/MapLibrePolygonConductor.kt | 5 ++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/mapconductor/maplibre/MapLibreViewStateImpl.kt b/src/main/java/com/mapconductor/maplibre/MapLibreViewStateImpl.kt index 93a0d95..1485310 100644 --- a/src/main/java/com/mapconductor/maplibre/MapLibreViewStateImpl.kt +++ b/src/main/java/com/mapconductor/maplibre/MapLibreViewStateImpl.kt @@ -21,7 +21,10 @@ class MapLibreViewState( cameraPosition: MapCameraPosition = MapCameraPosition.Default, ) : MapViewState(cameraPosition), MapLibreViewStateInterface { - private var controller: MapLibreViewControllerInterface? = null + // Read through the base so that letting go of the controller there (MapViewBase + // does it when the map is torn down) leaves nothing behind here. + private val controller: MapLibreViewControllerInterface? + get() = attachedMapController as? MapLibreViewControllerInterface private var _mapDesignType: MapLibreMapDesignTypeInterface = mapDesignType override var mapDesignType: MapLibreMapDesignTypeInterface @@ -32,7 +35,6 @@ class MapLibreViewState( get() = _mapDesignType internal fun setController(controller: MapLibreViewControllerInterface) { - this.controller = controller attachController(controller) } diff --git a/src/main/java/com/mapconductor/maplibre/polygon/MapLibrePolygonConductor.kt b/src/main/java/com/mapconductor/maplibre/polygon/MapLibrePolygonConductor.kt index f0150eb..096d277 100644 --- a/src/main/java/com/mapconductor/maplibre/polygon/MapLibrePolygonConductor.kt +++ b/src/main/java/com/mapconductor/maplibre/polygon/MapLibrePolygonConductor.kt @@ -130,7 +130,10 @@ class MapLibrePolygonConductor( } override fun destroy() { - // No native resources to clean up for polygons + // A destroyed conductor can still be referenced (by its map controller, + // or a state that has not let go yet); without this it keeps every entity. + polygonOverlay.polygonManager.clear() + polylineOverlay.polylineManager.clear() } }