在 Kotlin 序列化中使用 DataStore

image

我們之前已經(jīng) 分享Proto DataStore 和 Preferences DataStore 的使用方法。這兩個 DataStore 版本都會在后臺使用 Protos 對數(shù)據(jù)進(jìn)行序列化。您也可以使用 Kotlin 序列化,結(jié)合使用 DataStore 與自定義數(shù)據(jù)類。這有助于減少樣板代碼,且無需學(xué)習(xí)或依賴于 Protobuf 庫,同時仍可以為數(shù)據(jù)提供架構(gòu)。

您需要完成以下幾項操作:

  • 定義數(shù)據(jù)類
  • 確保您的數(shù)據(jù)類不可變
  • 使用 Kotlin 序列化實現(xiàn) DataStore 序列化器
  • 開始使用

定義數(shù)據(jù)類

Kotlin 數(shù)據(jù)類 非常適合與 DataStore 結(jié)合使用,這是因為它們能夠與 Kotlin 序列化無縫協(xié)作。DataStore 會依賴數(shù)據(jù)類自動生成的 equalshashCode。數(shù)據(jù)類也會生成便于調(diào)試和更新數(shù)據(jù)的 toStringcopy 函數(shù)。

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

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

確保您的數(shù)據(jù)類不可變

確保您的數(shù)據(jù)類不可變是非常重要的,這是因為 DataStore 無法兼容可變類型。結(jié)合使用可變類型與 DataStore 會導(dǎo)致難以捕獲的錯誤和競爭條件。數(shù)據(jù)類并非一定不可變。

Vars 是可變的,所以您應(yīng)使用 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)

數(shù)組是可變的,所以您不應(yīng)將其公開。

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

data class MyData(
-    var num: IntArray
)
-  myObj.num = 5 // This would mutate your object

即使將只讀列表用作數(shù)據(jù)類的一部分,該數(shù)據(jù)類也仍為可變的。您應(yīng)考慮改用 不可變/持久化集合:

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

將可變類型用作數(shù)據(jù)類的一部分會令數(shù)據(jù)類變?yōu)榭勺儬顟B(tài)。您不應(yīng)采取上述做法,反而要確保所有內(nèi)容都是不可變類型。

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

實現(xiàn) DataStore 序列化器

Kotlin 序列化支持包括 JSON 和協(xié)議緩沖區(qū)在內(nèi)的 多種格式。我將在此處使用 JSON,因為它十分常見、易于使用且會以明文形式進(jìn)行存儲,便于調(diào)試。Protobuf 也是一個不錯的選擇,因為它規(guī)模更小、速度更快且兼容 protobuf-lite

要使用 Kotlin 序列化讀取數(shù)據(jù)類并將其寫入 JSON,您需要使用 @Serializable 注釋數(shù)據(jù)類并使用 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 版本之間的數(shù)據(jù)格式可能會有所變化。

使用序列化器

在您構(gòu)建時,將您創(chuàng)建的序列化器傳遞到 DataStore:

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

val Context.dataStore by dataStore("my_file.json", serializer = UserPreferencesSerializer)

其讀取數(shù)據(jù)看起來與使用 protos 進(jìn)行讀取一樣:

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

suspend fun getShowCompleted(): Boolean {
  context.dataStore.data.first().showCompleted
}

您可以使用生成的 .copy() 函數(shù)更新數(shù)據(jù):

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

總結(jié)

結(jié)合使用 DataStore 與 Kotlin 序列化和數(shù)據(jù)類可減少樣板文件并有助于簡化代碼,但您必須多加小心,避免因為可變性而引發(fā)錯誤。您只需定義數(shù)據(jù)類和實現(xiàn)序列化器即可??靵韯邮謬L試一下吧!

如要詳細(xì)了解 DataStore,您可以查看我們的 文檔 并獲得一些使用 Proto DataStorePreferences DataStore Codelab 的實踐經(jīng)驗。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容