在 Kotlin 序列化中使用 DataStore
分享
https://android-developers.googleblog.com/2020/09/prefer-storing-data-with-jetpack.html
Proto
https://developers.google.cn/protocol-buffers/docs/overview
Preferences
https://developer.android.google.cn/reference/kotlin/androidx/datastore/preferences/core/package-summary
Kotlin 序列化
https://kotlinlang.org/docs/reference/serialization.html
您需要完成以下几项操作:
定义数据类
确保您的数据类不可变
使用 Kotlin 序列化实现 DataStore 序列化器
开始使用
定义数据类
数据类 https://kotlinlang.org/docs/reference/data-classes.html
1/* Copyright 2021 Google LLC.
2 SPDX-License-Identifier: Apache-2.0 */
3
4data class UserPreferences(
5 val showCompleted: Boolean,
6 val sortOrder: SortOrder
7)
确保您的数据类不可变
Vars 是可变的,所以您应使用 vals 代替:
1/* Copyright 2021 Google LLC.
2 SPDX-License-Identifier: Apache-2.0 */
3
4data class MyData(
5- var num: Int
6+ val num: Int
7)
8- myObj.num = 5 // Fails to compile when num is val
9+ val newObj = myObj.copy(num = 5)
1/* Copyright 2021 Google LLC.
2 SPDX-License-Identifier: Apache-2.0 */
3
4data class MyData(
5- var num: IntArray
6)
7- myObj.num = 5 // This would mutate your object
不可变/持久化集合 https://github.com/Kotlin/kotlinx.collections.immutable
1/* Copyright 2021 Google LLC.
2 SPDX-License-Identifier: Apache-2.0 */
3
4data class MyData(
5- val nums: List<Int>
6+ val nums: PersistentList<Int>
7)
8
9- val myInts = mutableListOf(1, 2, 3, 4)
10- val myObj = MyData(myInts)
11- myInts.add(5) // Fails to compile with PersistentList, but mutates with List
12+ val newData = myObj.copy(
13+ nums = myObj.nums.mutate { it += 5 } // Mutate returns a new PersistentList
14+ )
将可变类型用作数据类的一部分会令数据类变为可变状态。您不应采取上述做法,反而要确保所有内容都是不可变类型。
1/* Copyright 2021 Google LLC.
2 SPDX-License-Identifier: Apache-2.0 */
3
4data class MyData(
5- val mutableType: MutableType
6)
7
8- val myType = MutableType()
9- val myObj = MyData(myType)
10- myType.mutate()
实现 DataStore 序列化器
多种格式 https://kotlinlang.org/docs/reference/serialization.html#formats protobuf-lite https://developer.android.google.cn/codelabs/android-proto-datastore#0
要使用 Kotlin 序列化读取数据类并将其写入 JSON,您需要使用 @Serializable 注释数据类并使用 Json.decodeFromString<YourType>(string) 和 Json.encodeToString(data)。以下是带有 UserPreferences 的示例:
1/* Copyright 2021 Google LLC.
2 SPDX-License-Identifier: Apache-2.0 */
3
4@Serializable
5data class UserPreferences(
6 val showCompleted: Boolean = false,
7 val sortOrder: SortOrder = SortOrder.None
8)
9
10object UserPreferencesSerializer : Serializer<UserPreferences> {
11 override val defaultValue = UserPreferences()
12
13 override suspend fun readFrom(input: InputStream): UserPreferences {
14 try {
15 return Json.decodeFromString(
16 UserPreferences.serializer(), input.readBytes().decodeToString())
17 } catch (serialization: SerializationException) {
18 throw CorruptionException("Unable to read UserPrefs", serialization)
19 }
20 }
21
22 override suspend fun writeTo(t: UserPreferences, output: OutputStream) {
23 output.write(Json.encodeToString(UserPreferences.serializer(), t).encodeToByteArray())
24 }
25}
使用序列化器
在您构建时,将您创建的序列化器传递到 DataStore:
1/* Copyright 2021 Google LLC.
2 SPDX-License-Identifier: Apache-2.0 */
3
4val Context.dataStore by dataStore("my_file.json", serializer = UserPreferencesSerializer)
其读取数据看起来与使用 protos 进行读取一样:
1/* Copyright 2021 Google LLC.
2 SPDX-License-Identifier: Apache-2.0 */
3
4suspend fun getShowCompleted(): Boolean {
5 context.dataStore.data.first().showCompleted
6}
您可以使用生成的 .copy() 函数更新数据:
1/* Copyright 2021 Google LLC.
2 SPDX-License-Identifier: Apache-2.0 */
3
4suspend fun setShowCompleted(newShowCompleted: Boolean) {
5 // This will leave the sortOrder value untouched:
6 context.dataStore.updateData { it.copy(newShowCompleted = showCompleted) }
7}
总结
结合使用 DataStore 与 Kotlin 序列化和数据类可减少样板文件并有助于简化代码,但您必须多加小心,避免因为可变性而引发错误。您只需定义数据类和实现序列化器即可。快来动手尝试一下吧!
文档 https://developer.android.google.cn/topic/libraries/architecture/datastore Proto DataStore https://developer.android.google.cn/codelabs/android-proto-datastore#0 Preferences DataStore https://developer.android.google.cn/codelabs/android-preferences-datastore#0
推荐阅读