
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 : Android・JetpackCompose・Kotlin・Recommended・Review