Skip to content

Release the map view and the controller when the map leaves the composition - #23

Merged
wf9a5m75 merged 3 commits into
developfrom
fix/mapview-teardown
Nov 8, 2026
Merged

wf9a5m75 merged 3 commits into
developfrom
fix/mapview-teardown

Conversation

@wf9a5m75

@wf9a5m75 wf9a5m75 commented Nov 8, 2026 •

Copy link
Copy Markdown
Contributor

なぜ

MapView を破棄していたのは設定変更のときだけで、プロバイダを切り替えると MapView が生きたまま残り、SDK のスレッド経由でコントローラとオーバーレイがすべて残っていました。

加えて、Google Maps SDK はタイルオーバーレイの TileProvider を、オーバーレイを外して MapView を破棄した後もプロセス全体の表に持ち続けます(実測)。こちらの TileProvider はレンダラ、holder、MapView に届くので、切り替えた Google の地図はすべて残っていました。

何を変えたか

  • MapView を、MapViewBase の onDestroyMapView から 1 回だけ破棄します。理由を問わず、地図が外れたときです。ライフサイクル observer は onResume / onPause だけを転送します。
  • SDK には、転送だけをする DetachableTileProvider を渡します。オーバーレイを外したとき、差し替えたとき、コントローラを破棄したときに本物から切り離します。コントローラの破棄では、追加したオーバーレイをすべて外し、ローカルのタイルキューを閉じてから MapView を破棄します。
  • state が自分で持っていた controller フィールド(接続時に入れて二度と消さない)をやめ、基底の接続中のコントローラを読むようにしました。MapViewBase が地図を破棄するときに外すので、アプリが持ち続ける state に古いコントローラとオーバーレイが残りません。

残るもの

Google の地図を 1 回作るごとに、SDK のタイル用スレッド 3 本と Executor が増え続けます(SDK 側の問題で、こちらでは止められません)。1 回あたり数 KB 程度で、PSS では増加が見えませんでした。こちらの DetachableTileProvider(20 B)も 1 個ずつこのプールに握られます。スレッドに残っていた作業用バッファは android-sdk-core の ScratchPool で解消しています。

関連 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 していません)を使っています。

  • 地図の切り替え(Boston の City parcels、MapLibre ⇄ Google): 毎回 am dumpheap -g で GC させて、GC 後のヒープを測りました。
    • 修正前: 292 → 489 MB と増え続け、5 回目の切り替えで OutOfMemoryError(512 MB)で落ちました。ヒープダンプでは、切り替え後も古い MapLibreViewController / GoogleMapViewController が残り、それぞれ 98,843 件の PolygonEntity を抱えていました。
    • 修正後: 24 回切り替えても、GC 後のヒープは 307〜309 MB で一定でした。Native alloc も増えていません。
  • 全プロバイダの巡回(11 種類、City parcels): 巡回後のヒープダンプに残った地図コントローラは、表示中の 1 個だけでした。PolygonEntity も表示中の地図の 98,843 件だけです。FATAL は 0 件でした。
  • 回転(横 → 縦 → 横): map-design ページでデフォルト以外のデザインを選んでから回しました。Google / HERE / Mapbox / ArcGIS 3D / ArcGIS 2D / MapLibre / TomTom / MapTiler / Longdo で、デザインとカメラが保たれ、プロセスも落ちませんでした。
  • 単体テスト・apiCheck: develop を起点に 12 モジュールで testDebugUnitTest と apiCheck を流しました。新しく足したテストはすべて通っています。失敗は次の 5 つで、どれも develop の時点で同じ理由で落ちています。
    • apiCheck: arcgis / maplibre / mappls / openmobilemaps
    • openmobilemaps の conformance テスト 1 件

確認できていないもの:

  • Mappls の回転: 検証用のビルドでは Mappls の認証設定が無く、初期化に失敗していました。
  • OpenMobileMaps の回転時のデザイン保持: example のデザイン選択 UI が OMM の state に対応していません(カメラの保持は確認済み)。

The MapView was destroyed only on a configuration change; a provider
switch left it alive, and the SDK thread behind it kept the controller
with every overlay. It is now released through MapViewBase's
onDestroyMapView, once, whatever the reason the map went away. The
lifecycle observer keeps forwarding onResume / onPause only.
The SDK keeps the TileProvider of every tile overlay in a process-wide
table, even after the overlay is removed and the MapView destroyed
(measured). Our provider reached the renderer, its holder and the MapView,
so every Google map the user switched away from stayed in memory.

The SDK now gets a provider that only forwards; it is cut from the real
one when its overlay is removed, retired or the controller destroyed.
Destroying the controller also removes every overlay it added and closes
the local tile queues, before the MapView is destroyed.
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.
@wf9a5m75
wf9a5m75 merged commit b3b13a8 into develop Nov 8, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant