iTranslated by AI
Trying Out Jetpack DataStore
What is DataStore?
An evolution of SharedPreferences that allows you to store key-value pairs or typed objects using protocol buffers.
There are two types of DataStore:
-
Preferences DataStore
- Stores and accesses data using keys. This implementation does not require a
predefined schema, but it is not type-safe.
- Stores and accesses data using keys. This implementation does not require a
-
Proto DataStore
- Stores data as instances of a custom data type. This implementation requires you to
define a schema using protocol buffers, but it is type-safe.
- Stores data as instances of a custom data type. This implementation requires you to
Environment
I have created a repository here for the sample.
macOS Catalina ver 10.15.7 (Intel version)
Android Studio Arctic Fox | 2020.3.1 Patch 2
Preferences DataStore
First, let's try Preferences DataStore. Create a project and add the following to app/build.gradle.
implementation "androidx.datastore:datastore-preferences:1.0.0"
Let's try to persist simple key-value pairs to disk using the DataStore and Preferences classes.
Add the following to the top of your Kotlin file to treat DataStore as a singleton.
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")
Writing code to save and read values
Simply save -> read and log the output.
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
}
}
It's a success if DataStore: text = sample is output.
Proto DataStore
Next, let's try Proto DataStore.
Setup
First, modify the build.gradle within the module as follows.
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'
}
}
}
}
}
Add the following to the dependencies in the project-root build.gradle.
dependencies {
classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.17'
}
Next, create settings.proto in app/src/main/proto.
syntax = "proto3";
option java_package = "com.slowhand.datastoresample.model";
option java_multiple_files = true;
message Settings {
int32 example_counter = 1;
}
Create Settings.kt with the following content.
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
)
Let's try saving and reading.
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()
}
}
It's a success if the log for counter = is output.
References
- 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
Discussion