Jetpack Compose Navigation 3: ViewModel の生存期間を NavEntry 単位に変更する

Jetpack Navigation 3は、従来の Navigation Component と比較して非常にシンプルで「Action based」な設計になっています。しかし、デフォルト設定のまま ViewModel を利用すると、その生存期間に驚くかもしれません。

今回は、rememberViewModelStoreNavEntryDecorator() を使って NavEntry(画面)の破棄と同時に ViewModel をクリアする設定 について解説します。

 

🧑🏻‍💻 ViewModel のオーナーと生存期間

Nav3 で viewModel() を呼び出した際、そのオーナー(ViewModelStoreOwner)がどこにあるかを確認することは非常に重要です。

デフォルトの挙動:Activity スコープ

特に設定を行わない場合、ViewModel のオーナーは Activity (または Fragment) になります。

現状:
スクリーンが NavBackStack から消えても、ViewModel は破棄されません。

影響:
1 Activity で構成されたフル Jetpack Compose アプリの場合、一度生成された ViewModel はアプリが終了するまでメモリ上に生き続けます。

これは、画面を戻る(Pop back)たびに状態をリセットしたい場合には不都合な挙動となります。

 

🧑🏻‍💻 entryDecorators に ememberViewModelStoreNavEntryDecorator() を追加する

画面(NavEntry)ごとに独立した ViewModelStore を持たせるためのデコレータです。これを利用することで、NavEntry がスタックに積まれている間だけ ViewModel が生存するようになります。

設定方法:entryDecorators への追加

NavDisplay を呼び出す際、entryDecorators 引数にこのデコレータを渡します。


val backStack = rememberNavBackStack(initialBackStack)

NavDisplay(
    backStack = backStack,
    entryDecorators = listOf(
        rememberSaveableStateHolderNavEntryDecorator(),

        // これを追加することで NavEntry 単位の ViewModel スコープが有効になる
        rememberViewModelStoreNavEntryDecorator()
    )
) { entry ->
    // 各画面の Composable 呼び出し
    when (val key = entry.contentKey) {
        is ScreenA -> ScreenAContent()
        is ScreenB -> ScreenBContent()
    }
}

ViewModel の生存期間はどう変わるのか?

このデコレータを適用すると、内部的な構造は以下のように変化します。

適用前:
全ての NavEntry が Activity の ViewModelStore を参照。

適用後:
各 NavEntry が個別の ViewModelStore を保持。

これにより、NavBackStack から特定の NavEntry が取り除かれた(Pop された)タイミングで、紐づく ViewModelStore が clear() され、ViewModel も正しく破棄されるようになります。

 

🧑🏻‍💻 まとめ

Jetpack Navigation 3 で ViewModel を適切に管理するためのポイントは以下の通りです。

1. デフォルトでは Activity スコープ になり、画面を閉じても ViewModel は生き続ける。

2. rememberViewModelStoreNavEntryDecorator() を NavDisplay の entryDecorators に追加する。

3. これにより、Screen(NavEntry)の生存期間 = ViewModel の生存期間 となり、メモリ効率と状態管理の健全性が向上する。

Navigation 3 において、この設定は現代的な Android アプリ開発のスタンダードな構成と言えるでしょう。


Related Categories :  AndroidJetpackComposeKotlinRecommendedReview