Sansan Tech Blog

Sansanのものづくりを支えるメンバーの技術やデザイン、プロダクトマネジメントの情報を発信

Jetpack DataStore入門〜Preferences DataStore実装編〜

こんにちは!Sansan事業部 プロダクト開発部のふるしんです。

私は大阪のオフィスでSansanプロダクトのAndroidアプリの開発に従事しています。
play.google.com

この記事では、連載として2020年 9月 2日に1.0.0-alpha01として公開されたJetpack DataStoreについて解説していきます。
developer.android.com


2020年10月に行われたGDG DevFest 2020でもセッションでお話しましたので、よろしければその際の動画や資料も御覧ください。
www.youtube.com

www.slideshare.net

前置きが長くなりましたが本題です。

Jetpack DataStoreとは?

SharedPreferencesに代わるものとして提案されている、新しいデータの永続化の方法です。

公式ドキュメントによると、将来的にはSharedPreferencesから置き換えることを考えてね、と書かれていました。

DataStoreは、基本的にはSharedPreferencesの欠点を補うために提案されているようです。
ですので例えば動作はKotlin CotourinesのFlowに則って動作して非同期で走ってくれたりと、他にも素敵だなと思う点がいつくかありました。
今回はそんな点を紹介できたらなと思います。

DataStoreと一言で言っても実は2種類あります。
Preferences DataStoreと、Proto DataStoreです。
それぞれの使い方を見ていきます。

Preferences DataStore

こちらはSharedPreferencesに限りなく近いです。
格納できる型はSharedPreferencesとほぼ同じIntやStringなどとなっています。
他にどんな型が格納できるかは公式ドキュメントを御覧ください。

では実際にどうやって使うのか、コードを追って説明していきます。

build.gradleに追加

まずはいつも通りのやつです。build.gradleに必要なものを追加します。
本記事の執筆時点での最新は1.0.0-alpha06でした。

app/build.gradle

dependencies {
...
    // Preferences DataStore
    implementation "androidx.datastore:datastore-preferences:1.0.0-alpha06"
...
}

これで準備が整ったので、実際に使っていきます。

インスタンスを生成

まずはDataStoreのインスタンスを生成します。

MainActivity.kt

import androidx.datastore.preferences.createDataStore

class MainActivity : AppCompatActivity() {
    lateinit var dataStore: DataStore<Preferences>

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        dataStore = createDataStore(name = "preferences")

    }
}

先程Preferences DataStoreをbuild.gradleから使えるようにしたことで、createDataStoreの拡張関数がContextに生えていますのでこれを利用します。

イメージ的にはSharedPreferencesで言うところのgetSharedPreferencesにあたります。

データの格納

次にデータの格納方法です。
データを格納するには、DataStoreのeditメソッドを使います。

class MainActivity : AppCompatActivity() {
    lateinit var dataStore: DataStore<Preferences>

    override fun onCreate(savedInstanceState: Bundle?) {
//...
            GlobalScope.launch {
                saveName()
            }
    }

    private suspend fun saveName() {
        dataStore.edit { preferences: MutablePreferences ->
            preferences[PreferencesKeys.KEY_NAME] = "furusin"
        }
    }

    private object PreferencesKeys {
        val KEY_NAME = stringPreferencesKey("KEY_NAME")
    }
}

editメソッドではラムダ式が渡せるようになっているので、そこでKey Value Storeで保存するデータをセットします。
ちなみにここで渡しているKEY_NAMEの preferencesKeyですが、これもPreferences DataStoreで定義されているString型を格納したいときに使うPreferencesKeyです。
他にもintPreferencesKeyやbooleanPreferencesKeyなどがあり、格納できる型が限定されています。*1

実際にデータを格納する際は、上記saveNameメソッドでやっているようにMutablePreferencesのsetterでデータを格納します。
SharedPreferencesだとEditorを用意したりApplyしたりとおまじないが多かったですが、これだけで済むのは楽ですね。

データの読み込み

それでは次に、先ほど格納したデータを読み込む方法を見ていきます。

override fun onCreate(savedInstanceState: Bundle?) {
//...
       GlobalScope.launch {
           getName().collect {
               Log.d("test", "name = $it")
           }
       }
   }

private fun getName(): Flow<String> =
   dataStore.data.map { preferences ->
       preferences[PreferencesKeys.KEY_NAME] ?: "NO NAME"
   }

今度はgetNameメソッドを用意しました。
先ほどの格納時はDataStoreのeditメソッドから格納しましたが、今度はDataStoreのプロパティであるdataにアクセスします。

public interface DataStore<T> {
    public val data: Flow<T>
    public suspend fun updateData(transform: suspend (t: T) -> T): T
}

DataStoreインターフェースはこの様になっています。
プロパティであるdataはKotlin CoroutinesのFlowになっているので、このdataから情報を取得する際はFlowのcollectメソッドを使って読み込みます。

まとめ

以上で「Preferences DataStoreを使った準備〜データの格納&読み込む」までができるようになりました。
流れを簡単にまとめると次のようになります。

  • build.gradleに追加
    • implementation "androidx.datastore:datastore-preferences:1.0.0-alpha01"
  • DataStoreのインスタンスを生成
    • val dataStore = createDataStore(name = "preferences")
  • DataStoreのeditメソッドでデータを格納
GlobalScope.launch {
   dataStore.edit { preferences: MutablePreferences -> preferences[PreferencesKeys.KEY_NAME] = "furusin"   }
}
  • DataStoreのdataからCoroutines Flowのcollectを使ってデータを取得
GlobalScope.launch {
   dataStore.data.map { preferences -> preferences[PreferencesKeys.KEY_NAME] ?: "NO NAME"   }
.collect {       Log.d("test", "name = $it")   }
}


今回はPreferences DataStoreの実装編のみを解説しました。
次の記事ではもうひとつの種類であるProto DataStoreについても解説し、その後の連載として「SharedPreferencesからDataStoreへ置き換える方法」も解説していこうと思います。

最後に

Sansanでは一緒に働く仲間を募集しています。

私は大阪におりますので、興味が湧きましたらぜひお声がけください!
media.sansan-engineering.com

*1:まだAlpha01だった頃は「referencesKey<String>("KEY_NAME")」という指定方法だったので、ジェネリクスで任意の型を入れられてしまっていました。そこは改善されたようです。

© Sansan, Inc.