在 Kotlin 序列化中使用 DataStore

在 Kotlin 序列化中使用 DataStore

我们之前已经 分享Proto DataStore 和 Preferences DataStore 的使用方法。这两个 DataStore 版本都会在后台使用 Protos 对数据进行序列化。您也可以使用 Kotlin 序列化,结合使用 DataStore 与自定义数据类。这有助于减少样板代码,且无需学习或依赖于 Protobuf 库,同时仍可以为数据提供架构。

您需要完成以下几项操作:

  • 定义数据类
  • 确保您的数据类不可变
  • 使用 Kotlin 序列化实现 DataStore 序列化器
  • 开始使用

定义数据类

Kotlin 数据类 非常适合与 DataStore 结合使用,这是因为它们能够与 Kotlin 序列化无缝协作。DataStore 会依赖数据类自动生成的 equalshashCode。数据类也会生成便于调试和更新数据的 toStringcopy 函数。

/* Copyright 2021 Google LLC.  
   SPDX-License-Identifier: Apache-2.0 */

data class UserPreferences(
    val showCompleted: Boolean,
    val sortOrder: SortOrder
)

确保您的数据类不可变

确保您的数据类不可变是非常重要的,这是因为 DataStore 无法兼容可变类型。结合使用可变类型与 DataStore 会导致难以捕获的错误和竞争条件。数据类并非一定不可变。

Vars 是可变的,所以您应使用 vals 代替:

/* 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()

实现 DataStore 序列化器

Kotlin 序列化支持包括 JSON 和协议缓冲区在内的 多种格式。我将在此处使用 JSON,因为它十分常见、易于使用且会以明文形式进行存储,便于调试。Protobuf 也是一个不错的选择,因为它规模更小、速度更快且兼容 protobuf-lite

要使用 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 DataStorePreferences DataStore Codelab 的实践经验。

版权声明

禁止一切形式的转载-禁止商用-禁止衍生 申请授权

脉脉不得语
脉脉不得语
Zhengzhou Website
Android Developer | https://androiddevtools.cn and https://androidweekly.io WebMaster | GDG Zhengzhou Funder & Ex Organizer | http://Toast.show(∞) Podcast Host

你已经成功订阅到 Android 开发技术周报
太棒了!接下来,完成检验以获得全部访问权限 Android 开发技术周报
欢迎回来!你已经成功登录了。
Unable to sign you in. Please try again.
成功!您的帐户已完全激活,您现在可以访问所有内容。
Error! Stripe checkout failed.
Success! Your billing info is updated.
Error! Billing info update failed.