咱們以前已經 分享 了 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 的實踐經驗。