咱们以前已经 分享 了 Proto DataStore 和 Preferences DataStore 的使用方法。这两个 DataStore 版本都会在后台使用 Protos 对数据进行序列化。您也可使用 Kotlin 序列化,结合使用 DataStore 与自定义数据类。这有助于减小样板代码,且无需学习或依赖于 Protobuf 库,同时仍能够为数据提供架构。html
您须要完成如下几项操做:android
Kotlin 数据类 很是适合与 DataStore 结合使用,这是由于它们可以与 Kotlin 序列化无缝协做。DataStore 会依赖数据类自动生成的 equals
和 hashCode
。数据类也会生成便于调试和更新数据的 toString
和 copy
函数。git
/* Copyright 2021 Google LLC. SPDX-License-Identifier: Apache-2.0 */ data class UserPreferences( val showCompleted: Boolean, val sortOrder: SortOrder )
确保您的数据类不可变是很是重要的,这是由于 DataStore 没法兼容可变类型。结合使用可变类型与 DataStore 会致使难以捕获的错误和竞争条件。数据类并不是必定不可变。github
Vars 是可变的,因此您应使用 vals 代替:json
/* Copyright 2021 Google LLC. SPDX-License-Identifier: Apache-2.0 */ data class MyData( - var num: Int + val num: Int ) - myObj.num = 5 // Fails to compile when num is val + val newObj = myObj.copy(num = 5)
数组是可变的,因此您不该将其公开。数组
/* Copyright 2021 Google LLC. SPDX-License-Identifier: Apache-2.0 */ data class MyData( - var num: IntArray ) - myObj.num = 5 // This would mutate your object
即便将只读列表用做数据类的一部分,该数据类也仍为可变的。您应考虑改用 不可变/持久化集合:安全
/* Copyright 2021 Google LLC. SPDX-License-Identifier: Apache-2.0 */ data class MyData( - val nums: List<Int> + val nums: PersistentList<Int> ) - val myInts = mutableListOf(1, 2, 3, 4) - val myObj = MyData(myInts) - myInts.add(5) // Fails to compile with PersistentList, but mutates with List + val newData = myObj.copy( + nums = myObj.nums.mutate { it += 5 } // Mutate returns a new PersistentList + )
将可变类型用做数据类的一部分会令数据类变为可变状态。您不该采起上述作法,反而要确保全部内容都是不可变类型。架构
/* Copyright 2021 Google LLC. SPDX-License-Identifier: Apache-2.0 */ data class MyData( - val mutableType: MutableType ) - val myType = MutableType() - val myObj = MyData(myType) - myType.mutate()
Kotlin 序列化支持包括 JSON 和协议缓冲区在内的 多种格式。我将在此处使用 JSON,由于它十分常见、易于使用且会以明文形式进行存储,便于调试。Protobuf 也是一个不错的选择,由于它规模更小、速度更快且兼容 protobuf-lite。ide
要使用 Kotlin 序列化读取数据类并将其写入 JSON,您须要使用 @Serializable
注释数据类并使用 Json.decodeFromString<YourType>(string)
和 Json.encodeToString(data)
。如下是带有 UserPreferences
的示例:函数
/* Copyright 2021 Google LLC. SPDX-License-Identifier: Apache-2.0 */ @Serializable data class UserPreferences( val showCompleted: Boolean = false, val sortOrder: SortOrder = SortOrder.None ) object UserPreferencesSerializer : Serializer<UserPreferences> { override val defaultValue = UserPreferences() override suspend fun readFrom(input: InputStream): UserPreferences { try { return Json.decodeFromString( UserPreferences.serializer(), input.readBytes().decodeToString()) } catch (serialization: SerializationException) { throw CorruptionException("Unable to read UserPrefs", serialization) } } override suspend fun writeTo(t: UserPreferences, output: OutputStream) { output.write(Json.encodeToString(UserPreferences.serializer(), t).encodeToByteArray()) } }
⚠️ 将 Parcelables 与 DataStore 一块儿使用并不安全,由于不一样 Android 版本之间的数据格式可能会有所变化。
在您构建时,将您建立的序列化器传递到 DataStore:
/* Copyright 2021 Google LLC. SPDX-License-Identifier: Apache-2.0 */ val Context.dataStore by dataStore("my_file.json", serializer = UserPreferencesSerializer)
其读取数据看起来与使用 protos 进行读取同样:
/* Copyright 2021 Google LLC. SPDX-License-Identifier: Apache-2.0 */ suspend fun getShowCompleted(): Boolean { context.dataStore.data.first().showCompleted }
您可使用生成的 .copy() 函数更新数据:
/* Copyright 2021 Google LLC. SPDX-License-Identifier: Apache-2.0 */ suspend fun setShowCompleted(newShowCompleted: Boolean) { // This will leave the sortOrder value untouched: context.dataStore.updateData { it.copy(newShowCompleted = showCompleted) } }
结合使用 DataStore 与 Kotlin 序列化和数据类可减小样板文件并有助于简化代码,但您必须多加当心,避免由于可变性而引起错误。您只需定义数据类和实现序列化器便可。快来动手尝试一下吧!
如要详细了解 DataStore,您能够查看咱们的 文档 并得到一些使用 Proto DataStore 和 Preferences DataStore Codelab 的实践经验。