Repository navigation
Release the map view and detach the controller when MapViewBase leaves - #12
Merged
Merged
Conversation
MapViewBase destroyed the controller on dispose but nobody destroyed the SDK's map view. Its render thread kept the view, and through the camera listeners the old controller with every overlay, so each provider switch left the previous map in memory until the app ran out of it. MapViewBase now takes onDestroyMapView. On dispose it destroys the controller, detaches the view and hands it to the provider once. This also runs for a configuration change: the old Activity's view is never shown again, and what survives a rotation is the saved state. A provider that does not pass it gets a warning in the log instead of a silent leak. The previous signature stays as a hidden overload so code compiled against it still links (same JVM descriptor; the .api line differs only in the erased generic signature).
Some SDKs keep what a map view holds -- lifecycleScope coroutines, observers, component callbacks -- until the lifecycle they were given (or found in the view tree) is destroyed. With the Activity's, a map the user switched away from stays until the Activity goes. MapViewLifecycleOwner follows the host lifecycle and can be ended on its own from onDestroyMapView.
MapViewBase now hands the controller it destroyed back to the state (MapViewState.detachController), so a state the app keeps no longer holds the old map's controller and overlays. Done here for every provider; ArcGIS did it on its own before. The hidden overload without onDestroyMapView is removed: code compiled against it has to be rebuilt (binary break, accepted).
…tions A cycle through every provider (Boston parcels) still kept two old maps: - ArcGIS 2D: the example hands the same MapViewBase a second state for a frame (ArcGIS 3D and 2D share a state type), the provider attached the controller to that one, and dispose detached it from the first. MapViewBase now remembers the state current when the controller was made and asks it, the latest and the first to let go (each only if it is still attached). - Mapbox: the VectorStyleMutationSupport store left in the state's registry holds the map's holder and controller. MapViewBase removes it with the other two capabilities it already took down.
This was referenced Nov 8, 2026
Merged
A change that adds API to core and uses it here is opened as a pair of PRs on branches of the same name. CI always built core's default branch, so the compose PR failed until core was merged (fix/mapview-teardown: detachController(controller) not found). Use core's same-named branch when it exists; otherwise the default branch, as before.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
なぜ
MapViewBaseは、コンポジションから外れるときに controller のdestroy()を呼びますが、SDK の地図ビュー(MapView など)は誰も破棄していませんでした。多くの SDK では描画スレッドやリスナが地図ビューを持ち続け、そこからカメラのリスナ経由で古いコントローラとオーバーレイがすべて残ります。そのためプロバイダを切り替えるたびに前の地図がまるごとメモリに残り、区画データでは数回で OutOfMemoryError になっていました。何を変えたか
MapViewBaseにonDestroyMapView: ((ActualMapView) -> Unit)?を足しました。外れるときに、controller を破棄し、地図ビューを親から外して、プロバイダに 1 回だけ渡します。MapViewLifecycleOwnerを追加しました。ホストのライフサイクルに従いつつ、onDestroyMapViewから単独で終わらせられます。lifecycleScope のコルーチン、observer、ComponentCallbacks などを「渡されたライフサイクルが終わるまで」持ち続ける SDK(ArcGIS / Mapbox)のために使います。MapViewState.detachController)。外すよう頼む相手は、controller を作った時点の state、最新の state、最初の state の 3 つです。どれも、その controller がまだ接続中のときだけ外れます。MapViewBaseに 1 フレームだけ別の state が渡り、片方に残っていたためです。VectorStyleMutationSupportも registry から取り除きます。Mapbox ではこれが holder とコントローラを持っていました。互換性
onDestroyMapViewの無い旧シグネチャのMapViewBaseは削除しました。これに対してコンパイルされたコードは再ビルドが必要です(バイナリ互換の破壊。了承済み)。MapViewBaseを直接呼ぶのはプロバイダだけで、この PR 一式で全プロバイダを新しいシグネチャに移しています。関連 PR(同じ修正の一式)
fix/mapview-teardownは次のリポジトリにまたがります。core → compose → 各プロバイダの順にマージしてください。プロバイダの変更は、core のMapViewState.detachControllerと、compose のMapViewBase(onDestroyMapView = …)/MapViewLifecycleOwnerを使います。検証
タブレット(HA26Z6JL)の example-app で確認しました。区画データ(約 10 万ポリゴン)はポリゴンのタイル描画が要るので、検証には、このブランチのコミットを
feat/polygon-tiling/feat/overlay-on-addedの上に一時的に cherry-pick したブランチ(test/teardown-on-tiling、push していません)を使っています。am dumpheap -gで GC させて、GC 後のヒープを測りました。MapLibreViewController/GoogleMapViewControllerが残り、それぞれ 98,843 件の PolygonEntity を抱えていました。testDebugUnitTestとapiCheckを流しました。新しく足したテストはすべて通っています。失敗は次の 5 つで、どれも develop の時点で同じ理由で落ちています。確認できていないもの: