DataStoreとは?
プロトコル バッファを使用して Key-Value ペアや型付きオブジェクトを格納できるSharedPreferencesの進化版
DataStore には、以下2 種類がある
-
Preferences DataStore
- キーを使用してデータの保存およびアクセスを行います。この実装では、
定義済みのスキーマは必要ありませんが、タイプセーフではありません。
- キーを使用してデータの保存およびアクセスを行います。この実装では、
-
Proto DataStore
- カスタムデータ型のインスタンスとしてデータを保存します。この実装では、
プロトコル バッファを使用してスキーマを定義する必要がありますが、タイプセーフです。
- カスタムデータ型のインスタンスとしてデータを保存します。この実装では、
試した環境
サンプル用にこちらにリポジトリ作成しています。
macOS Catalina ver 10.15.7 (intel版)
Android Studio Arctic Fox | 2020.3.1 Pat ch 2
Preferences DataStore
まずは Preferences DataStore
を試してみます。早速プロジェクトを作成し、以下を app/build.gradle
に追加します。
implementation "androidx.datastore:datastore-preferences:1.0.0"
DataStore クラスと Preferences クラスを使用して、単純な Key-Value ペアをディスクに保持してみます。
Kotlinファイルのトップで以下を追加し、DataStoreをシングルトンとして扱えるようにします。
import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.preferencesDataStore
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")
値を保存して読み込む処理を書いてみる
シンプルに保存 -> 読み込みを行ってログ出力を行います。
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")
val TEXT_KEY = stringPreferencesKey("example_text")
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
...
GlobalScope.launch {
saveText(this@MainActivity, "sample")
val textFlow: Flow<String> = dataStore.data.map { p -> p[TEXT_KEY] ?: "" }
textFlow.collect { Log.d("DataStore", "text = $it") }
}
}
}
suspend fun saveText(context: Context, text: String) {
context.dataStore.edit { settings ->
settings[TEXT_KEY] = text
}
}
結果 DataStore: text = sample
が出力されればOK
Proto DataStore
次は Proto DataStore
を試してみます。
環境構築
まずは以下内容でモジュール内の build.gradle
を修正します。
plugins {
id "com.google.protobuf"
}
dependencies {
implementation "androidx.datastore:datastore:1.0.0"
implementation "com.google.protobuf:protobuf-javalite:3.14.0"
}
protobuf {
protoc {
artifact = "com.google.protobuf:protoc:3.14.0"
}
// Generates the java Protobuf-lite code for the Protobufs in this project. See
// https://github.com/google/protobuf-gradle-plugin#customizing-protobuf-compilation
// for more information.
generateProtoTasks {
all().each { task ->
task.builtins {
java {
option 'lite'
}
}
}
}
}
プロジェクトルートの build.gradle
の dependencies
に以下を追加します。
dependencies {
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.17'
}
次に app/src/main/proto
に settings.proto
を作成します。
syntax = "proto3";
option java_package = "com.slowhand.datastoresample.model";
option java_multiple_files = true;
message Settings {
int32 example_counter = 1;
}
Settings.kt
を以下内容で作成します。
package com.slowhand.datastoresample.model
import android.content.Context
import androidx.datastore.core.CorruptionException
import androidx.datastore.core.DataStore
import androidx.datastore.core.Serializer
import androidx.datastore.dataStore
import androidx.datastore.preferences.protobuf.InvalidProtocolBufferException
import java.io.InputStream
import java.io.OutputStream
object SettingsSerializer : Serializer<Settings> {
override val defaultValue: Settings = Settings.getDefaultInstance()
override suspend fun readFrom(input: InputStream): Settings {
try {
return Settings.parseFrom(input)
} catch (exception: InvalidProtocolBufferException) {
throw CorruptionException("Cannot read proto.", exception)
}
}
override suspend fun writeTo(
t: Settings,
output: OutputStream
) = t.writeTo(output)
}
val Context.settingsDataStore: DataStore<Settings> by dataStore(
fileName = "settings.proto",
serializer = SettingsSerializer
)
保存して読み込んでみます。
GlobalScope.launch {
incrementCounter(this@MainActivity)
val exampleCounterFlow: Flow<Int> = settingsDataStore.data
.map { settings ->
settings.exampleCounter
}
exampleCounterFlow.collect { Log.d("DataStore", "counter = $it")}
}
suspend fun incrementCounter(context: Context) {
context.settingsDataStore.updateData { currentSettings ->
currentSettings.toBuilder()
.setExampleCounter(currentSettings.exampleCounter + 1)
.build()
}
}
counter =
のログが出力されればOKです。
参考URL
- Jetpack DataStore入門〜Preferences DataStore実装編〜 - Sansan Builders Blog
- CoroutinesのlaunchとasyncとChannelとFlowの使い分け - Qiita
- Jetpack Compose の例 - たくさんの自由帳
- コンポーザブルのライフサイクル | Jetpack Compose | Android Developers
- Android Jetpack Proto DataStore. A Jetpack recommended solution for… | by Satya Pavan Kantamani | ProAndroidDev
- 公式ページを眺めているだけではわからないJetpack DataStoreのあれこれ - Qiita