【Android】初心者が学ぶViewModel 

4月からAndroid開発を行うことになったので頭の整理として記事にしていきます。
今回はViewmodelです。

## ViewModel

Androidの初心者本をやっているとActivityで何でも行っていたので
実務を始めてからViewmodelの存在は知りました。

[公式ドキュメント](https://developer.android.com/topic/libraries/architecture/viewmodel?hl=ja)では
>ViewModel は、ライフサイクルを意識した方法で UI 関連のデータを保存し管理するためのクラスです。ViewModel クラスを使用すると、画面の回転などの構成の変更後にデータを引き継ぐことができます。

とあります。どうやらActivityにデータを持つと画面回転しただけで消えてしまう様です。

### 検証

簡単なサンプルを作成して実際に検証しました。

#### MainActivity
```
class MainActivity : AppCompatActivity() {

    // カウント値
    private var countValue = 0

    private lateinit var textView: TextView
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        textView = findViewById<TextView>(R.id.count_result)
        
        //初期値
        textView.text = countValue.toString()
        
        //カウント処理
        findViewById<Button>(R.id.count_button).setOnClickListener{
            countValue++
            textView.text = countValue.toString()
        }
    }
}
```

仕様はCOUTN UPボタンを押下すると、その下にカウント数が表示されるシンプルなもので、
ボタン押下後、画面を回転させました。

<img width="1000" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311433/47312f3b-00c0-2282-9db4-fb1399c2a74a.gif">


結果はこのように画面を回転後はカウントアップの数字が消えてしまい、データが引き継がれていないことが確認できました。

原因はドキュメントに記載されています。Activityのライフサイクルに起因するとのこと。
>システムによって UI コントローラが破棄または再作成されると、UI コントローラに保存されている一時的な UI 関連のデータはすべて失われます。


### ViewModelを使ってみる

それではViewModelを使ってActivityが破棄されてもデータが失われないようにしたいと思います。

まずViewModelクラスを継承したクラスを作成します。
今回はカウント値を持つだけなのでシンプルですが下準備はこれだけです、

#### MainViewModel
```
import androidx.lifecycle.ViewModel

class MainViewModel: ViewModel() {

    var countValue = 0
}
```


続いてMainActivityにViewModelを組み込んでいきます。
ViewModelの作成方法は色々ありますが、今回はViewModelProviderを利用しています。
簡単に説明すると引数にthis(MainActivity自身)を渡すことでviewmodelと関連付けています。
そして、画面に表示する値は宣言したviewModelプロパティから取得します。

#### MainActivity
```
class MainActivity : AppCompatActivity() {

    private lateinit var textView: TextView
    //viewModelを宣言しておく
    private lateinit var viewModel: MainViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        // viewModelを作成
        viewModel = ViewModelProvider(this).get(MainViewModel::class.java)

        textView = findViewById<TextView>(R.id.count_result)

        // 値はviewModelを参照する
        textView.text = viewModel.countValue.toString()

        findViewById<Button>(R.id.count_button).setOnClickListener{
            viewModel.countValue++
            // 値はviewModelを参照する
            textView.text = viewModel.countValue.toString()
        }
    }
}
```

たったこれだけですが結果は...

<img width="1000" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/311433/eacddc35-5416-fee3-e0d9-3ed868c691e7.gif">

無事にカウント値を引き継ぐことができました。


### Viewmodelを使わない場合

ちなみに少量のデータであればViewModelを使わなくてもデータの引き継ぎはできる様です。

MainActivity
```
class MainActivity : AppCompatActivity() {

    private lateinit var textView: TextView

    private var countValue = 0

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        textView = findViewById<TextView>(R.id.count_result)

        textView.text = countValue.toString()

        findViewById<Button>(R.id.count_button).setOnClickListener{
            countValue++
            textView.text = countValue.toString()
        }
    }

    // onCreateのパラメータであるsavedInstanceStateがnullでなければ呼ばれる
    override fun onRestoreInstanceState(savedInstanceState: Bundle) {
        // キーを指定して値を取り出し
        var text = savedInstanceState?.getString("COUNT_VALUE")
        textView.text = text
    }

    // ActivityのonStopとonDestoryの間で呼ばれる
    override fun onSaveInstanceState(outState: Bundle) {
        outState.run {
            // 引き継がせたいデータをkeyValueで登録しておく
            putString("COUNT_VALUE", countValue.toString())
        }
        super.onSaveInstanceState(outState)
    }
}
```

### まとめ
ViewModelの基本的な動きを学ぶことができました。