こんにちは!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
前置きが長くなりましたが本題です。
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へ置き換える方法」も解説していこうと思います。
*1:まだAlpha01だった頃は「referencesKey<String>("KEY_NAME")」という指定方法だったので、ジェネリクスで任意の型を入れられてしまっていました。そこは改善されたようです。