Androidアプリでは、入力画面から確認画面へ移動したり、複数のFragmentやComposableで同じデータを表示したりするなど、画面をまたいで状態を扱う場面がよくあります。こうした処理で重要になるのが、データをどこに保持し、画面のライフサイクル変化にどう対応するかという設計です。
Android JetpackのViewModelを利用すると、UIの状態や画面で必要なデータをUIコンポーネントから分離して管理しやすくなります。さらにSavedStateHandleを組み合わせれば、プロセス終了後の状態復元を考慮した設計も可能です。
ただし、ViewModelとSavedStateHandleは万能なデータ保存場所ではありません。それぞれが解決する問題と寿命を理解し、Navigationの引数、データベース、DataStoreなどと適切に使い分けることが大切です。
ViewModelを使うメリットとは
ViewModelは、画面に必要な状態やロジックをActivity、Fragment、ComposableなどのUIから分離して保持するために利用されます。大きなメリットの一つは、構成変更によってUIが作り直されてもViewModelの状態を維持できることです。
例えば端末を回転すると、構成によってはActivityやFragmentが再生成されます。UIコンポーネントだけに入力途中のデータを保持していると、その値を適切に保存・復元しなければ失われる可能性があります。ViewModelへUI状態を持たせることで、このようなライフサイクルへの対応を整理しやすくなります。
さらに、UI側を「状態を表示し、ユーザー操作をViewModelへ伝える」という役割に寄せられるため、データ処理と画面表示の責務を分離しやすくなります。これはコードの保守性やテストのしやすさという点でもメリットがあります。
複数画面でViewModelを共有する仕組み
ViewModelは常にアプリ全体で共有されるわけではなく、どのViewModelStoreOwnerをスコープにするかによって寿命と共有範囲が決まります。例えば同じActivityに属する複数FragmentでActivityを所有者としたViewModelを利用すれば、そのFragment間で同じViewModelインスタンスを参照できます。
Navigationを利用している場合には、ナビゲーショングラフなど適切な範囲にViewModelをスコープする設計もできます。これにより、特定の一連の画面だけで状態を共有し、そのフローから離れたら不要な状態を破棄する、といった管理がしやすくなります。
例えば「商品入力→オプション選択→注文確認」という3画面を一つのフローとして扱う場合、フローに適したスコープのViewModelで商品名、数量、選択内容などを管理する方法が考えられます。各画面で大量の値を受け渡し続けるより、状態の管理場所を明確にできます。
SavedStateHandleを利用するメリット
ViewModelには重要な注意点があります。ViewModelは構成変更には耐えられますが、アプリのプロセスがOSによって終了した場合まで同じViewModelインスタンスが生き続けるわけではありません。この違いはAndroidの状態管理を理解するうえで非常に重要です。
SavedStateHandleは、ViewModelで扱う状態のうち、プロセス終了後にも復元したい小さなUI状態を保存・復元するために利用できます。ユーザーが別アプリへ移動している間にAndroidがアプリのプロセスを終了し、その後ユーザーが戻ってきた場合などへの対応に役立ちます。
例えば検索画面なら、入力した検索キーワードや選択中のフィルター条件などが候補になります。復元に必要な最低限の値をSavedStateHandleへ保持し、それを基に画面の状態を再構築するという設計ができます。
ViewModelとSavedStateHandleの違いを整理する
両者の役割を混同しないためには、どの状況に対応する仕組みなのかを整理すると分かりやすくなります。
| 項目 | ViewModel | SavedStateHandle |
|---|---|---|
| 主な目的 | UI状態・画面ロジックの管理 | 再生成に必要な状態の保存・復元 |
| 構成変更 | 同じスコープ内なら状態を維持しやすい | 対応可能 |
| プロセス終了後 | ViewModel自体は保持されない | 保存対象の状態を復元する用途に使える |
| 大量データの永続保存 | 不向き | 不向き |
| 代表的な用途 | 画面状態、処理結果、複数UI間の状態管理 | 検索語、ID、選択値など復元に必要な小さな状態 |
つまり、SavedStateHandleはViewModelの代替ではありません。ViewModelでUI状態を管理し、その中でもプロセス終了後に復元する必要がある情報をSavedStateHandleで扱うという考え方が基本になります。
画面間のデータ共有なら何でもViewModelに入れてよいわけではない
複数画面で値を使うからといって、すべて共有ViewModelへ格納する必要はありません。例えば「商品IDを詳細画面へ渡す」といった単純な画面遷移では、Navigationの引数としてIDを渡し、遷移先でそのIDから必要なデータを取得する方が責務を明確にできる場合があります。
一方、「複数画面を移動しながら一つのフォームを完成させる」といったケースでは、同じフローにスコープされたViewModelで状態を共有する方法が適しています。重要なのは、共有すること自体ではなく、そのデータの寿命とViewModelのスコープを一致させることです。
また、アプリを終了して後日再起動しても残しておきたいデータは、ViewModelやSavedStateHandleだけに依存すべきではありません。ユーザーデータとして永続的に保持する必要がある場合には、RoomなどのデータベースやDataStoreなど、目的に適した永続化手段を検討します。
実例:入力画面と確認画面でデータを共有する場合
例として、氏名とメールアドレスを入力する画面、その内容を確認する画面があるアプリを考えてみます。入力内容を一つのViewModelで管理し、両画面を同じ適切なスコープのViewModelへ接続すれば、確認画面ではViewModelの状態を参照して入力内容を表示できます。
さらに、入力途中にプロセスが終了しても復元したい場合には、氏名やメールアドレスなど復元に必要な値をSavedStateHandleで扱う方法があります。ただし個人情報などを保存する場合には、保存の必要性やセキュリティ、データの取り扱いも別途検討する必要があります。
この設計では、画面Aが画面Bを直接操作する必要がありません。各画面が共通の状態を適切なViewModelを通じて扱えるため、画面同士の依存関係を小さくできます。
よくある誤解:ViewModelならアプリを閉じてもデータが残る?
ViewModelについて特に注意したいのが、「ViewModelへ入れたデータはアプリを終了しても残る」という誤解です。ViewModelはデータベースのような永続ストレージではありません。
構成変更によるActivityなどの再生成と、Android OSによるプロセス終了は別の出来事です。ViewModelは前者への対応に有効ですが、後者ではViewModelそのものが失われる可能性があります。SavedStateHandleはその際に必要な状態を復元するための仕組みの一つです。
さらに、SavedStateHandleもデータベースの代用品ではありません。例えば大量の商品情報、画像、長期間保存するユーザーデータなどをSavedStateHandleへ詰め込む設計は避け、永続ストレージに保存したデータを識別するIDなど、復元に必要な小さな情報を持たせる方法を検討します。
状態管理では「データの寿命」から保存先を決める
Androidアプリの状態管理では、「複数画面で使うか」だけで保存場所を決めるより、そのデータをいつまで残す必要があるかを考えると設計しやすくなります。
画面を再生成しても維持したいUI状態ならViewModel、プロセス終了後の画面復元にも必要な小さな状態ならSavedStateHandle、アプリを完全に終了して後日利用するときにも必要なデータならデータベースやDataStore、といったように役割を分けます。
また、画面間で共有するViewModelのスコープを必要以上に広げないことも重要です。アプリ全体で共有する必要がない状態を広いスコープに置くと、古い状態が意図せず別画面に残るなど、かえって管理が複雑になる場合があります。
まとめ|ViewModelとSavedStateHandleは役割を分けて使う
Androidで複数画面にまたがる状態を管理する場合、ViewModelを利用するとUIと状態管理を分離しやすくなり、構成変更にも対応しやすくなります。また、適切なスコープを選択することで、複数のFragmentやComposableなどから同じ状態を利用できます。
SavedStateHandleは、そのViewModelで扱う状態のうち、プロセス終了後の復元にも必要となる小さな情報を扱う場合に有効です。「ViewModelはUI状態を管理する」「SavedStateHandleは復元に必要な状態を補助的に保存する」と整理すると理解しやすいでしょう。
最終的には、Navigationの引数、ViewModel、SavedStateHandle、Room、DataStoreなどから、データの用途と寿命に合った仕組みを選択することが重要です。単に複数画面からアクセスできることだけでなく、構成変更、プロセス終了、永続保存まで分けて考えることで、保守しやすく安定したAndroidアプリを設計できます。


コメント