0
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Android】 DataStoreを試してみる

Last updated at Posted at 2021-10-21

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.gradledependencies に以下を追加します。

dependencies {
        classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.17'
}

次に app/src/main/protosettings.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

0
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?