【Kotlin】SharedPreferenceの基本【書き込み / 読み出し / 修正 / 削除】

# はじめに
SharedPreferenceは今までちゃんと使ったことがなく、調べてもすぐに忘れるの繰り返しでした。
そこで、今回はsharedPreferenceの**CRUD**(Create,Read,Update,Delete)のやり方を見ながら基本を整理していこうと思います。
あくまで自分の備忘録として記していますが、もしも
* 基本的なsharedPreferenceの書き方が知りたい方
* どれがファイル名でどれがキーでどれがバリューなのかを整理したい方

のお力になれることがあれば大変幸いです。

## 実行環境
|  項目      |  情報                |      
| ------ | ---------------------------- | 
| PC     | MacBook Pro (14-inch,2021) |     
| CPU    | Apple M1 Pro 10-core                |      
| GPU  | Apple M1 Pro 16-core                       |      
| OS     | macOS Monterey (12.0.1)<br>  |   
|  Android Studio| Arctic Fox 2020.3.1 Patch4 | 

# 目次
* **Create** : 新規作成・書き込み
* **READ** : 読み出し
* **UPDATE** : 編集
* **DELETE** : 削除
* 詰まりポイント

# Create :新規作成・書き込み

```MainActivity.kt
    fun sharedPreferenceTest(){
        // インスタンスを取得
        val sharedPreferences: SharedPreferences = getSharedPreferences("FileName" , Context.MODE_PRIVATE)

       // 書き込み
        sharedPreferences.edit()
            .putString("これはキーです","これはバリューです")
            .apply()
    }
```
`        val sharedPreferences: SharedPreferences = getSharedPreferences("FileName" , Context.MODE_PRIVATE)`でsharedPreferenceのファイル名とモードを指定しつつ、変数にsharePreferenceのインスタンスを入れています。
この部分は編集・読み出し・削除でも使う共通部分です。

インスタンスを取得した後は`sharedPreference.edit()`以降でキーバリュー形式で値を入れています。
今回は文字列を保存するため`putString`を使っていますが、他にも`putInt` `putBoolean`などがあります。

全体に共通することですが、sharedpreferenceのデータに変更を加える場合は`.edit`をまず呼び、次にそれぞれの編集処理（今回は`.putString(`）を行い、最後に`.apply` `.commit`で変更を保存するというイメージです。

※ちなみに`.apply` `.commit`の違いは
`.apply`→非同期で値を保存
 `.commit`→メインスレッドで値を保存
という感じらしいです。基本的にはapplyを使うことが推奨されています。詳しくは[公式](https://developer.android.com/reference/android/content/SharedPreferences.Editor#commit())や[こちら](https://stackoverflow.com/questions/47829425/sharedpreferences-apply-vs-commit)のstackoverflowをぜひ見てみてください！


### 結果

```fileName.xml
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
    <string name="これはキーです">これはバリューです</string>
</map>
```

Android Studio右タブのDeviceFile Explorerを開き、data > data > パッケージ名 > shared_prefsの中に上記のようなxmlファイルが作成されています。
今回はファイル名を指定して書き込みを行ったため、ファイル名がFileName.xmlとなっています。

# READ：読み出し
上記で保存した値をエミュレーター上で表示させてみたいと思います。
この検証ではTextViewに渡す処理が必要なので、onCreateとレイアウトファイルを含めたコード全体を載せておきます。
ちょっと長いですが、要は`sharedPreferences.getString`で読み出せるよということです。

```MainActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val binding: ActivityMainBinding = DataBindingUtil.setContentView(this,R.layout.activity_main)

        binding?.activityMainText?.text = sharedPreferenceTest()
    }

    fun sharedPreferenceTest(): String?{
        // インスタンスを取得
        val sharedPreferences: SharedPreferences = getSharedPreferences("fileName" , Context.MODE_PRIVATE)

        // 読み出した結果を戻り値として渡す
        return sharedPreferences
            .getString("これはキーです","キーが見つかりませんでした")

    }
```

onCreate内はレイアウト作成とtextViewに値を入れる処理をしているだけなので、ここでは詳細な説明は省かせていただきます。

書き込んだ値を取得するには`getString`を使います。これも書き込み時と同じく`getInt`や`getBoolean`などがあります。
第一引数では参照したいキー名を、第二引数にはそのキーに値が存在しなかった場合に表示する値を設定します。

### 結果
↓データが存在した時
<img width="300px" src ="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1274260/350cbdd6-0bb0-d09d-aae0-d2cdad71b9ac.png">

↓データが存在しなかった時
<img width="300px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1274260/aa86fd50-ea22-aab2-621d-a4334355a000.png">


## UPDATE :編集
Create :新規作成・書き込みと全く同じです。
同じキー名を指定し、好きな値を設定すると上書きされます。

```MainActivity.kt
    fun sharedPreferenceTest(){
        // インスタンスを取得
        val sharedPreferences: SharedPreferences = getSharedPreferences("fileName" , Context.MODE_PRIVATE)

        // 上書き
        sharedPreferences.edit()
            .putString("これはキーです","これは新たなバリューです")
            .apply()
    }
```

### 結果

```fileName.xml
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
    <string name="これはキーです">これは新たなバリューです</string>
</map>
```

# DELETE : 削除
sharedPreferenceの値を削除する場合は`.remove`を呼びます。
sharedPreferenceのデータをいじるわけなので、`.edit` で変更開始、最後に`.apply`で締めています。

```MainActivity.kt
    fun sharedPreferenceTest() : String? {
        // インスタンスを取得
        val sharedPreferences: SharedPreferences = getSharedPreferences("fileName" , Context.MODE_PRIVATE)

        // removeで削除
        sharedPreferences.edit()
            .remove("これはキーです")
            .apply()
```

### 結果
```fileName.xml
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map />
```

綺麗さっぱり消えてますね！


# その他メモ
### AndroidStudioからsharedPreferenceを閲覧するとき、リアルタイムで更新されない時がある
どうやらビルドしても値が更新されない時があるようです。その場合は端末名(下記画像の「KYOCERA S6-KC」の部分)を押すと再読み込みされて最新のデータが取得できます。
<img width="300px" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1274260/72ba005b-f8d7-124b-05b2-e777fe917458.png" >

### インスタンスの取得方法

```MainActivity.kt
fun sharedPreferenceTest(){
        // インスタンスを取得
        val sharedPreferences: SharedPreferences = getPreferences(MODE_PRIVATE)
```

今回は上記のような形でファイル名を指定しつつインスタンスを取得していました。
インスタンスを取得する方法は`getPreferences`以外に`getPreference`もあるようです。
こちらを使うと1Activityに対して1つだけsharedPreferenceファイルを作成するという使い方ができるようになります。そのためファイル名も以下のようにActivityのクラス名がそのままが適用されます。
```MainActivity.kt
    fun sharedPreferenceTest(){
        // インスタンスを取得（この場合はファイル名を設定する必要なし）
        val sharedPreferences: SharedPreferences = getPreferences(MODE_PRIVATE)
        // 値の書き込み
        sharedPreferences.edit()
            .putString("これはキーです","これはバリューです")
            .apply()
    }
```
結果↓
```MainActivity.xml
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
    <string name="これはキーです">これはバリューです</string>
</map>
```