Skip to content

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

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 を破棄していたのは Activity の本当の ON_DESTROY のときだけで、プロバイダを切り替えると MapView がコントローラと一緒に残っていました。

さらに Mapbox のライフサイクルプラグインは、Activity に ComponentCallbacks を登録し、ビューツリーのライフサイクルを observe します。放すのはそのライフサイクルが destroy されたときだけで、MapView.onDestroy() では外れません。

何を変えたか

  • MapView を、MapViewBase の onDestroyMapView から 1 回だけ stop して破棄します。
  • MapView に専用の MapViewLifecycleOwner を渡し、onDestroyMapView で終わらせます。プラグインはそれを見て MapView を止めて破棄し、登録したものを外します。
  • state が自分で持っていた controller フィールド(接続時に入れて二度と消さない)をやめ、基底の接続中のコントローラを読むようにしました。MapViewBase が地図を破棄するときに外すので、アプリが持ち続ける state に古いコントローラとオーバーレイが残りません。
  • ポリゴン conductor の destroy() で、ポリゴンと輪郭の manager を clear するようにしました(core の PolygonController と同じ)。

別の PR との関係

#20(refactor/polygon-controller)は MapboxPolygonConductor を削除して core の PolygonController に置き換えます。そちらが先にマージされた場合、この PR の conductor の変更は不要になります(core 側の destroy() が同じことをします)。後にマージする側で衝突の手当てが要ります。

関連 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 に対応していません(カメラの保持は確認済み)。

Only the Activity's real ON_DESTROY destroyed the MapView, so a provider
switch left it alive with the controller it reaches. It is now stopped
and destroyed through MapViewBase's onDestroyMapView, once.
Mapbox's lifecycle plugin registers component callbacks on the Activity and
observes the view tree's lifecycle, and lets go only when that lifecycle
is destroyed; MapView.onDestroy() does not undo it. Under the Activity's
lifecycle every MapView the user switched away from (and through its click
listeners the controller) stayed until the Activity went. The MapView now
gets a MapViewLifecycleOwner, ended in onDestroyMapView: the plugin then
stops and destroys it and unregisters what it registered.
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.
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