【Android】SharedPreferences を使ってみる

## 概要
Android 端末の内部ストレージに Key-Value 形式でデータを保存することができる SharedPreferencesAPI を使う機会がありましたので、その使い方についてご紹介したいと思います。

SharedPreferencesAPI はあらかじめ用意されている Android のライブラリで、端末のアプリ固有の領域にデータを保存したりそれを参照したりすることができます。

## 環境
Android Studio：2021.1.1 Patch 3
kotlin：1.6.20
targetSdkVersion：32
minSdkVersion：27

## 内容
### 準備
SharedPreferencesAPI を使って内部ストレージにデータを保存するには、まず ```getSharedPreferences()``` メソッドを呼び出してデータを書き込むための xml ファイルを作成します。
すでに作成済みの場合は指定したファイルに対してデータを書き込むことになります。

``` MainActivity.kt
val sharedPref = getSharedPreferences("com.example.preferencesample.my_preferences", MODE_PRIVATE)
```

```getSharedPreferences()``` メソッドの第一引数ではファイル名を、第二引数ではファイルを他のアプリから扱えるようにするかどうかを指定します。

ファイル名は任意のもので良いのですが、公式ドキュメントでは「パッケージ名 + 任意のファイル名」の形式が紹介されていますので、コードではそれに従って命名しています。
第二引数は ```MODE_PRIVATE```、```MODE_WORLD_READABLE```、```MODE_WORLD_WRITEABLE ``` の中から指定するのですが、API レベル17で後者二つのサポートが終了しているため、現在使用できるのは ```MODE_PRIVATE``` のみとなっています。
これを指定することで作成したファイルには他のアプリからアクセスができなくなります。

### データの保存、呼び出し
作成したファイルに文字列を保存してみます。
データを保存するときは下記のように、```edit()``` メソッドで Editor オブジェクトを作成します。
そして文字列を保存する場合は ```putString()``` メソッドを呼び出し、最後に ```apply()``` メソッドで保存処理を確定します。

``` MainActivity.kt
val sharedPref = getSharedPreferences("com.example.preferencesample.my_preferences", MODE_PRIVATE)
val editor = sharedPref.edit()
    editor.putString("name", "taro").apply()
```
  
```putString()``` メソッドの第一引数が Key、第二引数が Value になります。
そして保存したデータは xml ファイルに以下のように書き込まれます。

``` com.example.preferencesample.my_preferences.xml
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
    <string name="name">taro</string>
</map>
```

次に保存したデータをアプリ内から呼び出してみます。
呼び出したいデータが文字列の場合は SharedPreferences インターフェースの ```getString()``` メソッドを使用します。

``` MainActivity.kt
val sharedPref = getSharedPreferences("com.example.preferencesample.my_preferences", MODE_PRIVATE)
val name = sharedPref.getString("name", "")
Log.d("MainActivity name", name.toString())
```

メソッドの第一引数は呼び出したいデータの Key、第二引数は指定した Key に対するデータが存在しなかった場合に返却されるデフォルト値です。

ログに以下の内容が出力されることを確認してください。

```
MainActivity name: taro
```

### 保存できるデータ型
ここまでは文字列を例に挙げてご紹介しましたが、SharedPreferences では文字列以外にも以下の型を持つデータを保存/呼び出しをすることができます。（引数の変数名は分かりやすい名前に変えています）

| データ型 | 保存メソッド |呼び出しメソッド|
| ---------------- | -------- |----|
| String                 |    putString(String key, String value)      |getString(String key, String default)|
| Int                 |    putInt(String key, Int value)      |getInt(String key, Int default)|
| Long                 |    putLong(String key, Long value)      |getLong(String key, Long default)|
| Float                 |    putFloat(String key, Float value)      |getFloat(String key, Float default)|
| Boolean                 |    putBoolean (String key, Boolean value)      |getBoolean (String key, Boolean  default)|
| Set\<String>                 |    putStringSet (String key, Set\<String> value)      |getStringSet (String key, Set\<String>  default)|

実際の使い方は以下の通りです。（保存メソッドは便宜上つなげて書いています）
``` MainActivity.kt
val editor = sharedPref.edit()
// 保存
editor.putString("name", "taro")
    .putInt("age", 20)
    .putFloat("weight", 60f)
    .putLong("height", 170)
    .putBoolean("isAdult", true)
    .putStringSet("circles", setOf("succor", "tennis")).apply()

// 呼び出し
Log.d("MainActivity", sharedPref.getString("name", "").toString())
Log.d("MainActivity", sharedPref.getInt("age", 0).toString())
Log.d("MainActivity", sharedPref.getFloat("weight", 0f).toString())
Log.d("MainActivity", sharedPref.getLong("height", 0).toString())
Log.d("MainActivity", sharedPref.getBoolean("isAdult", false).toString())
Log.d("MainActivity", sharedPref.getStringSet("circles", setOf()).toString())
```

また、xml ファイルには以下のように保存されます。
``` com.example.preferencesample.my_preferences.xml
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
    <string name="name">taro</string>
    <float name="weight" value="60.0" />
    <set name="circles">
        <string>succor</string>
        <string>tennis</string>
    </set>
    <int name="age" value="20" />
    <boolean name="isAdult" value="true" />
    <long name="height" value="170" />
</map>
```

### データの削除
保存したデータの中から特定のkeyを持つものを削除したい場合は ```remove()``` メソッドを使います。

``` MainActivity.kt
val editor = sharedPref.edit()
editor.remove("name").apply()

val name = sharedPref.getString("name", "")
Log.d("MainActivity name", name.toString())
```

メソッドの引数には削除したいデータの key を指定し、```apply()``` メソッドで削除処理を確定します。

ログに先ほど保存した「taro」が表示されていないことを確認してください。(デフォルト値に設定した空文字が返却されます)

```
MainActivity name: 
```

なお、保存した値全てを消去したい場合は ```clear()``` メソッドを使います。

``` MainActivity.kt
val editor = sharedPref.edit()
editor.clear().apply()
```

## データが保存されるファイルの場所
ここまでお読みいただいて「データは実際どこに保存されているの？」と思われた方もいらっしゃるかもしれません。
データが実際に書き込まれているファイルは Android Studio を使って簡単に確認することができます。

まず Android Studio 画面右下の「Device File Explorer」をクリックします。
そうすると端末の内部ストレージが開かれますので、「data/data/アプリのパッケージ名/shared_prefs/」以下に記事冒頭で作成したファイル名で xml ファイルが作成されていることを確認してください。
このファイルを開くと保存したデータが反映されていることが確認できます。

![スクリーンショット 2022-05-05 22.35.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1261074/7e49fe43-b055-4389-793e-d022fd64bdfb.png)


## 参考
[Key-Value データを保存する  \|  Android デベロッパー  |  Android Developers](https://developer.android.com/training/data-storage/shared-preferences?hl=ja)
[[Android]SharedPreferencesファイルの正体と保存場所 \| プログラミング挫折撲滅プロジェクト・みんなのプログラミング by Telulu LLC（FlutterでiOS/Androidスマホアプリ開発）](https://minpro.net/sharedpreferences)