【Kotlin/Android】onSaveInstanceStateの使い方!Activityの再生成
この記事からわかること
- Android Studio/KotlinでonSaveInstanceStateとは?
- ActivityやFragmentが破棄された場合に再生成する方法
- デバッグする方法
index
[open]
\ アプリをリリースしました /
友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-
posted withアプリーチ
環境
- Android Studio:Koala
- Kotlin:1.8.20
参考文献:AndroidアプリにおけるUIの状態保存と復元について調べてみた
ActivityやFragmentは破棄される
AndroidアプリではActivityやFragmentは一定の条件を満たすと破棄されてしまいます。破棄されるとUIの状態がリセットされ、表示するために取得したデータなども空になってしまいます。
破棄されるタイミングはイメージしやすいユーザー操作による別画面への移動やfinish
の呼び出しなどだけでなく、画面の回転やメモリの使用状況などに応じてシステムからActivityが破棄されることがあります。
ActivityやFragmentの破棄を想定した実装にしていないとViewの再生成が行われなかったり、表示していたデータがリセットされてしまったりと予期せぬバグを引き起こす可能性があります。
そのためにActivityやFragmentのライフサイクルを理解し、データを適切に保存して再利用することが重要になります。
破棄されるタイミング
- 明示的なActivityの破棄(遷移やfinishなど)
- 画面の回転
- メモリ不足
UIの再構築
UIを適切に再構築するためには各ライフサイクルで適切にUIが構築されるように実装する必要があります。といっても破棄された場合はライフサイクルが最初から呼ばれるだけなので下手な分岐処理さえ挟まなければ問題なく表示はされると思います。
Activityでいうとシステムによって破棄されたタイミングでonDestroy
が呼ばれ再度生成されるタイミングでonCreate()
が呼ばれます。
データの保存
APIやローカルから取得して表示していたデータなども破棄されてしまうのでデータを再度取得する処理の実装が必要になります。例えばユーザーが入力した値など一時的な値もリセットされてしまうのでここは以下の方法で回避することができます。
- ViewModel
- 保存済みインスタンスの状態(onSaveInstanceState)
- 永続ストレージ
それぞれにメリット/デメリットがあるので使い分けは公式ドキュメントに記載の以下の表を確認すると分かりやすいです。
ViewModel | 保存済みインスタンスの状態 | 永続ストレージ | |
---|---|---|---|
保存先 | メモリ内 | メモリ内 | ディスクまたはネットワーク上 |
構成変更後も保持 | ○ | ○ | ○ |
システムによるプロセス終了後も保持 | × | ○ | ○ |
ユーザーによるアクティビティ終了/onFinish() も保持 | × | × | ○ |
データの上限 | 複雑なオブジェクトは問題ないが、使用可能なメモリによってスペースが制限される | プリミティブ型と String などのシンプルな小さなオブジェクトのみ | ネットワーク リソースからのディスク スペースまたはネットワークからの取得コスト / 時間によってのみ制限される |
読み取り / 書き込み時間 | 高速(メモリアクセスのみ) | 低速(シリアル化 / シリアル化解除が必要) | 低速(ディスク アクセスまたはネットワーク トランザクションが必要) |
引用:公式リファレンス:Options for preserving UI stat
ViewModel
永続ストレージ
今回はonSaveInstanceStateに焦点を当ててみたいと思います。
onSaveInstanceState
onSaveInstanceState
は状態を保存するために使用されるライフサイクルメソッドです。呼び出されるタイミングはActivityやFragmentが破棄される直前です。ライフサイクルに合わせて出力してみると以下の通りになりました。
onCreate
onStart
onResume
------- 破棄 -------
onPause
onSaveInstanceState
onStop
onDestroy
------- 復帰 -------
onCreate
onStart
onRestoreInstanceState
onResume
onSaveInstanceState
内でBundleにデータを保存する処理を実装することでデータを保持しておき、ActivityやFragmentが復帰した際に避難させておいたデータをBundle
から取得することができるようになります。
private val KEY_TEXT = "KEY_TEXT"
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
// データの保存
outState.putString(KEY_TEXT, textValue)
}
Bundle
のデータ取得や保存方法は以下の記事を参考にしてください。
onRestoreInstanceState
データの取得はonRestoreInstanceState
またはonCreate
のどちらかからBundle
経由で取得することができます。
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
super.onRestoreInstanceState(savedInstanceState)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// データの復元
if (savedInstanceState != null) {
textValue = savedInstanceState.getString(KEY_TEXT)
}
}
Activity/Fragmentの破棄をデバッグする
ActivityやFragmentが破棄される挙動を再現したい場合は端末を開発者モードに変更して「アクティビティを保持しない」にチェックを入れておくとアプリが停止するタイミングで破棄されるようにすることができます。
開発者モードへの変更方法は以下の記事を参考にしてください。
Activityの再生成を無効化する
特定の構成変更に関してはActivityの自動再作成を制御することが可能です。「AndroidManifest.xml」ファイルのactivity
エントリでandroid:configChanges
を追加し、生成性を無効にしたい値を指定します。
<activity
android:name=".MyActivity"
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden"
android:label="@string/app_name">
指定できる値
属性 | 説明 |
---|---|
colorMode | 画面の色モード機能(色域またはダイナミックレンジ)が変更されました。アクティビティで使用しているカラーモードを変更しても、ディスプレイのカラー機能は変更されません。 |
density | 表示密度の変更(ユーザーが異なるディスプレイスケールまたは別のディスプレイがアクティブになったとき)。API レベル 24 で追加されました。 |
fontScale | フォントのスケーリングファクタの変更(ユーザーが新しいグローバルフォントサイズを選択したときなど)。 |
fontWeightAdjustment | フォントの太さの増加量が変更されました。 |
grammaticalGender | 言語の文法上の性別が変更されました。GrammaticalInflectionManager。API レベル 34 で追加されました。 |
keyboard | キーボードの種類の変更(ユーザーが外部キーボードを接続したときなど)。 |
keyboardHidden | キーボードのユーザー補助の変更(ユーザーがハードウェアキーボードを表示したときなど)。 |
layoutDirection | レイアウト方向の変更(左から右(LTR)から右から左(RTL)に変更する)。API レベル 17 で追加されました。 |
locale | ロケールの変更(ユーザーがテキストを表示する新しい言語を選択したときなど)。 |
mcc | IMSI モバイルカントリーコード(MCC)の変更(MCC を更新する SIM が検出された場合)。 |
mnc | IMSI モバイルネットワークコード(MNC)の変更(MNC を更新する SIM が検出された場合)。 |
navigation | ナビゲーションタイプ(トラックボールまたは D-pad)への TA の変更。通常、このようなことは起こりません。 |
orientation | 画面方向の変更(ユーザーがデバイスを回転させたときなど)。アプリが Android 3.2(API レベル 13)以上を対象としている場合は、"screenLayout" と "screenSize" の構成も宣言します。 |
screenLayout | 画面レイアウトの変更(別のディスプレイがアクティブになったときなど)。 |
screenSize | 現在使用可能な画面サイズの変更。ユーザーが横向きと縦向きを切り替えると変わります。API レベル 13 で追加されました。 |
smallestScreenSize | 物理画面サイズの変更。向きに関係なくサイズが変更されたことを表しています。API レベル 13 で追加されました。 |
touchscreen | タッチスクリーンの変更。通常、このようなことは起こりません。 |
uiMode | ユーザーインターフェースモードの変更(ユーザーがデバイスをデスクやカーホルダーに置いたとき、夜間モードが変更されたときなど)。API レベル 8 で追加されました。 |
公式リファレンス:android:configChanges
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。