【Kotlin】初心者向け: 画面回転やスリープ後のUIの保持

# はじめに
　今回は画面を回転させたときや本体をスリープさせた後、ビューの一部がその前後で変わってしまう問題について取り組みます。下の簡易的なボタンカウンターアプリでその様子が確認できます。縦画面でカウントアップさせたとき、横に切り替えると数字が0に戻ってしまうのです。
<img width="211" alt="キャプチャ.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/658219/19f24c31-e98e-3f60-bf9f-5eec3f03dc25.png">
<img width="425" alt="キャプチャ2.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/658219/7b7ab53f-fce7-e72f-ca82-c570b4b8eb90.png">

#原因
　この問題の原因は、画面の向きを変えるなどの諸動作によって、Activityがライフサイクルを一からやり直す事にあります。これによってハードウェアは、柔軟にかつ素早くユーザーのアクションを反映させることが出来ますが、同時にUIまでもが破棄されて初期化されてしまいます。これを防ぐためには、ライフサイクルが終わる前に、UI等の必要な情報を一時的に保持しておく必要があります。

＊ライフサイクルについてはこちら：https://qiita.com/K4N4/items/2f4babe2bab67ddacf89

#onSaveInstanceState
 一時的に情報を保持する方法の一つに、onSaveInstanceStateを利用するというものがあります。この方法はあまり大容量ではない情報を、簡単に保持する時に利用します。使い方は非常にシンプルです。

```
override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)

        outState?.putInt("key", i)
    }

    override fun onRestoreInstanceState(savedInstanceState: Bundle) {
        super.onRestoreInstanceState(savedInstanceState)

        i = savedInstanceState?.getInt("key", 0)
        textView.text = i.toString()
    }
```
　`onSaveInstanceState`メソッド（データを保持する）と`onRestoreInstanceState`メソッド（保持したデータを復元する）を呼び出し、`put〇〇`と`get〇〇`で実際にデータの受け渡しを行っています。
　`onSaveInstanceState`メソッドの`outState?.putInt("key", i)`というのは`outState`（データをメモリに保存する実体）に"key"（任意）というキーを使って、iというIntの値を保持する。という事を指しています。キーとはその名の通り鍵の事です。呼び出し側と共通のキーを持たなければ、データの受け渡しは出来ません。
　`onRestoreInstanceState`メソッドの`i =　savedInstanceState?.getInt("key", 0)`は、Activityが破棄されて値が初期化されてしまった  i に、保持したデータの実態`savedInstanceState`から`getInt`メソッドで共通のキーを持つ値を取り出しています。`getInt`の第二引数は、`putInt`がnullの場合の値を設定します。
　この二つのメソッドとその中身を入れるだけで、簡単に設定した値の保持が行えます。

#onSaveInstanceStateとonRestoreInstanceStateとライフサイクル
　先述したようにこの二つのメソッドを使えば、簡単にUIの保持を行えます。では、この二つのメソッドはActivityのライフサイクルにおいて、どの段階で機能してるのでしょうか？
![android_jitsumu2_6.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/658219/4a69d6af-aa12-256b-a5fc-cc918ddde234.jpeg)
それを示すのがこの図です。この図はActivityの基本的な工程に、今回用いた二つのメソッド等を加えたものです。この図の通り、`onSaveInstanceState`はonPauseの後に値の保持を、`onRestoreInstanceState`は`onStart`の後に値の復元を行っています。`onRestoreInstanceState`の値は`onCreate`に影響を及ぼさなかったり、`onSaveInstanceState`が呼び出されたら必ずアプリが止まることなどが分かります。

#実際にこのライフサイクルを可視化してみる
```
class MainActivity : AppCompatActivity() {

    private var i : Int = 0

    override fun onCreate(savedInstanceState: Bundle?) {
        Log.d(TAG, "onStart: called")
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        button.setOnClickListener {
            i += 1
            textView.text = i.toString()
        }
    }

    override fun onStart() {
        Log.d(TAG, "onStart: called")
        super.onStart()
    }

    override fun onResume() {
        Log.d(TAG, "onResume: called")
        super.onResume()
    }

    override fun onPause() {
        Log.d(TAG, "onPause: called")
        super.onPause()
    }

    override fun onSaveInstanceState(outState: Bundle) {
        Log.d(TAG, "onSaveInstanceState: called")
        super.onSaveInstanceState(outState)

        outState.putInt("key", i)
    }

    override fun onRestoreInstanceState(savedInstanceState: Bundle) {
        Log.d(TAG, "onRestoreInstanceState: called")
        super.onRestoreInstanceState(savedInstanceState)

        i = savedInstanceState.getInt("key", 0)
        textView.text = i.toString()
    }

    override fun onStop() {
        Log.d(TAG, "onStop: called")
        super.onStop()
    }

    override fun onRestart() {
        Log.d(TAG, "onRestart: called")
        super.onRestart()
    }

    override fun onDestroy() {
        Log.d(TAG, "onDestroy: called")
        super.onDestroy()
    }
}
```
 MainActivityの中身をこのようにすることで、実際にどこでデータの保持等が行われているかログを通して可視化することが出来ます。このソースコードでは、上で示したカウントアップ機能のためにデータのやり取りを行っています。上手くいけば、各工程ごとにログに出力がなされます。是非回転などさせて試してみてください。