Android开发拾遗:如何使用Proto DataStore

创建于:发布于:文集:Android开发拾遗

持久化一些用户数据是一个常见的需求,例如保存用户设置,Android提供了一个方便的机制,叫做DataStore。其中有两套API,一个是Preferences DataStore,可以存取简单的key-value数据;另一个是Proto DataStore,顾名思义,这需要开发者定义一个protocol buffers的schema,可以存取自定义的数据类型,并提供类型安全保证。

最初我仅用到了简单的键值对API,因为业务上需要的配置项逐渐变多,并且在一些地方我需要使用枚举,所以萌生了从Preferences DataStore转到Proto DataStore的念头。但可惜的是,Android的文档关于Proto DataStore没有详细的描述,在参考了一些开源代码后,我找到了能适配Kotlin以及Gradle Kotlin DSL的使用方法,在此记录一下。

Schema

首先要在app/src/main/proto目录下创建一个protobuf文件,如settings.proto

syntax = "proto3";

// 这里替换成自己的包名
option java_package = "com.example.application";
option java_multiple_files = true;

message Settings {
  int32 example_counter = 1;
}

Gradle配置

有了schema之后,还需要有对应的用来序列化/反序列化的数据结构,这个数据结构可以通过库来生成。修改Gradle配置:

import com.google.protobuf.gradle.id

plugins {
    // ...
    id("com.google.protobuf") version "0.9.1"
}

dependencies {
    // 添加这两个依赖
    implementation("androidx.datastore:datastore:1.1.1")
    implementation("com.google.protobuf:protobuf-javalite:3.17.3")
}

// 这里Android Studio可能有lint报错,直接忽略,sync gradle
protobuf {
    protoc {
        artifact = "com.google.protobuf:protoc:3.21.9"
    }

    generateProtoTasks {
        all().forEach { task ->
            task.builtins {
                id("java") {
                    option("lite")
                }
            }
        }
    }
}

在代码中使用

先创建一个SettingsSerializer.kt文件,定义序列化器:

// 这个Settings类是自动生成的
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.pb",
  serializer = SettingsSerializer
)

读取时就可以使用冷流:

val settings = context.settingsDataStore.data.first()

如果要设置值,可以使用updateData方法:

suspend fun incrementCounter() {
  context.settingsDataStore.updateData { currentSettings ->
    currentSettings.toBuilder()
      .setExampleCounter(currentSettings.exampleCounter + 1)
      .build()
    }
}
EOF
Githubmastodonrss-box
Copyright © 2020-2024 Elliot