ステートリストレーション(State Restoration)は、アプリケーションの再起動または最小化後にユーザーインターフェースを保存および復元できるモバイルオペレーティングシステムのメカニズムです。システムはUIの状態をメモリまたは永続ストレージに保存し、再度開いたときに復元します。Android Developers(2025)によると、State Restorationは高品質なユーザーエクスペリエンスを目指すアプリケーションに必須です。State Restorationは、アプリケーションが予期せず終了した際のデータ損失を防ぐために重要です。
重要なポイント
State Restoration(ステートリストレーション)は、アプリケーションのユーザーインターフェースの現在の状態を保存し、終了後または再起動後に復元できるシステムメカニズムです。ユーザーがアプリケーションを最小化したり、システムがリソースを解放するためにアプリケーションを閉じたりすると、State Restorationは主要なUIパラメータをキャプチャして暗号化ストレージに保存します。
State Restorationがないと、ユーザーはアプリケーションを切り替える際にすべての未保存データを失います。例えば、記入済みのフィードバックフォーム、長い検索クエリ、または部分的に表示されたニュースリストなど、これらはすべて再起動時に消えてしまいます。State Restorationは、最小化の瞬間にViewControllerまたはActivityの状態を自動的にキャプチャすることでこの問題を解決します。
このメカニズムはシステムレベルで動作し、両方の主要モバイルプラットフォームでサポートされています。iOSはNSUserActivityとUIStateRestoringプロトコルを介してState Restorationを提供し、AndroidはJetpackアーキテクチャコンポーネントとViewModel内のSavedStateHandleを介して提供します。実装は異なりますが、概念は同じです。
State Restorationのプロセスは、保存と復元の2つのフェーズに分かれています。保存フェーズでは、システムが対応するライフサイクルメソッドを呼び出し、アプリケーションは現在のUI状態をコンパクトな表現にシリアライズする必要があります。復元フェーズでは、システムが保存されたデータを返し、アプリケーションがそれをデシリアライズしてUIを復元します。
保存は、アプリケーションがバックグラウンドに移動したとき、または終了が差し迫った信号を受信したときにシステムによって開始されます。iOSではUIViewControllerのencodeRestorableStateメソッドが呼び出され、AndroidではActivityのonSaveInstanceStateまたはSavedStateHandleを介した保存が行われます。データはプリミティブ型(文字列、数値、バイト配列、Parcelableオブジェクト)をサポートする形式にシリアライズされます。
保存されるデータ量は最小限にする必要があります。システムは保存状態バンドルのサイズに制限を課します。Androidでは、制限はプロセスあたり約50 KBです。制限を超えるとTransactionTooLargeException例外が発生します。したがって、アーキテクトは識別子とキーのみを保存し、復元時に永続ストレージから完全なデータをロードすることを推奨します。
復元時には、システムは起動時に保存されたデータバンドルをアプリケーションに渡します。iOSではdecodeRestorableStateメソッドが呼び出され、AndroidではonRestoreInstanceStateまたはSavedStateHandleからの読み取りが行われます。アプリケーションはバンドルから識別子とキーを抽出し、UI(スクロール位置、選択されたアイテム、入力されたデータ)を復元します。
復元は新しいプロセスで発生する可能性があることに注意することが重要です。アプリケーションがメモリから完全にアンロードされた場合、プロセスは新たに作成され、メモリ内のすべてのオブジェクトは存在しません。したがって、状態はシリアライズ可能で、前のセッションのランタイムコンテキストから独立している必要があります。これは、複数の入力フィールドを持つ大きなフォームや長いマルチページインターフェースで特に重要です。
class MainActivity : AppCompatActivity() {
private var searchQuery: String = ""
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putString("search_query", searchQuery)
}
override fun onRestoreInstanceState(savedState: Bundle) {
super.onRestoreInstanceState(savedState)
searchQuery = savedState.getString("search_query", "")!!
restoreSearchUI(searchQuery)
}
}
State Restorationの実装はプラットフォームによって大きく異なります。iOSはストーリーボードとUIKitプロトコルを介した宣言的アプローチを使用し、AndroidはActivityライフサイクルメソッドとJetpackアーキテクチャコンポーネントを介した命令的アプローチを使用します。アプローチの選択は、ターゲットプラットフォームとアプリケーションアーキテクチャに依存します。
iOSでは、State Restorationは3つのコンポーネントで構成されています。UIApplicationが全体的なプロセスを管理し、UIViewControllerがUIStateRestoringプロトコルを実装し、NSUserActivityがナビゲーション復元用のデータを保存します。有効にするには、UIViewControllerにrestorationIdentifierを設定し、encodeRestorableStateとdecodeRestorableStateを実装する必要があります。
iOSは、restorationIdentifierが設定されている場合、ナビゲーションコントローラー(UINavigationController)とすべてのネストされたViewControllerの状態を自動的に保存します。システムはナビゲーションスタックを管理し、元の状態に復元します。ただし、コントローラー内のデータ(入力されたテキスト、スクロール位置)は開発者が明示的に保存する必要があります。
Androidでは、State Restorationの最新のアプローチはSavedStateHandle(AndroidX Lifecycleライブラリのコンポーネント)に基づいています。SavedStateHandleはViewModel内でアクセス可能で、設定変更(画面回転)およびプロセス再起動時にデータを自動的に保存および復元します。データはBundleに保存され、自動的にシリアライズされます。
SavedStateHandleはLiveDataをサポートするキー-バリューストアのように動作します。設定変更時、データは自動的に保存および復元されます。プロセス再起動をサポートするには、ViewModelをSavedStateViewModelFactoryを介して作成する必要があります。これにより、ViewModelはアプリケーションの完全な終了後も存続できます。
class SearchViewModel(
private val savedStateHandle: SavedStateHandle
) : ViewModel() {
companion object {
private val KEY_QUERY = string("search_query")
}
fun getSearchQuery(): String? = savedStateHandle[KEY_QUERY]
fun saveSearchQuery(query: String) {
savedStateHandle[KEY_QUERY] = query
}
}
State Restorationの実践的な実装には、適切なストレージの選択、保存するデータ量の決定、さまざまな終了シナリオのテストなど、いくつかの側面を考慮する必要があります。Flutterアプリケーションでstate_restorationパッケージを使用した段階的な実装を見てみましょう。
class RestorableSearchField extends RestorableProperty<String> {
String _value = '';
@override
String get value => _value;
@override
void set value(String newValue) {
if (_value != newValue) {
_value = newValue;
notifyListeners();
}
}
@override
String? toPrimitives() => _value;
@override
void fromPrimitives(String? data) {
_value = data ?? '';
}
}
実装時には、保存の境界を覚えておくことが重要です。すべてのUIフィールドを復元する必要はありません。長いリストのスクロール位置は保存すべきです。一時的なアニメーション状態は保存する必要がありません。開発者は、どのデータがユーザーエクスペリエンスにとって重要で、どのデータが利便性を損なうことなく安全にリセットできるかを意識的に選択する必要があります。
State Restorationのテストは、プロセス終了のシミュレーションを必要とする独立したタスクです。Androidでは、adb shell am killコマンドで実行できます。iOSでは、Xcodeでの終了シミュレーションで実行できます。EspressoやXCTestなどのUIテストフレームワークは、状態復元を検証するための特別なメソッドを提供します。
第一のルール — データではなく識別子を保存する。100のフィールドを持つ完全なオブジェクトを保存する代わりに、その一意の識別子を保存し、復元時にデータベースまたはAPIから実際のデータをロードします。これによりBundleのスペースを節約し、復元時のデータの新鮮さを保証します。
第二のルール — すべてのシナリオをテストする。画面回転後、最小化して1時間後に戻った後、メモリ不足によるシステムによるアプリケーション終了後の復元を確認します。各シナリオは、オペレーティングシステムの状態と利用可能なリソースに応じて異なる動作をする可能性があります。
第三のルール — カスタムではなくシステムメカニズムを使用する。iOSとAndroidは、それぞれのプラットフォームに最適化されたState Restoration用の組み込みAPIを提供します。SharedPreferencesやUserDefaultsを介したカスタム実装は、同期の問題や復元時の予期しない動作を引き起こす可能性があります。
第四のルール — 状態の欠如を処理する。初回起動時またはデータクリア後、状態が存在しない場合があります。UIは例外をスローせずに初期状態で正しく動作する必要があります。使用前にすべての保存データをnullチェックし、デフォルト値を提供してください。
第五のルール — 保存されたキーを文書化する。プロジェクトに数十の画面があり、それぞれが複数のフィールドを保存する場合、集中管理されたキー管理なしでは混乱が生じます。各モジュールにState Restoration用のキー定数を持つ単一のクラスまたはファイルを作成します。これによりメンテナンスが簡素化され、リファクタリング中の偶発的なデータ上書きが防止されます。
よくある質問
State Restorationは、アプリケーションの再起動または最小化後にユーザーインターフェースを保存および復元するメカニズムであり、データとユーザーのコンテキストの損失を防ぎます。
State Restorationは一時的なUI状態(スクロール位置、フォームに入力されたデータ)を保存し、データベースは永続的なユーザーデータを保存します。State Restorationはボリューム制限のあるシステムメカニズム(Bundle、NSData)を使用します。
AndroidX LifecycleのViewModelでSavedStateHandleを使用します。最小化時に自動的にデータを保存し、戻ったときに復元します。完全な再起動をサポートするには、SavedStateViewModelFactoryを使用します。
UIViewControllerにrestorationIdentifierを設定し、encodeRestorableStateとdecodeRestorableStateメソッドを実装します。ナビゲーションには、コントローラースタックのパスを保持したNSUserActivityを使用します。
完全なデータではなく識別子を保存します:選択されたアイテムのID、検索クエリ、スクロール位置、トグル状態。大きなオブジェクトや画像の保存は避けてください。
まとめ
ターンキー方式のモバイルアプリケーションを開発します
IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。