Android开发拾遗:DataStore与JSON结合

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

在本系列的上一篇中我介绍了Android的Proto DataStore的用法,但是我对protobuf的schema定义并不熟悉,所以就想着有没有使用JSON格式结合DataStore存储数据,事实证明这是可能的。

首先注意到DataStoreprotobuf相关类是解耦的,dataStore函数需要的参数是一个文件名——持久化数据存放位置,以及一个serializer对象。现在尝试改动serializer来实现用JSON存储配置数据。

先安装依赖:

plugins {
    // ...
    alias(libs.plugins.jetbrains.kotlin.serialization)
}

dependencies {
    // datastore还是必要的
    implementation(libs.androidx.datastore)

    // 用于序列化/反序列化
    implementation(libs.kotlinx.serialization.json)
}
# libs.versions.toml
[versions]
datastore = "1.1.1"
kotlin = "1.9.0"
kotlinxSerializationJson = "1.1.0"

[libraries]
kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" }

[plugins]
jetbrains-kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }

下一步是创建一个用于序列化/反序列化的数据类(之前Proto DataStore中这个类是自动生成的):

import kotlinx.serialization.Serializable

@Serializable
data class Settings(
    val theme: Theme = Theme.SYSTEM,
    val isDebugMode: Boolean = false,
)

enum class Theme { SYSTEM, LIGHT, DARK }

接下来设置好Serializer对象:

object SettingsSerializer : Serializer<Settings> {
    override val defaultValue: Settings
        get() = Settings()

    override suspend fun readFrom(input: InputStream): Settings {
        return try {
            Json.decodeFromString(
                deserializer = Settings.serializer(),
                string = input.readBytes().decodeToString()
            )
        } catch (e: SerializationException) {
            e.printStackTrace()
            defaultValue
        }
    }

    override suspend fun writeTo(t: Settings, output: OutputStream) {
        withContext(Dispatchers.IO) {
            output.write(
                Json.encodeToString(
                    serializer = Settings.serializer(),
                    value = t
                ).encodeToByteArray()
            )
        }
    }
}

将其设置到Context上:

val Context.dataStore by dataStore("settings.json", SettingsSerializer)

简单写个UI试验下:

@Composable
fun MainScreen() {
    Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
        val context = LocalContext.current
        val settings by context.dataStore.data.collectAsState(initial = Settings())
        val scope = rememberCoroutineScope()

        Surface(modifier = Modifier.padding(innerPadding)) {
            Column {
                Text(if (settings.isDebugMode) "Debug" else "Release")
                Button(onClick = {
                    scope.launch {
                        context.dataStore.updateData {
                            it.copy(isDebugMode = !it.isDebugMode)
                        }
                    }
                }) {
                    Text(text = "Toggle")
                }
            }
        }
    }
}

json-data-store

完成!

EOF
Githubmastodonrss-box
Copyright © 2020-2024 Elliot